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 (docTypeParams != null && mb.IsGenericMethod ()) {
3234 IList<GenericParameter> args = mb.GenericParameters;
3235 if (args.Count == docTypeParams.Length) {
3236 typeParams = args.Select (p => p.Name).ToArray ();
3240 else if (mi is PropertyDefinition)
3241 pis = ((PropertyDefinition)mi).Parameters;
3243 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3244 int pcount = pis == null ? 0 : pis.Count;
3245 if (mcount != pcount)
3248 MethodDefinition mDef = mi as MethodDefinition;
3249 if (mDef != null && !mDef.IsConstructor) {
3250 // Casting operators can overload based on return type.
3251 string rtype = GetReplacedString (
3252 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3253 typeParams, docTypeParams);
3254 string originalRType = rtype;
3255 if (MDocUpdater.SwitchingToMagicTypes) {
3256 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3259 if ((returntype != rtype && originalRType == rtype) ||
3260 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3264 if (originalRType != rtype)
3265 matchedMagicType = true;
3271 for (int i = 0; i < pis.Count; i++) {
3272 string paramType = GetReplacedString (
3273 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3274 typeParams, docTypeParams);
3276 // if magictypes, replace paramType to "classic value" ... so the comparison works
3277 string originalParamType = paramType;
3278 if (MDocUpdater.SwitchingToMagicTypes) {
3279 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3282 string xmlMemberType = member.Parameters [i];
3283 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3284 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3286 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3287 // namespace, we should see if it matches when added
3288 bool stillDoesntMatch = true;
3289 if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3290 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3292 stillDoesntMatch = withDroppedNs != paramType;
3295 if (stillDoesntMatch) {
3301 if (originalParamType != paramType)
3302 matchedMagicType = true;
3304 if (!good) continue;
3306 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3307 // 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
3308 likelyCandidate = mi;
3315 return likelyCandidate;
3318 static string[] GetTypeParameters (string docName)
3320 if (docName [docName.Length-1] != '>')
3322 StringList types = new StringList ();
3323 int endToken = docName.Length-2;
3324 int i = docName.Length-2;
3326 if (docName [i] == ',' || docName [i] == '<') {
3327 types.Add (docName.Substring (i + 1, endToken - i));
3330 if (docName [i] == '<')
3335 return types.ToArray ();
3338 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3340 // In case of dropping the namespace, we have to remove the dropped NS
3341 // so that docName will match what's in the assembly/type
3342 if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3343 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3344 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3347 // need to worry about 4 forms of //@MemberName values:
3348 // 1. "Normal" (non-generic) member names: GetEnumerator
3350 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3351 // - try as-is, and try type.member (due to "kludge" for property
3353 // 3. "Normal" Generic member names: Sort<T> (CSC)
3354 // - need to remove generic parameters --> "Sort"
3355 // 4. Explicitly-implemented interface members for generic interfaces:
3356 // -- System.Collections.Generic.IEnumerable<T>.Current
3357 // - Try as-is, and try type.member, *keeping* the generic parameters.
3358 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3359 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3360 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3361 // this as (1) or (2).
3362 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3364 foreach (MemberReference mi in type.GetMembers (docName))
3366 if (CountChars (docName, '.') > 0)
3367 // might be a property; try only type.member instead of
3368 // namespace.type.member.
3369 foreach (MemberReference mi in
3370 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3377 int startLt, startType, startMethod;
3378 startLt = startType = startMethod = -1;
3379 for (int i = 0; i < docName.Length; ++i) {
3380 switch (docName [i]) {
3389 if (numLt == 0 && (i + 1) < docName.Length)
3390 // there's another character in docName, so this <...> sequence is
3391 // probably part of a generic type -- case 4.
3395 startType = startMethod;
3401 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3403 foreach (MemberReference mi in type.GetMembers (refName))
3407 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3410 // If we _still_ haven't found it, we've hit another generic naming issue:
3411 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3412 // explicitly-implemented METHOD names (not properties), e.g.
3413 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3414 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3415 // which the XML docs will contain.
3417 // Alas, we can't derive the Mono name from docName, so we need to iterate
3418 // over all member names, convert them into CSC format, and compare... :-(
3421 foreach (MemberReference mi in type.GetMembers ()) {
3422 if (MDocUpdater.GetMemberName (mi) == docName)
3427 static string GetReplacedString (string typeName, string[] from, string[] to)
3431 for (int i = 0; i < from.Length; ++i)
3432 typeName = typeName.Replace (from [i], to [i]);
3436 private static int CountChars (string s, char c)
3439 for (int i = 0; i < s.Length; ++i) {
3447 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3452 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3455 this.ecmadocs = ecmaDocs;
3458 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3460 HashSet<string> seen = new HashSet<string> ();
3461 return GetDocumentationTypes (assembly, forTypes, seen)
3462 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3465 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3468 while (ecmadocs.Read ()) {
3469 switch (ecmadocs.Name) {
3471 if (typeDepth == -1)
3472 typeDepth = ecmadocs.Depth;
3473 if (ecmadocs.NodeType != XmlNodeType.Element)
3475 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3477 string typename = ecmadocs.GetAttribute ("FullName");
3478 string typename2 = MDocUpdater.GetTypeFileName (typename);
3479 if (forTypes != null &&
3480 forTypes.BinarySearch (typename) < 0 &&
3481 typename != typename2 &&
3482 forTypes.BinarySearch (typename2) < 0)
3485 if ((t = assembly.GetType (typename)) == null &&
3486 (t = assembly.GetType (typename2)) == null)
3488 seen.Add (typename);
3489 if (typename != typename2)
3490 seen.Add (typename2);
3491 Console.WriteLine (" Import: {0}", t.FullName);
3492 if (ecmadocs.Name != "Docs") {
3493 int depth = ecmadocs.Depth;
3494 while (ecmadocs.Read ()) {
3495 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3499 if (!ecmadocs.IsStartElement ("Docs"))
3500 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3510 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3512 return GetMembers (basefile, type)
3513 .Concat (base.GetDocumentationMembers (basefile, type));
3516 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3518 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3521 if (ecmadocs.IsEmptyElement)
3524 int membersDepth = ecmadocs.Depth;
3526 while (go && ecmadocs.Read ()) {
3527 switch (ecmadocs.Name) {
3529 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3531 DocumentationMember dm = new DocumentationMember (ecmadocs);
3533 string xp = MDocUpdater.GetXPathForMember (dm);
3534 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3536 if (oldmember == null) {
3537 m = GetMember (type, dm);
3539 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3540 type.FullName, dm.MemberSignatures ["C#"]);
3541 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3544 // oldmember lookup may have failed due to type parameter renames.
3546 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3547 if (oldmember == null) {
3548 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3549 oldmember = basefile.CreateElement ("Member");
3550 oldmember.SetAttribute ("MemberName", dm.MemberName);
3551 members.AppendChild (oldmember);
3552 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3553 XmlElement ms = basefile.CreateElement ("MemberSignature");
3554 ms.SetAttribute ("Language", key);
3555 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3556 oldmember.AppendChild (ms);
3558 oldmember.SetAttribute ("__monodocer-seen__", "true");
3559 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3564 m = GetMember (type, new DocumentationMember (oldmember));
3566 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3567 type.FullName, dm.MemberSignatures ["C#"]);
3570 oldmember.SetAttribute ("__monodocer-seen__", "true");
3572 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3573 if (ecmadocs.Name != "Docs")
3574 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3579 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3588 abstract class DocumentationImporter {
3590 public abstract void ImportDocumentation (DocsNodeInfo info);
3593 class MsxdocDocumentationImporter : DocumentationImporter {
3595 XmlDocument slashdocs;
3597 public MsxdocDocumentationImporter (string file)
3599 var xml = File.ReadAllText (file);
3601 // Ensure Unix line endings
3602 xml = xml.Replace ("\r", "");
3604 slashdocs = new XmlDocument();
3605 slashdocs.LoadXml (xml);
3608 public override void ImportDocumentation (DocsNodeInfo info)
3610 XmlNode elem = GetDocs (info.Member ?? info.Type);
3615 XmlElement e = info.Node;
3617 if (elem.SelectSingleNode("summary") != null)
3618 MDocUpdater.ClearElement(e, "summary");
3619 if (elem.SelectSingleNode("remarks") != null)
3620 MDocUpdater.ClearElement(e, "remarks");
3621 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3622 MDocUpdater.ClearElement(e, "value");
3623 MDocUpdater.ClearElement(e, "returns");
3626 foreach (XmlNode child in elem.ChildNodes) {
3627 switch (child.Name) {
3630 XmlAttribute name = child.Attributes ["name"];
3633 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3635 p2.InnerXml = child.InnerXml;
3638 // Occasionally XML documentation will use <returns/> on
3639 // properties, so let's try to normalize things.
3642 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3643 v.InnerXml = child.InnerXml;
3649 case "permission": {
3650 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3653 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3655 a = e.OwnerDocument.CreateElement (child.Name);
3656 a.SetAttribute ("cref", cref.Value);
3659 a.InnerXml = child.InnerXml;
3663 XmlAttribute cref = child.Attributes ["cref"];
3666 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3668 a = e.OwnerDocument.CreateElement ("altmember");
3669 a.SetAttribute ("cref", cref.Value);
3676 if (child.NodeType == XmlNodeType.Element &&
3677 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3680 MDocUpdater.CopyNode (child, e);
3687 private XmlNode GetDocs (MemberReference member)
3689 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3690 if (slashdocsig != null)
3691 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3696 class EcmaDocumentationImporter : DocumentationImporter {
3700 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3702 this.ecmadocs = ecmaDocs;
3705 public override void ImportDocumentation (DocsNodeInfo info)
3707 if (!ecmadocs.IsStartElement ("Docs")) {
3711 XmlElement e = info.Node;
3713 int depth = ecmadocs.Depth;
3714 ecmadocs.ReadStartElement ("Docs");
3715 while (ecmadocs.Read ()) {
3716 if (ecmadocs.Name == "Docs") {
3717 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3720 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3722 if (!ecmadocs.IsStartElement ())
3724 switch (ecmadocs.Name) {
3727 string name = ecmadocs.GetAttribute ("name");
3730 XmlNode doc = e.SelectSingleNode (
3731 ecmadocs.Name + "[@name='" + name + "']");
3732 string value = ecmadocs.ReadInnerXml ();
3734 doc.InnerXml = value.Replace ("\r", "");
3741 string name = ecmadocs.Name;
3742 string cref = ecmadocs.GetAttribute ("cref");
3745 XmlNode doc = e.SelectSingleNode (
3746 ecmadocs.Name + "[@cref='" + cref + "']");
3747 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3749 doc.InnerXml = value;
3751 XmlElement n = e.OwnerDocument.CreateElement (name);
3752 n.SetAttribute ("cref", cref);
3759 string name = ecmadocs.Name;
3760 string xpath = ecmadocs.Name;
3761 StringList attributes = new StringList (ecmadocs.AttributeCount);
3762 if (ecmadocs.MoveToFirstAttribute ()) {
3764 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3765 } while (ecmadocs.MoveToNextAttribute ());
3766 ecmadocs.MoveToContent ();
3768 if (attributes.Count > 0) {
3769 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3771 XmlNode doc = e.SelectSingleNode (xpath);
3772 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3774 doc.InnerXml = value;
3777 XmlElement n = e.OwnerDocument.CreateElement (name);
3779 foreach (string a in attributes) {
3780 int eq = a.IndexOf ('=');
3781 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3792 class DocumentationMember {
3793 public StringToStringMap MemberSignatures = new StringToStringMap ();
3794 public string ReturnType;
3795 public StringList Parameters;
3796 public string MemberName;
3797 public string MemberType;
3799 public DocumentationMember (XmlReader reader)
3801 MemberName = reader.GetAttribute ("MemberName");
3802 int depth = reader.Depth;
3804 StringList p = new StringList ();
3806 if (reader.NodeType != XmlNodeType.Element)
3809 bool shouldUse = true;
3811 string apistyle = reader.GetAttribute ("apistyle");
3812 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
3814 catch (Exception ex) {}
3815 switch (reader.Name) {
3816 case "MemberSignature":
3818 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3822 MemberType = reader.ReadElementString ();
3825 if (reader.Depth == depth + 2 && shouldUse)
3826 ReturnType = reader.ReadElementString ();
3829 if (reader.Depth == depth + 2 && shouldUse)
3830 p.Add (reader.GetAttribute ("Type"));
3833 if (reader.Depth == depth + 1)
3837 } while (go && reader.Read () && reader.Depth >= depth);
3843 public DocumentationMember (XmlNode node)
3845 MemberName = node.Attributes ["MemberName"].Value;
3846 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3847 XmlAttribute l = n.Attributes ["Language"];
3848 XmlAttribute v = n.Attributes ["Value"];
3849 XmlAttribute apistyle = n.Attributes ["apistyle"];
3850 bool shouldUse = apistyle == null || apistyle.Value == "classic";
3851 if (l != null && v != null && shouldUse)
3852 MemberSignatures [l.Value] = v.Value;
3854 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3855 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
3857 ReturnType = rt.InnerText;
3858 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
3860 Parameters = new StringList (p.Count);
3861 for (int i = 0; i < p.Count; ++i)
3862 Parameters.Add (p [i].Attributes ["Type"].Value);
3867 public class DynamicParserContext {
3868 public ReadOnlyCollection<bool> TransformFlags;
3869 public int TransformIndex;
3871 public DynamicParserContext (ICustomAttributeProvider provider)
3874 if (provider.HasCustomAttributes &&
3875 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
3876 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
3877 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
3878 ? new CustomAttributeArgument [0]
3879 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
3881 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
3886 public enum MemberFormatterState {
3888 WithinGenericTypeParameters,
3891 public abstract class MemberFormatter {
3893 public virtual string Language {
3897 public string GetName (MemberReference member)
3899 return GetName (member, null);
3902 public virtual string GetName (MemberReference member, DynamicParserContext context)
3904 TypeReference type = member as TypeReference;
3906 return GetTypeName (type, context);
3907 MethodReference method = member as MethodReference;
3908 if (method != null && method.Name == ".ctor") // method.IsConstructor
3909 return GetConstructorName (method);
3911 return GetMethodName (method);
3912 PropertyReference prop = member as PropertyReference;
3914 return GetPropertyName (prop);
3915 FieldReference field = member as FieldReference;
3917 return GetFieldName (field);
3918 EventReference e = member as EventReference;
3920 return GetEventName (e);
3921 throw new NotSupportedException ("Can't handle: " +
3922 (member == null ? "null" : member.GetType().ToString()));
3925 protected virtual string GetTypeName (TypeReference type)
3927 return GetTypeName (type, null);
3930 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
3933 throw new ArgumentNullException ("type");
3934 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
3937 protected virtual char[] ArrayDelimeters {
3938 get {return new char[]{'[', ']'};}
3941 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3943 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3945 if (type is ArrayType) {
3946 TypeSpecification spec = type as TypeSpecification;
3947 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
3948 return AppendArrayModifiers (buf, (ArrayType) type);
3950 if (type is ByReferenceType) {
3951 return AppendRefTypeName (buf, type, context);
3953 if (type is PointerType) {
3954 return AppendPointerTypeName (buf, type, context);
3956 if (type is GenericParameter) {
3957 return AppendTypeName (buf, type, context);
3959 AppendNamespace (buf, type);
3960 GenericInstanceType genInst = type as GenericInstanceType;
3961 if (type.GenericParameters.Count == 0 &&
3962 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3963 return AppendFullTypeName (buf, type, context);
3965 return AppendGenericType (buf, type, context);
3968 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3970 string ns = DocUtils.GetNamespace (type);
3971 if (ns != null && ns.Length > 0)
3972 buf.Append (ns).Append ('.');
3976 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3978 if (type.DeclaringType != null)
3979 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
3980 return AppendTypeName (buf, type, context);
3983 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3985 if (context != null)
3986 context.TransformIndex++;
3987 return AppendTypeName (buf, type.Name);
3990 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3992 int n = typename.IndexOf ("`");
3994 return buf.Append (typename.Substring (0, n));
3995 return buf.Append (typename);
3998 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4000 buf.Append (ArrayDelimeters [0]);
4001 int rank = array.Rank;
4003 buf.Append (new string (',', rank-1));
4004 return buf.Append (ArrayDelimeters [1]);
4007 protected virtual string RefTypeModifier {
4011 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4013 TypeSpecification spec = type as TypeSpecification;
4014 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4015 .Append (RefTypeModifier);
4018 protected virtual string PointerModifier {
4022 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4024 TypeSpecification spec = type as TypeSpecification;
4025 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4026 .Append (PointerModifier);
4029 protected virtual char[] GenericTypeContainer {
4030 get {return new char[]{'<', '>'};}
4033 protected virtual char NestedTypeSeparator {
4037 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4039 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4040 type is GenericInstanceType ? type.GetElementType () : type);
4041 List<TypeReference> genArgs = GetGenericArguments (type);
4044 bool insertNested = false;
4045 foreach (var decl in decls) {
4046 TypeReference declDef = decl.Resolve () ?? decl;
4048 buf.Append (NestedTypeSeparator);
4050 insertNested = true;
4051 AppendTypeName (buf, declDef, context);
4052 int ac = DocUtils.GetGenericArgumentCount (declDef);
4056 buf.Append (GenericTypeContainer [0]);
4057 var origState = MemberFormatterState;
4058 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4059 _AppendTypeName (buf, genArgs [argIdx++], context);
4060 for (int i = 1; i < c; ++i) {
4061 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
4063 MemberFormatterState = origState;
4064 buf.Append (GenericTypeContainer [1]);
4070 protected List<TypeReference> GetGenericArguments (TypeReference type)
4072 var args = new List<TypeReference> ();
4073 GenericInstanceType inst = type as GenericInstanceType;
4075 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4077 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4081 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4086 protected virtual string GetConstructorName (MethodReference constructor)
4088 return constructor.Name;
4091 protected virtual string GetMethodName (MethodReference method)
4096 protected virtual string GetPropertyName (PropertyReference property)
4098 return property.Name;
4101 protected virtual string GetFieldName (FieldReference field)
4106 protected virtual string GetEventName (EventReference e)
4111 public virtual string GetDeclaration (MemberReference member)
4114 throw new ArgumentNullException ("member");
4115 TypeDefinition type = member as TypeDefinition;
4117 return GetTypeDeclaration (type);
4118 MethodDefinition method = member as MethodDefinition;
4119 if (method != null && method.IsConstructor)
4120 return GetConstructorDeclaration (method);
4122 return GetMethodDeclaration (method);
4123 PropertyDefinition prop = member as PropertyDefinition;
4125 return GetPropertyDeclaration (prop);
4126 FieldDefinition field = member as FieldDefinition;
4128 return GetFieldDeclaration (field);
4129 EventDefinition e = member as EventDefinition;
4131 return GetEventDeclaration (e);
4132 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4135 protected virtual string GetTypeDeclaration (TypeDefinition type)
4138 throw new ArgumentNullException ("type");
4139 StringBuilder buf = new StringBuilder (type.Name.Length);
4140 _AppendTypeName (buf, type, null);
4141 AppendGenericTypeConstraints (buf, type);
4142 return buf.ToString ();
4145 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4147 return GetConstructorName (constructor);
4150 protected virtual string GetMethodDeclaration (MethodDefinition method)
4152 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4153 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4156 // Special signature for destructors.
4157 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4158 return GetFinalizerName (method);
4160 StringBuilder buf = new StringBuilder ();
4162 AppendVisibility (buf, method);
4163 if (buf.Length == 0 &&
4164 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4167 AppendModifiers (buf, method);
4169 if (buf.Length != 0)
4171 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4173 AppendMethodName (buf, method);
4174 AppendGenericMethod (buf, method).Append (" ");
4175 AppendParameters (buf, method, method.Parameters);
4176 AppendGenericMethodConstraints (buf, method);
4177 return buf.ToString ();
4180 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4182 return buf.Append (method.Name);
4185 protected virtual string GetFinalizerName (MethodDefinition method)
4190 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4195 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4200 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4205 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4210 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4215 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4217 return GetPropertyName (property);
4220 protected virtual string GetFieldDeclaration (FieldDefinition field)
4222 return GetFieldName (field);
4225 protected virtual string GetEventDeclaration (EventDefinition e)
4227 return GetEventName (e);
4231 class ILFullMemberFormatter : MemberFormatter {
4233 public override string Language {
4234 get {return "ILAsm";}
4237 protected override char NestedTypeSeparator {
4243 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4245 if (GetBuiltinType (type.FullName) != null)
4247 string ns = DocUtils.GetNamespace (type);
4248 if (ns != null && ns.Length > 0) {
4249 if (type.IsValueType)
4250 buf.Append ("valuetype ");
4252 buf.Append ("class ");
4253 buf.Append (ns).Append ('.');
4258 protected static string GetBuiltinType (string t)
4261 case "System.Byte": return "unsigned int8";
4262 case "System.SByte": return "int8";
4263 case "System.Int16": return "int16";
4264 case "System.Int32": return "int32";
4265 case "System.Int64": return "int64";
4266 case "System.IntPtr": return "native int";
4268 case "System.UInt16": return "unsigned int16";
4269 case "System.UInt32": return "unsigned int32";
4270 case "System.UInt64": return "unsigned int64";
4271 case "System.UIntPtr": return "native unsigned int";
4273 case "System.Single": return "float32";
4274 case "System.Double": return "float64";
4275 case "System.Boolean": return "bool";
4276 case "System.Char": return "char";
4277 case "System.Void": return "void";
4278 case "System.String": return "string";
4279 case "System.Object": return "object";
4284 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4286 return buf.Append (typename);
4289 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4291 if (type is GenericParameter) {
4292 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4296 string s = GetBuiltinType (type.FullName);
4298 return buf.Append (s);
4300 return base.AppendTypeName (buf, type, context);
4303 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4305 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4306 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4308 GenericParameterAttributes attrs = type.Attributes;
4309 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4310 buf.Append ("class ");
4311 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4312 buf.Append ("struct ");
4313 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4314 buf.Append (".ctor ");
4315 IList<TypeReference> constraints = type.Constraints;
4316 MemberFormatterState = 0;
4317 if (constraints.Count > 0) {
4318 var full = new ILFullMemberFormatter ();
4319 buf.Append ("(").Append (full.GetName (constraints [0]));
4320 for (int i = 1; i < constraints.Count; ++i) {
4321 buf.Append (", ").Append (full.GetName (constraints [i]));
4325 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4327 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4329 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4334 protected override string GetTypeDeclaration (TypeDefinition type)
4336 string visibility = GetTypeVisibility (type.Attributes);
4337 if (visibility == null)
4340 StringBuilder buf = new StringBuilder ();
4342 buf.Append (".class ");
4344 buf.Append ("nested ");
4345 buf.Append (visibility).Append (" ");
4346 if (type.IsInterface)
4347 buf.Append ("interface ");
4348 if (type.IsSequentialLayout)
4349 buf.Append ("sequential ");
4350 if (type.IsAutoLayout)
4351 buf.Append ("auto ");
4352 if (type.IsAnsiClass)
4353 buf.Append ("ansi ");
4354 if (type.IsAbstract)
4355 buf.Append ("abstract ");
4356 if (type.IsSerializable)
4357 buf.Append ("serializable ");
4359 buf.Append ("sealed ");
4360 if (type.IsBeforeFieldInit)
4361 buf.Append ("beforefieldinit ");
4362 var state = MemberFormatterState;
4363 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4364 buf.Append (GetName (type));
4365 MemberFormatterState = state;
4366 var full = new ILFullMemberFormatter ();
4367 if (type.BaseType != null) {
4368 buf.Append (" extends ");
4369 if (type.BaseType.FullName == "System.Object")
4370 buf.Append ("System.Object");
4372 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4375 foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()))
4376 .Select (i => full.GetName (i))
4377 .OrderBy (n => n)) {
4379 buf.Append (" implements ");
4388 return buf.ToString ();
4391 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4393 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4394 type is GenericInstanceType ? type.GetElementType () : type);
4396 foreach (var decl in decls) {
4397 TypeReference declDef = decl.Resolve () ?? decl;
4399 buf.Append (NestedTypeSeparator);
4402 AppendTypeName (buf, declDef, context);
4406 foreach (TypeReference arg in GetGenericArguments (type)) {
4410 _AppendTypeName (buf, arg, context);
4416 static string GetTypeVisibility (TypeAttributes ta)
4418 switch (ta & TypeAttributes.VisibilityMask) {
4419 case TypeAttributes.Public:
4420 case TypeAttributes.NestedPublic:
4423 case TypeAttributes.NestedFamily:
4424 case TypeAttributes.NestedFamORAssem:
4432 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4434 return GetMethodDeclaration (constructor);
4437 protected override string GetMethodDeclaration (MethodDefinition method)
4439 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4442 var buf = new StringBuilder ();
4443 buf.Append (".method ");
4444 AppendVisibility (buf, method);
4445 if (method.IsStatic)
4446 buf.Append ("static ");
4447 if (method.IsHideBySig)
4448 buf.Append ("hidebysig ");
4449 if (method.IsPInvokeImpl) {
4450 var info = method.PInvokeInfo;
4451 buf.Append ("pinvokeimpl (\"")
4452 .Append (info.Module.Name)
4453 .Append ("\" as \"")
4454 .Append (info.EntryPoint)
4456 if (info.IsCharSetAuto)
4457 buf.Append (" auto");
4458 if (info.IsCharSetUnicode)
4459 buf.Append (" unicode");
4460 if (info.IsCharSetAnsi)
4461 buf.Append (" ansi");
4462 if (info.IsCallConvCdecl)
4463 buf.Append (" cdecl");
4464 if (info.IsCallConvStdCall)
4465 buf.Append (" stdcall");
4466 if (info.IsCallConvWinapi)
4467 buf.Append (" winapi");
4468 if (info.IsCallConvThiscall)
4469 buf.Append (" thiscall");
4470 if (info.SupportsLastError)
4471 buf.Append (" lasterr");
4474 if (method.IsSpecialName)
4475 buf.Append ("specialname ");
4476 if (method.IsRuntimeSpecialName)
4477 buf.Append ("rtspecialname ");
4478 if (method.IsNewSlot)
4479 buf.Append ("newslot ");
4480 if (method.IsVirtual)
4481 buf.Append ("virtual ");
4482 if (!method.IsStatic)
4483 buf.Append ("instance ");
4484 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4486 .Append (method.Name);
4487 if (method.IsGenericMethod ()) {
4488 var state = MemberFormatterState;
4489 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4490 IList<GenericParameter> args = method.GenericParameters;
4491 if (args.Count > 0) {
4493 _AppendTypeName (buf, args [0], null);
4494 for (int i = 1; i < args.Count; ++i)
4495 _AppendTypeName (buf.Append (", "), args [i], null);
4498 MemberFormatterState = state;
4503 for (int i = 0; i < method.Parameters.Count; ++i) {
4507 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4509 buf.Append (method.Parameters [i].Name);
4513 buf.Append (" cil");
4514 if (method.IsRuntime)
4515 buf.Append (" runtime");
4516 if (method.IsManaged)
4517 buf.Append (" managed");
4519 return buf.ToString ();
4522 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4524 if (DocUtils.IsExplicitlyImplemented (method)) {
4525 TypeReference iface;
4526 MethodReference ifaceMethod;
4527 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4528 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4530 .Append (ifaceMethod.Name);
4532 return base.AppendMethodName (buf, method);
4535 protected override string RefTypeModifier {
4539 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4541 if (method.IsPublic)
4542 return buf.Append ("public ");
4543 if (method.IsFamilyAndAssembly)
4544 return buf.Append ("familyandassembly");
4545 if (method.IsFamilyOrAssembly)
4546 return buf.Append ("familyorassembly");
4547 if (method.IsFamily)
4548 return buf.Append ("family");
4552 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4554 string modifiers = String.Empty;
4555 if (method.IsStatic) modifiers += " static";
4556 if (method.IsVirtual && !method.IsAbstract) {
4557 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4558 else modifiers += " override";
4560 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4561 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4562 if (method.IsFinal) modifiers += " sealed";
4563 if (modifiers == " virtual sealed") modifiers = "";
4565 return buf.Append (modifiers);
4568 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4570 if (method.IsGenericMethod ()) {
4571 IList<GenericParameter> args = method.GenericParameters;
4572 if (args.Count > 0) {
4574 buf.Append (args [0].Name);
4575 for (int i = 1; i < args.Count; ++i)
4576 buf.Append (",").Append (args [i].Name);
4583 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4585 return AppendParameters (buf, method, parameters, '(', ')');
4588 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4592 if (parameters.Count > 0) {
4593 if (DocUtils.IsExtensionMethod (method))
4594 buf.Append ("this ");
4595 AppendParameter (buf, parameters [0]);
4596 for (int i = 1; i < parameters.Count; ++i) {
4598 AppendParameter (buf, parameters [i]);
4602 return buf.Append (end);
4605 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4607 if (parameter.ParameterType is ByReferenceType) {
4608 if (parameter.IsOut)
4609 buf.Append ("out ");
4611 buf.Append ("ref ");
4613 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4614 return buf.Append (parameter.Name);
4617 protected override string GetPropertyDeclaration (PropertyDefinition property)
4619 MethodDefinition gm = null, sm = null;
4621 string get_visible = null;
4622 if ((gm = property.GetMethod) != null &&
4623 (DocUtils.IsExplicitlyImplemented (gm) ||
4624 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4625 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4626 string set_visible = null;
4627 if ((sm = property.SetMethod) != null &&
4628 (DocUtils.IsExplicitlyImplemented (sm) ||
4629 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4630 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4632 if ((set_visible == null) && (get_visible == null))
4635 StringBuilder buf = new StringBuilder ()
4636 .Append (".property ");
4637 if (!(gm ?? sm).IsStatic)
4638 buf.Append ("instance ");
4639 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4640 buf.Append (' ').Append (property.Name);
4641 if (!property.HasParameters || property.Parameters.Count == 0)
4642 return buf.ToString ();
4646 foreach (ParameterDefinition p in property.Parameters) {
4650 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4654 return buf.ToString ();
4657 protected override string GetFieldDeclaration (FieldDefinition field)
4659 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4660 if (declType.IsEnum && field.Name == "value__")
4661 return null; // This member of enums aren't documented.
4663 StringBuilder buf = new StringBuilder ();
4664 AppendFieldVisibility (buf, field);
4665 if (buf.Length == 0)
4668 buf.Insert (0, ".field ");
4671 buf.Append ("static ");
4672 if (field.IsInitOnly)
4673 buf.Append ("initonly ");
4674 if (field.IsLiteral)
4675 buf.Append ("literal ");
4676 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4677 buf.Append (' ').Append (field.Name);
4678 AppendFieldValue (buf, field);
4680 return buf.ToString ();
4683 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4686 return buf.Append ("public ");
4687 if (field.IsFamilyAndAssembly)
4688 return buf.Append ("familyandassembly ");
4689 if (field.IsFamilyOrAssembly)
4690 return buf.Append ("familyorassembly ");
4692 return buf.Append ("family ");
4696 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4698 // enums have a value__ field, which we ignore
4699 if (field.DeclaringType.IsGenericType ())
4701 if (field.HasConstant && field.IsLiteral) {
4704 val = field.Constant;
4709 buf.Append (" = ").Append ("null");
4710 else if (val is Enum)
4712 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4714 .Append (val.ToString ())
4716 else if (val is IFormattable) {
4717 string value = ((IFormattable)val).ToString();
4720 buf.Append ("\"" + value + "\"");
4722 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4731 protected override string GetEventDeclaration (EventDefinition e)
4733 StringBuilder buf = new StringBuilder ();
4734 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4739 buf.Append (".event ")
4740 .Append (GetName (e.EventType))
4744 return buf.ToString ();
4748 class ILMemberFormatter : ILFullMemberFormatter {
4749 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4755 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
4756 protected static string _GetBuiltinType (string t)
4758 //string moddedType = base.GetBuiltinType (t);
4760 //return moddedType;
4764 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
4765 protected override string GetCSharpType (string t) {
4766 string moddedType = base.GetCSharpType (t);
4768 switch (moddedType) {
4769 case "int": return "nint";
4774 case "System.Drawing.SizeF":
4775 return "CoreGraphics.CGSize";
4776 case "System.Drawing.PointF":
4777 return "CoreGraphics.CGPoint";
4778 case "System.Drawing.RectangleF":
4779 return "CoreGraphics.CGPoint";
4785 class CSharpFullMemberFormatter : MemberFormatter {
4787 public override string Language {
4791 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4794 string ns = DocUtils.GetNamespace (type);
4795 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4796 buf.Append (ns).Append ('.');
4800 protected virtual string GetCSharpType (string t)
4803 case "System.Byte": return "byte";
4804 case "System.SByte": return "sbyte";
4805 case "System.Int16": return "short";
4806 case "System.Int32": return "int";
4807 case "System.Int64": return "long";
4809 case "System.UInt16": return "ushort";
4810 case "System.UInt32": return "uint";
4811 case "System.UInt64": return "ulong";
4813 case "System.Single": return "float";
4814 case "System.Double": return "double";
4815 case "System.Decimal": return "decimal";
4816 case "System.Boolean": return "bool";
4817 case "System.Char": return "char";
4818 case "System.Void": return "void";
4819 case "System.String": return "string";
4820 case "System.Object": return "object";
4825 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4827 if (context != null && context.TransformFlags != null &&
4828 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
4829 context.TransformIndex++;
4830 return buf.Append ("dynamic");
4833 if (type is GenericParameter)
4834 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
4835 string t = type.FullName;
4836 if (!t.StartsWith ("System.")) {
4837 return base.AppendTypeName (buf, type, context);
4840 string s = GetCSharpType (t);
4842 if (context != null)
4843 context.TransformIndex++;
4844 return buf.Append (s);
4847 return base.AppendTypeName (buf, type, context);
4850 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
4852 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4854 GenericParameterAttributes attrs = type.Attributes;
4855 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4856 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4860 buf.Append ("out ");
4864 protected override string GetTypeDeclaration (TypeDefinition type)
4866 string visibility = GetTypeVisibility (type.Attributes);
4867 if (visibility == null)
4870 StringBuilder buf = new StringBuilder ();
4872 buf.Append (visibility);
4875 MemberFormatter full = new CSharpFullMemberFormatter ();
4877 if (DocUtils.IsDelegate (type)) {
4878 buf.Append("delegate ");
4879 MethodDefinition invoke = type.GetMethod ("Invoke");
4880 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
4881 buf.Append (GetName (type));
4882 AppendParameters (buf, invoke, invoke.Parameters);
4883 AppendGenericTypeConstraints (buf, type);
4886 return buf.ToString();
4889 if (type.IsAbstract && !type.IsInterface)
4890 buf.Append("abstract ");
4891 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4892 buf.Append("sealed ");
4893 buf.Replace ("abstract sealed", "static");
4895 buf.Append (GetTypeKind (type));
4897 buf.Append (GetCSharpType (type.FullName) == null
4902 TypeReference basetype = type.BaseType;
4903 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4906 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4907 .Select (iface => full.GetName (iface))
4911 if (basetype != null || interface_names.Count > 0)
4914 if (basetype != null) {
4915 buf.Append (full.GetName (basetype));
4916 if (interface_names.Count > 0)
4920 for (int i = 0; i < interface_names.Count; i++){
4923 buf.Append (interface_names [i]);
4925 AppendGenericTypeConstraints (buf, type);
4928 return buf.ToString ();
4931 static string GetTypeKind (TypeDefinition t)
4937 if (t.IsClass || t.FullName == "System.Enum")
4941 throw new ArgumentException(t.FullName);
4944 static string GetTypeVisibility (TypeAttributes ta)
4946 switch (ta & TypeAttributes.VisibilityMask) {
4947 case TypeAttributes.Public:
4948 case TypeAttributes.NestedPublic:
4951 case TypeAttributes.NestedFamily:
4952 case TypeAttributes.NestedFamORAssem:
4960 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4962 if (type.GenericParameters.Count == 0)
4964 return AppendConstraints (buf, type.GenericParameters);
4967 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4969 foreach (GenericParameter genArg in genArgs) {
4970 GenericParameterAttributes attrs = genArg.Attributes;
4971 IList<TypeReference> constraints = genArg.Constraints;
4972 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4975 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4976 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4977 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4980 if (!isref && !isvt && !isnew && constraints.Count == 0)
4982 buf.Append (" where ").Append (genArg.Name).Append (" : ");
4984 buf.Append ("class");
4988 buf.Append ("struct");
4991 if (constraints.Count > 0 && !isvt) {
4994 buf.Append (GetTypeName (constraints [0]));
4995 for (int i = 1; i < constraints.Count; ++i)
4996 buf.Append (", ").Append (GetTypeName (constraints [i]));
4998 if (isnew && !isvt) {
5001 buf.Append ("new()");
5007 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5009 StringBuilder buf = new StringBuilder ();
5010 AppendVisibility (buf, constructor);
5011 if (buf.Length == 0)
5015 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
5016 AppendParameters (buf, constructor, constructor.Parameters);
5019 return buf.ToString ();
5022 protected override string GetMethodDeclaration (MethodDefinition method)
5024 string decl = base.GetMethodDeclaration (method);
5030 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
5032 if (DocUtils.IsExplicitlyImplemented (method)) {
5033 TypeReference iface;
5034 MethodReference ifaceMethod;
5035 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5036 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
5038 .Append (ifaceMethod.Name);
5040 return base.AppendMethodName (buf, method);
5043 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
5045 if (method.GenericParameters.Count == 0)
5047 return AppendConstraints (buf, method.GenericParameters);
5050 protected override string RefTypeModifier {
5054 protected override string GetFinalizerName (MethodDefinition method)
5056 return "~" + method.DeclaringType.Name + " ()";
5059 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
5063 if (method.IsPublic)
5064 return buf.Append ("public");
5065 if (method.IsFamily || method.IsFamilyOrAssembly)
5066 return buf.Append ("protected");
5070 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5072 string modifiers = String.Empty;
5073 if (method.IsStatic) modifiers += " static";
5074 if (method.IsVirtual && !method.IsAbstract) {
5075 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5076 else modifiers += " override";
5078 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5079 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5080 if (method.IsFinal) modifiers += " sealed";
5081 if (modifiers == " virtual sealed") modifiers = "";
5083 return buf.Append (modifiers);
5086 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5088 if (method.IsGenericMethod ()) {
5089 IList<GenericParameter> args = method.GenericParameters;
5090 if (args.Count > 0) {
5092 buf.Append (args [0].Name);
5093 for (int i = 1; i < args.Count; ++i)
5094 buf.Append (",").Append (args [i].Name);
5101 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5103 return AppendParameters (buf, method, parameters, '(', ')');
5106 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5110 if (parameters.Count > 0) {
5111 if (DocUtils.IsExtensionMethod (method))
5112 buf.Append ("this ");
5113 AppendParameter (buf, parameters [0]);
5114 for (int i = 1; i < parameters.Count; ++i) {
5116 AppendParameter (buf, parameters [i]);
5120 return buf.Append (end);
5123 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5125 if (parameter.ParameterType is ByReferenceType) {
5126 if (parameter.IsOut)
5127 buf.Append ("out ");
5129 buf.Append ("ref ");
5131 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5132 buf.Append (parameter.Name);
5133 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5134 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5139 protected override string GetPropertyDeclaration (PropertyDefinition property)
5141 MethodDefinition method;
5143 string get_visible = null;
5144 if ((method = property.GetMethod) != null &&
5145 (DocUtils.IsExplicitlyImplemented (method) ||
5146 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5147 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5148 string set_visible = null;
5149 if ((method = property.SetMethod) != null &&
5150 (DocUtils.IsExplicitlyImplemented (method) ||
5151 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5152 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5154 if ((set_visible == null) && (get_visible == null))
5158 StringBuilder buf = new StringBuilder ();
5159 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5160 buf.Append (visibility = get_visible);
5161 else if (set_visible != null && get_visible == null)
5162 buf.Append (visibility = set_visible);
5164 buf.Append (visibility = "public");
5166 // Pick an accessor to use for static/virtual/override/etc. checks.
5167 method = property.SetMethod;
5169 method = property.GetMethod;
5171 string modifiers = String.Empty;
5172 if (method.IsStatic) modifiers += " static";
5173 if (method.IsVirtual && !method.IsAbstract) {
5174 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5175 modifiers += " virtual";
5177 modifiers += " override";
5179 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5180 if (method.IsAbstract && !declDef.IsInterface)
5181 modifiers += " abstract";
5183 modifiers += " sealed";
5184 if (modifiers == " virtual sealed")
5186 buf.Append (modifiers).Append (' ');
5188 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5190 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5191 string name = property.Name;
5192 foreach (MemberReference mi in defs) {
5193 if (mi == property) {
5198 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5200 if (property.Parameters.Count != 0) {
5201 AppendParameters (buf, method, property.Parameters, '[', ']');
5205 if (get_visible != null) {
5206 if (get_visible != visibility)
5207 buf.Append (' ').Append (get_visible);
5208 buf.Append (" get;");
5210 if (set_visible != null) {
5211 if (set_visible != visibility)
5212 buf.Append (' ').Append (set_visible);
5213 buf.Append (" set;");
5217 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5220 protected override string GetFieldDeclaration (FieldDefinition field)
5222 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5223 if (declType.IsEnum && field.Name == "value__")
5224 return null; // This member of enums aren't documented.
5226 StringBuilder buf = new StringBuilder ();
5227 AppendFieldVisibility (buf, field);
5228 if (buf.Length == 0)
5231 if (declType.IsEnum)
5234 if (field.IsStatic && !field.IsLiteral)
5235 buf.Append (" static");
5236 if (field.IsInitOnly)
5237 buf.Append (" readonly");
5238 if (field.IsLiteral)
5239 buf.Append (" const");
5241 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5242 buf.Append (field.Name);
5243 AppendFieldValue (buf, field);
5246 return buf.ToString ();
5249 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5252 return buf.Append ("public");
5253 if (field.IsFamily || field.IsFamilyOrAssembly)
5254 return buf.Append ("protected");
5258 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5260 // enums have a value__ field, which we ignore
5261 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5262 field.DeclaringType.IsGenericType ())
5264 if (field.HasConstant && field.IsLiteral) {
5267 val = field.Constant;
5272 buf.Append (" = ").Append ("null");
5273 else if (val is Enum)
5274 buf.Append (" = ").Append (val.ToString ());
5275 else if (val is IFormattable) {
5276 string value = ((IFormattable)val).ToString();
5278 value = "\"" + value + "\"";
5279 buf.Append (" = ").Append (value);
5285 protected override string GetEventDeclaration (EventDefinition e)
5287 StringBuilder buf = new StringBuilder ();
5288 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5292 AppendModifiers (buf, e.AddMethod);
5294 buf.Append (" event ");
5295 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5296 buf.Append (e.Name).Append (';');
5298 return buf.ToString ();
5302 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5303 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5309 class DocTypeFullMemberFormatter : MemberFormatter {
5310 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5312 protected override char NestedTypeSeparator {
5317 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5318 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5324 class SlashDocMemberFormatter : MemberFormatter {
5326 protected override char[] GenericTypeContainer {
5327 get {return new char[]{'{', '}'};}
5330 private bool AddTypeCount = true;
5332 private TypeReference genDeclType;
5333 private MethodReference genDeclMethod;
5335 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5337 if (type is GenericParameter) {
5339 if (genDeclType != null) {
5340 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5341 for (int i = 0; i < genArgs.Count; ++i) {
5342 if (genArgs [i].Name == type.Name) {
5343 buf.Append ('`').Append (i);
5348 if (genDeclMethod != null) {
5349 IList<GenericParameter> genArgs = null;
5350 if (genDeclMethod.IsGenericMethod ()) {
5351 genArgs = genDeclMethod.GenericParameters;
5352 for (int i = 0; i < genArgs.Count; ++i) {
5353 if (genArgs [i].Name == type.Name) {
5354 buf.Append ("``").Append (i);
5360 if (genDeclType == null && genDeclMethod == null) {
5361 // Probably from within an explicitly implemented interface member,
5362 // where CSC uses parameter names instead of indices (why?), e.g.
5363 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5364 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5365 buf.Append (type.Name);
5367 if (buf.Length == l) {
5368 throw new Exception (string.Format (
5369 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5370 type.Name, genDeclType, genDeclMethod));
5374 base.AppendTypeName (buf, type, context);
5376 int numArgs = type.GenericParameters.Count;
5377 if (type.DeclaringType != null)
5378 numArgs -= type.GenericParameters.Count;
5380 buf.Append ('`').Append (numArgs);
5387 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5389 buf.Append (ArrayDelimeters [0]);
5390 int rank = array.Rank;
5393 for (int i = 1; i < rank; ++i) {
5397 return buf.Append (ArrayDelimeters [1]);
5400 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5403 base.AppendGenericType (buf, type, context);
5405 AppendType (buf, type, context);
5409 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5411 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5412 bool insertNested = false;
5413 int prevParamCount = 0;
5414 foreach (var decl in decls) {
5416 buf.Append (NestedTypeSeparator);
5417 insertNested = true;
5418 base.AppendTypeName (buf, decl, context);
5419 int argCount = DocUtils.GetGenericArgumentCount (decl);
5420 int numArgs = argCount - prevParamCount;
5421 prevParamCount = argCount;
5423 buf.Append ('`').Append (numArgs);
5428 public override string GetDeclaration (MemberReference member)
5430 TypeReference r = member as TypeReference;
5432 return "T:" + GetTypeName (r);
5434 return base.GetDeclaration (member);
5437 protected override string GetConstructorName (MethodReference constructor)
5439 return GetMethodDefinitionName (constructor, "#ctor");
5442 protected override string GetMethodName (MethodReference method)
5445 MethodDefinition methodDef = method as MethodDefinition;
5446 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5449 TypeReference iface;
5450 MethodReference ifaceMethod;
5451 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5452 AddTypeCount = false;
5453 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5454 AddTypeCount = true;
5456 return GetMethodDefinitionName (method, name);
5459 private string GetMethodDefinitionName (MethodReference method, string name)
5461 StringBuilder buf = new StringBuilder ();
5462 buf.Append (GetTypeName (method.DeclaringType));
5464 buf.Append (name.Replace (".", "#"));
5465 if (method.IsGenericMethod ()) {
5466 IList<GenericParameter> genArgs = method.GenericParameters;
5467 if (genArgs.Count > 0)
5468 buf.Append ("``").Append (genArgs.Count);
5470 IList<ParameterDefinition> parameters = method.Parameters;
5472 genDeclType = method.DeclaringType;
5473 genDeclMethod = method;
5474 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5478 genDeclMethod = null;
5480 return buf.ToString ();
5483 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5485 if (parameters.Count == 0)
5490 AppendParameter (buf, genArgs, parameters [0]);
5491 for (int i = 1; i < parameters.Count; ++i) {
5493 AppendParameter (buf, genArgs, parameters [i]);
5496 return buf.Append (')');
5499 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5501 AddTypeCount = false;
5502 buf.Append (GetTypeName (parameter.ParameterType));
5503 AddTypeCount = true;
5507 protected override string GetPropertyName (PropertyReference property)
5511 PropertyDefinition propertyDef = property as PropertyDefinition;
5512 MethodDefinition method = null;
5513 if (propertyDef != null)
5514 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5515 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5516 name = property.Name;
5518 TypeReference iface;
5519 MethodReference ifaceMethod;
5520 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5521 AddTypeCount = false;
5522 name = string.Join ("#", new string[]{
5523 GetTypeName (iface).Replace (".", "#"),
5524 DocUtils.GetMember (property.Name)
5526 AddTypeCount = true;
5529 StringBuilder buf = new StringBuilder ();
5530 buf.Append (GetName (property.DeclaringType));
5533 IList<ParameterDefinition> parameters = property.Parameters;
5534 if (parameters.Count > 0) {
5535 genDeclType = property.DeclaringType;
5537 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5538 AppendParameter (buf, genArgs, parameters [0]);
5539 for (int i = 1; i < parameters.Count; ++i) {
5541 AppendParameter (buf, genArgs, parameters [i]);
5546 return buf.ToString ();
5549 protected override string GetFieldName (FieldReference field)
5551 return string.Format ("{0}.{1}",
5552 GetName (field.DeclaringType), field.Name);
5555 protected override string GetEventName (EventReference e)
5557 return string.Format ("{0}.{1}",
5558 GetName (e.DeclaringType), e.Name);
5561 protected override string GetTypeDeclaration (TypeDefinition type)
5563 string name = GetName (type);
5569 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5571 string name = GetName (constructor);
5577 protected override string GetMethodDeclaration (MethodDefinition method)
5579 string name = GetName (method);
5582 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5583 genDeclType = method.DeclaringType;
5584 genDeclMethod = method;
5585 name += "~" + GetName (method.ReturnType);
5587 genDeclMethod = null;
5592 protected override string GetPropertyDeclaration (PropertyDefinition property)
5594 string name = GetName (property);
5600 protected override string GetFieldDeclaration (FieldDefinition field)
5602 string name = GetName (field);
5608 protected override string GetEventDeclaration (EventDefinition e)
5610 string name = GetName (e);
5617 class FileNameMemberFormatter : SlashDocMemberFormatter {
5618 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5623 protected override char NestedTypeSeparator {