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 && MDocUpdater.HasDroppedNamespace () && !typename.StartsWith ("System")) {
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;
169 public static bool HasDroppedNamespace() {
170 return !string.IsNullOrWhiteSpace (droppedNamespace);
172 public string PreserveTag { get; set; }
173 public static MDocUpdater Instance { get; private set; }
174 public static bool SwitchingToMagicTypes { get; private set; }
176 public override void Run (IEnumerable<string> args)
179 show_exceptions = DebugOutput;
180 var types = new List<string> ();
181 var p = new OptionSet () {
183 "Delete removed members from the XML files.",
184 v => delete = v != null },
186 "Document potential exceptions that members can generate. {SOURCES} " +
187 "is a comma-separated list of:\n" +
188 " asm Method calls in same assembly\n" +
189 " depasm Method calls in dependent assemblies\n" +
190 " all Record all possible exceptions\n" +
191 " added Modifier; only create <exception/>s\n" +
192 " for NEW types/members\n" +
193 "If nothing is specified, then only exceptions from the member will " +
195 v => exceptions = ParseExceptionLocations (v) },
197 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
200 case "ignore-missing-types":
201 ignore_missing_types = true;
203 case "no-assembly-versions":
204 no_assembly_versions = true;
207 throw new Exception ("Unsupported flag `" + v + "'.");
210 { "fignore-missing-types",
211 "Do not report an error if a --type=TYPE type\nwas not found.",
212 v => ignore_missing_types = v != null },
213 { "fno-assembly-versions",
214 "Do not generate //AssemblyVersion elements.",
215 v => no_assembly_versions = v != null },
217 "Import documentation from {FILE}.",
218 v => AddImporter (v) },
220 "Check for assembly references in {DIRECTORY}.",
221 v => assemblyResolver.AddSearchDirectory (v) },
223 "Ignored for compatibility with update-ecma-xml.",
226 "Root {DIRECTORY} to generate/update documentation.",
229 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
230 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
231 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
233 "Manually specify the assembly {VERSION} that new members were added in.",
236 "Only update documentation for {TYPE}.",
237 v => types.Add (v) },
239 "Instructs the update process that {NAMESPACE} has been dropped, so that types and members will match existing documentation nodes.",
240 v => droppedNamespace = v },
242 "If the new assembly is switching to 'magic types', then this switch should be defined.",
243 v => SwitchingToMagicTypes = true },
245 "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
246 v => PreserveTag = "true" },
248 var assemblies = Parse (p, args, "update",
249 "[OPTIONS]+ ASSEMBLIES",
250 "Create or update documentation from ASSEMBLIES.");
251 if (assemblies == null)
253 if (assemblies.Count == 0)
254 Error ("No assemblies specified.");
256 foreach (var dir in assemblies
257 .Where (a => a.Contains (Path.DirectorySeparatorChar))
258 .Select (a => Path.GetDirectoryName (a)))
259 assemblyResolver.AddSearchDirectory (dir);
261 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
264 throw new InvalidOperationException("The --out option is required.");
266 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
268 // Store types that have been forwarded to avoid duplicate generation
269 GatherForwardedTypes ();
271 docEnum = docEnum ?? new DocumentationEnumerator ();
273 // PERFORM THE UPDATES
275 if (types.Count > 0) {
277 DoUpdateTypes (srcPath, types, srcPath);
280 else if (opts.@namespace != null)
281 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
282 Path.Combine (dest_dir, opts.@namespace));
285 DoUpdateAssemblies (srcPath, srcPath);
287 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
289 public static bool IsInAssemblies(string name) {
290 var query = Instance.assemblies.Where (a => a.MainModule.Name == name).ToArray ();
291 return query.Length > 0;
293 void AddImporter (string path)
296 XmlReader r = new XmlTextReader (path);
298 while (r.NodeType != XmlNodeType.Element) {
300 Error ("Unable to read XML file: {0}.", path);
302 if (r.LocalName == "doc") {
303 importers.Add (new MsxdocDocumentationImporter (path));
305 else if (r.LocalName == "Libraries") {
306 var ecmadocs = new XmlTextReader (path);
307 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
308 importers.Add (new EcmaDocumentationImporter (ecmadocs));
311 Error ("Unsupported XML format within {0}.", path);
314 } catch (Exception e) {
315 Environment.ExitCode = 1;
316 Error ("Could not load XML file: {0}.", e.Message);
320 void GatherForwardedTypes ()
322 foreach (var asm in assemblies)
323 foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
324 forwardedTypes.Add (type);
327 static ExceptionLocations ParseExceptionLocations (string s)
329 ExceptionLocations loc = ExceptionLocations.Member;
332 foreach (var type in s.Split (',')) {
334 case "added": loc |= ExceptionLocations.AddedMembers; break;
335 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
336 case "asm": loc |= ExceptionLocations.Assembly; break;
337 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
338 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
344 internal void Warning (string format, params object[] args)
346 Message (TraceLevel.Warning, "mdoc: " + format, args);
349 private AssemblyDefinition LoadAssembly (string name)
351 AssemblyDefinition assembly = null;
353 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
354 } catch (System.IO.FileNotFoundException) { }
356 if (assembly == null)
357 throw new InvalidOperationException("Assembly " + name + " not found.");
362 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
363 OrderTypeAttributes (element);
364 XmlTextWriter writer = new XmlTextWriter(output);
365 writer.Formatting = Formatting.Indented;
366 writer.Indentation = 2;
367 writer.IndentChar = ' ';
368 element.WriteTo(writer);
372 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
374 Action<string> creator = file => {
375 using (var writer = OpenWrite (file, mode))
379 MdocFile.UpdateFile (filename, creator);
382 private static void OrderTypeAttributes (XmlElement e)
384 foreach (XmlElement type in e.SelectNodes ("//Type")) {
385 OrderTypeAttributes (type.Attributes);
389 static readonly string[] TypeAttributeOrder = {
390 "Name", "FullName", "FullNameSP", "Maintainer"
393 private static void OrderTypeAttributes (XmlAttributeCollection c)
395 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
396 for (int i = 0; i < c.Count; ++i) {
397 XmlAttribute a = c [i];
398 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
399 if (a.Name == TypeAttributeOrder [j]) {
405 for (int i = attrs.Length-1; i >= 0; --i) {
406 XmlAttribute n = attrs [i];
409 XmlAttribute r = null;
410 for (int j = i+1; j < attrs.Length; ++j) {
411 if (attrs [j] != null) {
419 c.InsertBefore (n, r);
423 private XmlDocument CreateIndexStub()
425 XmlDocument index = new XmlDocument();
427 XmlElement index_root = index.CreateElement("Overview");
428 index.AppendChild(index_root);
430 if (assemblies.Count == 0)
431 throw new Exception ("No assembly");
433 XmlElement index_assemblies = index.CreateElement("Assemblies");
434 index_root.AppendChild(index_assemblies);
436 XmlElement index_remarks = index.CreateElement("Remarks");
437 index_remarks.InnerText = "To be added.";
438 index_root.AppendChild(index_remarks);
440 XmlElement index_copyright = index.CreateElement("Copyright");
441 index_copyright.InnerText = "To be added.";
442 index_root.AppendChild(index_copyright);
444 XmlElement index_types = index.CreateElement("Types");
445 index_root.AppendChild(index_types);
450 private static void WriteNamespaceStub(string ns, string outdir) {
451 XmlDocument index = new XmlDocument();
453 XmlElement index_root = index.CreateElement("Namespace");
454 index.AppendChild(index_root);
456 index_root.SetAttribute("Name", ns);
458 XmlElement index_docs = index.CreateElement("Docs");
459 index_root.AppendChild(index_docs);
461 XmlElement index_summary = index.CreateElement("summary");
462 index_summary.InnerText = "To be added.";
463 index_docs.AppendChild(index_summary);
465 XmlElement index_remarks = index.CreateElement("remarks");
466 index_remarks.InnerText = "To be added.";
467 index_docs.AppendChild(index_remarks);
469 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
470 writer => WriteXml (index.DocumentElement, writer));
473 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
475 var index = CreateIndexForTypes (dest);
477 var found = new HashSet<string> ();
478 foreach (AssemblyDefinition assembly in assemblies) {
479 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
480 string relpath = DoUpdateType (type, basepath, dest);
484 found.Add (type.FullName);
489 index.Add (assembly);
497 if (ignore_missing_types)
500 var notFound = from n in typenames where !found.Contains (n) select n;
502 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
505 class IndexForTypes {
511 XmlElement index_types;
512 XmlElement index_assemblies;
514 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
517 this.indexFile = indexFile;
520 index_types = WriteElement (index.DocumentElement, "Types");
521 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
524 public void Add (AssemblyDefinition assembly)
526 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
529 app.AddIndexAssembly (assembly, index_assemblies);
532 public void Add (TypeDefinition type)
534 app.AddIndexType (type, index_types);
539 SortIndexEntries (index_types);
540 WriteFile (indexFile, FileMode.Create,
541 writer => WriteXml (index.DocumentElement, writer));
545 IndexForTypes CreateIndexForTypes (string dest)
547 string indexFile = Path.Combine (dest, "index.xml");
548 if (File.Exists (indexFile))
550 return new IndexForTypes (this, indexFile, CreateIndexStub ());
553 /// <summary>Constructs the presumed path to the type's documentation file</summary>
554 /// <returns><c>true</c>, if the type file was found, <c>false</c> otherwise.</returns>
555 /// <param name="result">A typle that contains 1) the 'reltypefile', 2) the 'typefile', and 3) the file info</param>
556 bool TryFindTypeFile(string nsname, string typename, string basepath, out Tuple<string, string, FileInfo> result) {
557 string reltypefile = DocUtils.PathCombine (nsname, typename + ".xml");
558 string typefile = Path.Combine (basepath, reltypefile);
559 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
561 result = new Tuple<string, string, FileInfo> (reltypefile, typefile, file);
566 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
568 if (type.Namespace == null)
569 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
571 if (!IsPublic (type))
574 // Must get the A+B form of the type name.
575 string typename = GetTypeFileName(type);
576 string nsname = DocUtils.GetNamespace (type);
578 // Find the file, if it exists
579 string[] searchLocations = new string[] {
583 if (MDocUpdater.HasDroppedNamespace ()) {
584 // If dropping namespace, types may have moved into a couple of different places.
585 var newSearchLocations = searchLocations.Union (new string[] {
586 string.Format ("{0}.{1}", droppedNamespace, nsname),
587 nsname.Replace (droppedNamespace + ".", string.Empty),
588 MDocUpdater.droppedNamespace
591 searchLocations = newSearchLocations.ToArray ();
594 string reltypefile="", typefile="";
595 System.IO.FileInfo file = null;
597 foreach (var f in searchLocations) {
598 Tuple<string, string, FileInfo> result;
599 bool fileExists = TryFindTypeFile (f, typename, basepath, out result);
602 reltypefile = result.Item1;
603 typefile = result.Item2;
610 if (file == null || !file.Exists) {
611 // we were not able to find a file, let's use the original type informatio.
612 // so that we create the stub in the right place.
613 Tuple<string, string, FileInfo> result;
614 TryFindTypeFile (nsname, typename, basepath, out result);
616 reltypefile = result.Item1;
617 typefile = result.Item2;
621 string output = null;
624 } else if (dest == "-") {
627 output = Path.Combine (dest, reltypefile);
630 if (file != null && file.Exists) {
632 XmlDocument basefile = new XmlDocument();
634 basefile.Load(typefile);
635 } catch (Exception e) {
636 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
639 DoUpdateType2("Updating", basefile, type, output, false);
642 XmlElement td = StubType(type, output);
646 System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
649 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
655 public void DoUpdateNS (string ns, string nspath, string outpath)
657 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
658 AssemblyDefinition assembly = assemblies [0];
660 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
661 XmlDocument basefile = new XmlDocument();
662 string typefile = Path.Combine(nspath, file.Name);
664 basefile.Load(typefile);
665 } catch (Exception e) {
666 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
670 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
671 TypeDefinition type = assembly.GetType(typename);
674 if (!string.IsNullOrWhiteSpace (droppedNamespace)) {
675 string nameWithNs = string.Format ("{0}.{1}", droppedNamespace, typename);
676 type = assembly.GetType (nameWithNs);
678 Warning ("Type no longer in assembly: " + typename);
685 seenTypes[type] = seenTypes;
686 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
689 // Stub types not in the directory
690 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
691 if (type.Namespace != ns || seenTypes.ContainsKey(type))
694 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
695 if (td == null) continue;
699 private static string GetTypeFileName (TypeReference type)
701 return filenameFormatter.GetName (type);
704 public static string GetTypeFileName (string typename)
706 StringBuilder filename = new StringBuilder (typename.Length);
710 for (int i = 0; i < typename.Length; ++i) {
711 char c = typename [i];
720 filename.Append ('`').Append ((numArgs+1).ToString());
735 return filename.ToString ();
738 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
740 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
741 index_assembly.SetAttribute ("Name", assembly.Name.Name);
742 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
744 AssemblyNameDefinition name = assembly.Name;
745 if (name.HasPublicKey) {
746 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
747 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
749 foreach (byte b in name.PublicKey)
750 key.AppendFormat ("{0,2:x2} ", b);
752 pubkey.InnerText = key.ToString ();
753 index_assembly.AppendChild (pubkey);
756 if (!string.IsNullOrEmpty (name.Culture)) {
757 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
758 culture.InnerText = name.Culture;
759 index_assembly.AppendChild (culture);
762 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
763 parent.AppendChild(index_assembly);
766 private void AddIndexType (TypeDefinition type, XmlElement index_types)
768 string typename = GetTypeFileName(type);
770 // Add namespace and type nodes into the index file as needed
771 string ns = DocUtils.GetNamespace (type);
772 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
773 if (nsnode == null) {
774 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
775 nsnode.SetAttribute ("Name", ns);
776 index_types.AppendChild (nsnode);
778 string doc_typename = GetDocTypeName (type);
779 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
780 if (typenode == null) {
781 typenode = index_types.OwnerDocument.CreateElement ("Type");
782 typenode.SetAttribute ("Name", typename);
783 nsnode.AppendChild (typenode);
785 if (typename != doc_typename)
786 typenode.SetAttribute("DisplayName", doc_typename);
788 typenode.RemoveAttribute("DisplayName");
790 typenode.SetAttribute ("Kind", GetTypeKind (type));
793 private void DoUpdateAssemblies (string source, string dest)
795 string indexfile = dest + "/index.xml";
797 if (System.IO.File.Exists(indexfile)) {
798 index = new XmlDocument();
799 index.Load(indexfile);
802 ClearElement(index.DocumentElement, "Assembly");
803 ClearElement(index.DocumentElement, "Attributes");
805 index = CreateIndexStub();
808 string defaultTitle = "Untitled";
809 if (assemblies.Count == 1)
810 defaultTitle = assemblies[0].Name.Name;
811 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
813 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
814 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
815 index_assemblies.RemoveAll ();
818 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
820 foreach (AssemblyDefinition assm in assemblies) {
821 AddIndexAssembly (assm, index_assemblies);
822 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
825 SortIndexEntries (index_types);
827 CleanupFiles (dest, goodfiles);
828 CleanupIndexTypes (index_types, goodfiles);
829 CleanupExtensions (index_types);
831 WriteFile (indexfile, FileMode.Create,
832 writer => WriteXml(index.DocumentElement, writer));
835 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
837 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
839 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
840 string typename = GetTypeFileName(type);
841 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
844 string reltypepath = DoUpdateType (type, source, dest);
845 if (reltypepath == null)
848 // Add namespace and type nodes into the index file as needed
849 AddIndexType (type, index_types);
851 // Ensure the namespace index file exists
852 string namespaceToUse = type.Namespace;
853 if (HasDroppedNamespace()) {
854 namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
856 string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
857 string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
858 if (File.Exists (onsdoc)) {
859 File.Move (onsdoc, nsdoc);
862 if (!File.Exists (nsdoc)) {
863 Console.WriteLine("New Namespace File: " + type.Namespace);
864 WriteNamespaceStub(namespaceToUse, dest);
867 goodfiles.Add (reltypepath);
871 private static void SortIndexEntries (XmlElement indexTypes)
873 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
874 XmlNodeComparer c = new AttributeNameComparer ();
875 SortXmlNodes (indexTypes, namespaces, c);
877 for (int i = 0; i < namespaces.Count; ++i)
878 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
881 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
883 MyXmlNodeList l = new MyXmlNodeList (children.Count);
884 for (int i = 0; i < children.Count; ++i)
885 l.Add (children [i]);
887 for (int i = l.Count - 1; i > 0; --i) {
888 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
892 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
894 public abstract int Compare (XmlNode x, XmlNode y);
896 public int Compare (object x, object y)
898 return Compare ((XmlNode) x, (XmlNode) y);
902 class AttributeNameComparer : XmlNodeComparer {
905 public AttributeNameComparer ()
910 public AttributeNameComparer (string attribute)
912 this.attribute = attribute;
915 public override int Compare (XmlNode x, XmlNode y)
917 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
921 class VersionComparer : XmlNodeComparer {
922 public override int Compare (XmlNode x, XmlNode y)
924 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
925 string a = GetVersion (x.InnerText);
926 string b = GetVersion (y.InnerText);
927 return new Version (a).CompareTo (new Version (b));
930 static string GetVersion (string v)
932 int n = v.IndexOf ("x");
935 return v.Substring (0, n-1);
939 private static string GetTypeKind (TypeDefinition type)
942 return "Enumeration";
943 if (type.IsValueType)
945 if (type.IsInterface)
947 if (DocUtils.IsDelegate (type))
949 if (type.IsClass || type.FullName == "System.Enum") // FIXME
951 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
954 private static bool IsPublic (TypeDefinition type)
956 TypeDefinition decl = type;
957 while (decl != null) {
958 if (!(decl.IsPublic || decl.IsNestedPublic ||
959 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
962 decl = (TypeDefinition) decl.DeclaringType;
967 private void CleanupFiles (string dest, HashSet<string> goodfiles)
969 // Look for files that no longer correspond to types
970 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
971 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
972 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
973 if (!goodfiles.Contains (relTypeFile)) {
974 XmlDocument doc = new XmlDocument ();
975 doc.Load (typefile.FullName);
976 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
977 if (e != null && !no_assembly_versions && UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
978 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
979 WriteXml(doc.DocumentElement, writer);
980 goodfiles.Add (relTypeFile);
984 if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was no -preserve
985 string newname = typefile.FullName + ".remove";
986 try { System.IO.File.Delete(newname); } catch (Exception) { }
987 try { typefile.MoveTo(newname); } catch (Exception) { }
988 Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
995 private static TextWriter OpenWrite (string path, FileMode mode)
997 var w = new StreamWriter (
998 new FileStream (path, mode),
999 new UTF8Encoding (false)
1005 private string[] GetAssemblyVersions ()
1007 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
1010 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
1012 // Look for type nodes that no longer correspond to types
1013 MyXmlNodeList remove = new MyXmlNodeList ();
1014 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
1015 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
1016 if (!goodfiles.Contains (fulltypename)) {
1017 remove.Add (typenode);
1020 foreach (XmlNode n in remove)
1021 n.ParentNode.RemoveChild (n);
1024 private void CleanupExtensions (XmlElement index_types)
1026 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
1027 if (extensionMethods.Count == 0) {
1030 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
1034 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
1035 index_types.SelectSingleNode ("/Overview").AppendChild (e);
1039 extensionMethods.Sort (DefaultExtensionMethodComparer);
1040 foreach (XmlNode m in extensionMethods) {
1041 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
1045 class ExtensionMethodComparer : XmlNodeComparer {
1046 public override int Compare (XmlNode x, XmlNode y)
1048 XmlNode xLink = x.SelectSingleNode ("Member/Link");
1049 XmlNode yLink = y.SelectSingleNode ("Member/Link");
1051 int n = xLink.Attributes ["Type"].Value.CompareTo (
1052 yLink.Attributes ["Type"].Value);
1055 n = xLink.Attributes ["Member"].Value.CompareTo (
1056 yLink.Attributes ["Member"].Value);
1057 if (n == 0 && !object.ReferenceEquals (x, y))
1058 throw new InvalidOperationException ("Duplicate extension method found!");
1063 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
1065 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
1067 Console.WriteLine(message + ": " + type.FullName);
1069 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
1071 // Update type metadata
1072 UpdateType(basefile.DocumentElement, type);
1074 // Update existing members. Delete member nodes that no longer should be there,
1075 // and remember what members are already documented so we don't add them again.
1077 MyXmlNodeList todelete = new MyXmlNodeList ();
1079 var docNodes = docEnum.GetDocumentationMembers (basefile, type).ToArray();
1080 foreach (DocsNodeInfo info in docNodes) {
1081 XmlElement oldmember = info.Node;
1082 MemberReference oldmember2 = info.Member;
1083 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
1085 // Interface implementations and overrides are deleted from the docs
1086 // unless the overrides option is given.
1087 if (oldmember2 != null && sig == null)
1090 // Deleted (or signature changed)
1091 if (oldmember2 == null) {
1092 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
1095 DeleteMember ("Member Removed", output, oldmember, todelete);
1100 if (seenmembers.ContainsKey (sig)) {
1101 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
1102 // ignore, already seen
1104 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
1105 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
1107 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
1111 // Update signature information
1114 // get all apistyles of sig from info.Node
1115 var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
1116 .Where (x => x.GetAttribute ("Language") == "C#" && !seenmembers.ContainsKey(x.GetAttribute("Value")))
1117 .Select (x => x.GetAttribute ("Value"));
1119 foreach (var stylesig in styles) {
1120 seenmembers.Add (stylesig, oldmember);
1123 foreach (XmlElement oldmember in todelete)
1124 oldmember.ParentNode.RemoveChild (oldmember);
1127 if (!DocUtils.IsDelegate (type)) {
1128 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
1129 var typemembers = type.GetMembers()
1131 if (m is TypeDefinition) return false;
1132 string sig = memberFormatters [0].GetDeclaration (m);
1133 if (sig == null) return false;
1134 if (seenmembers.ContainsKey(sig)) return false;
1138 foreach (MemberReference m in typemembers) {
1139 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
1140 if (mm == null) continue;
1142 if (MDocUpdater.SwitchingToMagicTypes) {
1143 // this is a new style API that obviously doesn't exist in the old API. Let's mark
1144 // it with apistyle="new", so that it's not displayed for old style APIs
1145 mm.SetAttribute ("apistyle", "new");
1148 members.AppendChild( mm );
1150 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
1155 // Import code snippets from files
1156 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
1157 if (!(code is XmlElement)) continue;
1158 string file = ((XmlElement)code).GetAttribute("src");
1159 string lang = ((XmlElement)code).GetAttribute("lang");
1161 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1163 code.InnerText = src;
1167 if (insertSince && since != null) {
1168 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1169 docs.AppendChild (CreateSinceNode (basefile));
1173 XmlElement d = basefile.DocumentElement ["Docs"];
1174 XmlElement m = basefile.DocumentElement ["Members"];
1175 if (d != null && m != null)
1176 basefile.DocumentElement.InsertBefore (
1177 basefile.DocumentElement.RemoveChild (d), m);
1178 SortTypeMembers (m);
1182 WriteXml(basefile.DocumentElement, Console.Out);
1184 FileInfo file = new FileInfo (output);
1185 if (!file.Directory.Exists) {
1186 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1187 file.Directory.Create ();
1189 WriteFile (output, FileMode.Create,
1190 writer => WriteXml(basefile.DocumentElement, writer));
1194 private string GetCodeSource (string lang, string file)
1197 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1198 // Grab the specified region
1199 string region = "#region " + file.Substring (anchorStart + 4);
1200 file = file.Substring (0, anchorStart + 3);
1202 using (StreamReader reader = new StreamReader (file)) {
1204 StringBuilder src = new StringBuilder ();
1206 while ((line = reader.ReadLine ()) != null) {
1207 if (line.Trim() == region) {
1208 indent = line.IndexOf (region);
1211 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1216 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1219 return src.ToString ();
1221 } catch (Exception e) {
1222 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1223 file, region, show_exceptions ? e.ToString () : e.Message);
1228 using (StreamReader reader = new StreamReader (file))
1229 return reader.ReadToEnd ();
1230 } catch (Exception e) {
1231 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1236 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1238 string format = output != null
1239 ? "{0}: File='{1}'; Signature='{4}'"
1240 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1244 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1245 member.Attributes ["MemberName"].Value,
1246 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1247 if (!delete && MemberDocsHaveUserContent (member)) {
1248 Warning ("Member deletions must be enabled with the --delete option.");
1249 } else if (HasDroppedNamespace ()) {
1250 // if we're dropping the namespace, add the "old style"
1251 var existingAttribute = member.Attributes ["apistyle"];
1252 if (existingAttribute != null) {
1253 existingAttribute.Value = "old";
1255 // add the attribute and do not remove
1256 XmlAttribute apistyleAttr = member.OwnerDocument.CreateAttribute ("apistyle");
1258 apistyleAttr.Value = "old";
1260 member.Attributes.Append (apistyleAttr);
1262 } else if (!HasDroppedNamespace () && member.Attributes ["apistyle"] != null && member.Attributes ["apistyle"].Value == "new") {
1263 // do nothing if there's an apistyle=new attribute and we haven't dropped the namespace
1264 } else if (!string.IsNullOrWhiteSpace (PreserveTag)) {
1267 todelete.Add (member);
1272 class MemberComparer : XmlNodeComparer {
1273 public override int Compare (XmlNode x, XmlNode y)
1276 string xMemberName = x.Attributes ["MemberName"].Value;
1277 string yMemberName = y.Attributes ["MemberName"].Value;
1279 // generic methods *end* with '>'
1280 // it's possible for explicitly implemented generic interfaces to
1281 // contain <...> without being a generic method
1282 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1283 (r = xMemberName.CompareTo (yMemberName)) != 0)
1287 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1288 xMemberName = xMemberName.Substring (0, lt);
1289 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1290 yMemberName = yMemberName.Substring (0, lt);
1291 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1294 // if @MemberName matches, then it's either two different types of
1295 // members sharing the same name, e.g. field & property, or it's an
1296 // overloaded method.
1297 // for different type, sort based on MemberType value.
1298 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1299 y.SelectSingleNode ("MemberType").InnerText);
1303 // same type -- must be an overloaded method. Sort based on type
1304 // parameter count, then parameter count, then by the parameter
1306 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1307 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1308 if (xTypeParams.Count != yTypeParams.Count)
1309 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1310 for (int i = 0; i < xTypeParams.Count; ++i) {
1311 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1312 yTypeParams [i].Attributes ["Name"].Value);
1317 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1318 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1319 if (xParams.Count != yParams.Count)
1320 return xParams.Count <= yParams.Count ? -1 : 1;
1321 for (int i = 0; i < xParams.Count; ++i) {
1322 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1323 yParams [i].Attributes ["Type"].Value);
1327 // all parameters match, but return value might not match if it was
1328 // changed between one version and another.
1329 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1330 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1331 if (xReturn != null && yReturn != null) {
1332 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1341 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1343 private static void SortTypeMembers (XmlNode members)
1345 if (members == null)
1347 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1350 private static bool MemberDocsHaveUserContent (XmlNode e)
1352 e = (XmlElement)e.SelectSingleNode("Docs");
1353 if (e == null) return false;
1354 foreach (XmlElement d in e.SelectNodes("*"))
1355 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1360 // UPDATE HELPER FUNCTIONS
1362 // CREATE A STUB DOCUMENTATION FILE
1364 public XmlElement StubType (TypeDefinition type, string output)
1366 string typesig = typeFormatters [0].GetDeclaration (type);
1367 if (typesig == null) return null; // not publicly visible
1369 XmlDocument doc = new XmlDocument();
1370 XmlElement root = doc.CreateElement("Type");
1371 doc.AppendChild (root);
1373 DoUpdateType2 ("New Type", doc, type, output, true);
1378 private XmlElement CreateSinceNode (XmlDocument doc)
1380 XmlElement s = doc.CreateElement ("since");
1381 s.SetAttribute ("version", since);
1385 // STUBBING/UPDATING FUNCTIONS
1387 public void UpdateType (XmlElement root, TypeDefinition type)
1389 root.SetAttribute("Name", GetDocTypeName (type));
1390 root.SetAttribute("FullName", GetDocTypeFullName (type));
1392 foreach (MemberFormatter f in typeFormatters) {
1393 string element = "TypeSignature[@Language='" + f.Language + "']";
1394 string valueToUse = f.GetDeclaration (type);
1397 root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
1398 x => x.GetAttribute ("Value") == valueToUse,
1399 x => x.SetAttribute ("Value", valueToUse),
1401 var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
1402 var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
1407 string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace () ? "[@apistyle='new']" : "[not(@apistyle) or @apistyle='old']";
1410 root.SelectNodes("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement>().ToArray(),
1411 x => x.SelectSingleNode("AssemblyName").InnerText == type.Module.Assembly.Name.Name,
1412 x => WriteElementText(x, "AssemblyName", type.Module.Assembly.Name.Name),
1414 XmlElement ass = WriteElement(root, "AssemblyInfo", forceNewElement:true);
1416 if (MDocUpdater.HasDroppedNamespace ()) ass.SetAttribute ("apistyle", "new");
1418 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1419 if (!no_assembly_versions) {
1420 UpdateAssemblyVersions (root, type, true);
1423 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1424 foreach (var version in versions)
1425 ass.RemoveChild (version);
1427 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1428 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1430 ClearElement(ass, "AssemblyCulture");
1433 // Why-oh-why do we put assembly attributes in each type file?
1434 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1435 // since they're outdated in current docs, and a waste of space.
1436 //MakeAttributes(ass, type.Assembly, true);
1437 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1438 if (assattrs != null)
1439 ass.RemoveChild(assattrs);
1441 NormalizeWhitespace(ass);
1446 if (type.IsGenericType ()) {
1447 MakeTypeParameters (root, type.GenericParameters, MDocUpdater.HasDroppedNamespace());
1449 ClearElement(root, "TypeParameters");
1452 if (type.BaseType != null) {
1453 XmlElement basenode = WriteElement(root, "Base");
1455 string basetypename = GetDocTypeFullName (type.BaseType);
1456 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1457 WriteElementText(root, "Base/BaseTypeName", basetypename);
1459 // Document how this type instantiates the generic parameters of its base type
1460 TypeReference origBase = type.BaseType.GetElementType ();
1461 if (origBase.IsGenericType ()) {
1462 ClearElement(basenode, "BaseTypeArguments");
1463 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1464 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1465 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1466 if (baseGenArgs.Count != baseGenParams.Count)
1467 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1468 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1469 GenericParameter param = baseGenParams [i];
1470 TypeReference value = baseGenArgs [i];
1472 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1473 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1474 bta.AppendChild(arg);
1475 arg.SetAttribute ("TypeParamName", param.Name);
1476 arg.InnerText = GetDocTypeFullName (value);
1480 ClearElement(root, "Base");
1483 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1484 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1485 List<string> interface_names = userInterfaces
1486 .Select (iface => GetDocTypeFullName (iface))
1490 XmlElement interfaces = WriteElement(root, "Interfaces");
1491 interfaces.RemoveAll();
1492 foreach (string iname in interface_names) {
1493 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1494 interfaces.AppendChild(iface);
1495 WriteElementText(iface, "InterfaceName", iname);
1498 ClearElement(root, "Interfaces");
1501 MakeAttributes (root, GetCustomAttributes (type), type);
1503 if (DocUtils.IsDelegate (type)) {
1504 MakeTypeParameters (root, type.GenericParameters, MDocUpdater.HasDroppedNamespace());
1505 MakeParameters(root, type.GetMethod("Invoke").Parameters);
1506 MakeReturnValue(root, type.GetMethod("Invoke"));
1509 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1510 MakeDocNode (typeInfo);
1512 if (!DocUtils.IsDelegate (type))
1513 WriteElement (root, "Members");
1515 OrderTypeNodes (root, root.ChildNodes);
1516 NormalizeWhitespace(root);
1519 static readonly string[] TypeNodeOrder = {
1523 "ThreadingSafetyStatement",
1524 "ThreadSafetyStatement",
1536 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1538 ReorderNodes (member, children, TypeNodeOrder);
1541 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1543 List<T> l = new List<T> (list);
1548 private void UpdateMember (DocsNodeInfo info)
1550 XmlElement me = (XmlElement) info.Node;
1551 MemberReference mi = info.Member;
1553 foreach (MemberFormatter f in memberFormatters) {
1554 string element = "MemberSignature[@Language='" + f.Language + "']";
1556 var valueToUse = f.GetDeclaration (mi);
1559 me.SelectNodes (element).Cast<XmlElement> ().ToArray(),
1560 x => x.GetAttribute("Value") == valueToUse,
1561 x => x.SetAttribute ("Value", valueToUse),
1563 var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
1564 var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
1570 WriteElementText(me, "MemberType", GetMemberType(mi));
1572 if (!no_assembly_versions) {
1573 UpdateAssemblyVersions (me, mi, true);
1576 ClearElement (me, "AssemblyInfo");
1579 MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
1581 MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace());
1582 if (mi is MethodReference) {
1583 MethodReference mb = (MethodReference) mi;
1584 if (mb.IsGenericMethod ())
1585 MakeTypeParameters (me, mb.GenericParameters, MDocUpdater.HasDroppedNamespace());
1587 MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace());
1590 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1591 WriteElementText(me, "MemberValue", fieldValue);
1593 info.Node = WriteElement (me, "Docs");
1595 OrderMemberNodes (me, me.ChildNodes);
1596 UpdateExtensionMethods (me, info);
1599 /// <summary>Adds an xml node, reusing the node if it's available</summary>
1600 /// <param name="relevant">The existing set of nodes</param>
1601 /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
1602 /// <param name="setValue">Sets the node's value</param>
1603 /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
1604 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode)
1606 bool shouldDuplicate = MDocUpdater.HasDroppedNamespace ();
1607 var styleToUse = shouldDuplicate ? ApiStyle.New : ApiStyle.Old;
1608 var existing = relevant;
1610 bool addedOldApiStyle = false;
1612 if (shouldDuplicate) {
1613 existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
1614 foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse))) {
1615 if (valueMatches (n)) {
1619 n.AddApiStyle (ApiStyle.Old);
1620 addedOldApiStyle = true;
1625 if (existing.Count () == 0) {
1626 var newNode = makeNewNode ();
1627 if (shouldDuplicate && addedOldApiStyle) {
1628 newNode.AddApiStyle (ApiStyle.New);
1632 var itemToReuse = existing.First ();
1633 setValue (itemToReuse);
1635 if (shouldDuplicate && addedOldApiStyle) {
1636 itemToReuse.AddApiStyle (styleToUse);
1642 static readonly string[] MemberNodeOrder = {
1657 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1659 ReorderNodes (member, children, MemberNodeOrder);
1662 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1664 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1665 for (int i = 0; i < ordering.Length; ++i) {
1666 for (int j = 0; j < children.Count; ++j) {
1667 XmlNode c = children [j];
1668 if (c.Name == ordering [i]) {
1669 newChildren.Add (c);
1673 if (newChildren.Count >= 0)
1674 node.PrependChild ((XmlNode) newChildren [0]);
1675 for (int i = 1; i < newChildren.Count; ++i) {
1676 XmlNode prev = (XmlNode) newChildren [i-1];
1677 XmlNode cur = (XmlNode) newChildren [i];
1678 node.RemoveChild (cur);
1679 node.InsertAfter (cur, prev);
1683 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1685 IEnumerable<string> attrs = Enumerable.Empty<string>();
1687 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1689 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1691 PropertyDefinition pd = mi as PropertyDefinition;
1693 if (pd.GetMethod != null)
1694 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1695 if (pd.SetMethod != null)
1696 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1699 EventDefinition ed = mi as EventDefinition;
1701 if (ed.AddMethod != null)
1702 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1703 if (ed.RemoveMethod != null)
1704 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1710 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1712 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1714 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1715 if (attrType != null && !IsPublic (attrType))
1717 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1720 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1723 StringList fields = new StringList ();
1725 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1726 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1727 fields.Add (MakeAttributesValueString (
1732 (from namedArg in attribute.Fields
1733 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1735 (from namedArg in attribute.Properties
1736 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1737 .OrderBy (v => v.Name);
1738 foreach (var d in namedArgs)
1739 fields.Add (string.Format ("{0}={1}", d.Name,
1740 MakeAttributesValueString (d.Value, d.Type)));
1742 string a2 = String.Join(", ", fields.ToArray ());
1743 if (a2 != "") a2 = "(" + a2 + ")";
1745 string name = attribute.GetDeclaringType();
1746 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1747 yield return prefix + name + a2;
1751 static readonly string[] ValidExtensionMembers = {
1760 static readonly string[] ValidExtensionDocMembers = {
1766 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1768 MethodDefinition me = info.Member as MethodDefinition;
1771 if (info.Parameters.Count < 1)
1773 if (!DocUtils.IsExtensionMethod (me))
1776 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1777 XmlNode member = e.CloneNode (true);
1778 em.AppendChild (member);
1779 RemoveExcept (member, ValidExtensionMembers);
1780 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1781 WriteElementText (member, "MemberType", "ExtensionMethod");
1782 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1783 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1784 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1785 member.AppendChild (link);
1786 AddTargets (em, info);
1788 extensionMethods.Add (em);
1791 private static void RemoveExcept (XmlNode node, string[] except)
1795 MyXmlNodeList remove = null;
1796 foreach (XmlNode n in node.ChildNodes) {
1797 if (Array.BinarySearch (except, n.Name) < 0) {
1799 remove = new MyXmlNodeList ();
1804 foreach (XmlNode n in remove)
1805 node.RemoveChild (n);
1808 private static void AddTargets (XmlNode member, DocsNodeInfo info)
1810 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1811 member.PrependChild (targets);
1812 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1813 AppendElementAttributeText (targets, "Target", "Type",
1814 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1817 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1818 IList<TypeReference> constraints = gp.Constraints;
1819 if (constraints.Count == 0)
1820 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1822 foreach (TypeReference c in constraints)
1823 AppendElementAttributeText(targets, "Target", "Type",
1824 slashdocFormatter.GetDeclaration (c));
1828 private static bool GetFieldConstValue (FieldDefinition field, out string value)
1831 TypeDefinition type = field.DeclaringType.Resolve ();
1832 if (type != null && type.IsEnum) return false;
1834 if (type != null && type.IsGenericType ()) return false;
1835 if (!field.HasConstant)
1837 if (field.IsLiteral) {
1838 object val = field.Constant;
1839 if (val == null) value = "null";
1840 else if (val is Enum) value = val.ToString();
1841 else if (val is IFormattable) {
1842 value = ((IFormattable)val).ToString();
1844 value = "\"" + value + "\"";
1846 if (value != null && value != "")
1852 // XML HELPER FUNCTIONS
1854 internal static XmlElement WriteElement(XmlNode parent, string element, bool forceNewElement = false) {
1855 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1856 if (ret == null || forceNewElement) {
1857 string[] path = element.Split('/');
1858 foreach (string p in path) {
1859 ret = (XmlElement)parent.SelectSingleNode(p);
1860 if (ret == null || forceNewElement) {
1862 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1863 ename = ename.Substring(0, ename.IndexOf('['));
1864 ret = parent.OwnerDocument.CreateElement(ename);
1865 parent.AppendChild(ret);
1874 private static XmlElement WriteElementText(XmlNode parent, string element, string value, bool forceNewElement = false) {
1875 XmlElement node = WriteElement(parent, element, forceNewElement: forceNewElement);
1876 node.InnerText = value;
1880 static XmlElement AppendElementText (XmlNode parent, string element, string value)
1882 XmlElement n = parent.OwnerDocument.CreateElement (element);
1883 parent.AppendChild (n);
1884 n.InnerText = value;
1888 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1890 XmlElement n = parent.OwnerDocument.CreateElement (element);
1891 parent.AppendChild (n);
1892 n.SetAttribute (attribute, value);
1896 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
1898 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1899 dest.AppendChild (copy);
1903 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1904 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1907 node = WriteElement(parent, element);
1908 node.InnerText = value;
1910 private static XmlElement WriteElementAttribute(XmlElement parent, string element, string attribute, string value, bool forceNewElement = false) {
1911 XmlElement node = WriteElement(parent, element, forceNewElement:forceNewElement);
1912 return WriteElementAttribute (parent, node, attribute, value);
1914 private static XmlElement WriteElementAttribute(XmlElement parent, XmlElement node, string attribute, string value) {
1915 if (node.GetAttribute (attribute) != value) {
1916 node.SetAttribute (attribute, value);
1920 internal static void ClearElement(XmlElement parent, string name) {
1921 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1923 parent.RemoveChild(node);
1926 // DOCUMENTATION HELPER FUNCTIONS
1928 private void MakeDocNode (DocsNodeInfo info)
1930 List<GenericParameter> genericParams = info.GenericParameters;
1931 IList<ParameterDefinition> parameters = info.Parameters;
1932 TypeReference returntype = info.ReturnType;
1933 bool returnisreturn = info.ReturnIsReturn;
1934 XmlElement e = info.Node;
1935 bool addremarks = info.AddRemarks;
1937 WriteElementInitialText(e, "summary", "To be added.");
1939 if (parameters != null) {
1940 string[] values = new string [parameters.Count];
1941 for (int i = 0; i < values.Length; ++i)
1942 values [i] = parameters [i].Name;
1943 UpdateParameters (e, "param", values);
1946 if (genericParams != null) {
1947 string[] values = new string [genericParams.Count];
1948 for (int i = 0; i < values.Length; ++i)
1949 values [i] = genericParams [i].Name;
1950 UpdateParameters (e, "typeparam", values);
1953 string retnodename = null;
1954 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1955 retnodename = returnisreturn ? "returns" : "value";
1956 string retnodename_other = !returnisreturn ? "returns" : "value";
1958 // If it has a returns node instead of a value node, change its name.
1959 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1960 if (retother != null) {
1961 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1962 foreach (XmlNode node in retother)
1963 retnode.AppendChild(node.CloneNode(true));
1964 e.ReplaceChild(retnode, retother);
1966 WriteElementInitialText(e, retnodename, "To be added.");
1969 ClearElement(e, "returns");
1970 ClearElement(e, "value");
1974 WriteElementInitialText(e, "remarks", "To be added.");
1976 if (exceptions.HasValue && info.Member != null &&
1977 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1978 UpdateExceptions (e, info.Member);
1981 foreach (DocumentationImporter importer in importers)
1982 importer.ImportDocumentation (info);
1984 OrderDocsNodes (e, e.ChildNodes);
1985 NormalizeWhitespace(e);
1988 static readonly string[] DocsNodeOrder = {
1989 "typeparam", "param", "summary", "returns", "value", "remarks",
1992 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1994 ReorderNodes (docs, children, DocsNodeOrder);
1998 private void UpdateParameters (XmlElement e, string element, string[] values)
2000 if (values != null) {
2001 XmlNode[] paramnodes = new XmlNode[values.Length];
2003 // Some documentation had param nodes with leading spaces.
2004 foreach (XmlElement paramnode in e.SelectNodes(element)){
2005 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2008 // If a member has only one parameter, we can track changes to
2009 // the name of the parameter easily.
2010 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2011 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2014 bool reinsert = false;
2016 // Pick out existing and still-valid param nodes, and
2017 // create nodes for parameters not in the file.
2018 Hashtable seenParams = new Hashtable();
2019 for (int pi = 0; pi < values.Length; pi++) {
2020 string p = values [pi];
2023 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2024 if (paramnodes[pi] != null) continue;
2026 XmlElement pe = e.OwnerDocument.CreateElement(element);
2027 pe.SetAttribute("name", p);
2028 pe.InnerText = "To be added.";
2029 paramnodes[pi] = pe;
2033 // Remove parameters that no longer exist and check all params are in the right order.
2035 MyXmlNodeList todelete = new MyXmlNodeList ();
2036 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2037 string name = paramnode.GetAttribute("name");
2038 if (!seenParams.ContainsKey(name)) {
2039 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2040 Warning ("The following param node can only be deleted if the --delete option is given: ");
2041 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2043 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2044 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2048 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2049 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2050 e.ParentNode.Attributes ["MemberName"].Value,
2053 Warning ("\tValue={0}", paramnode.OuterXml);
2055 todelete.Add (paramnode);
2060 if ((int)seenParams[name] != idx)
2066 foreach (XmlNode n in todelete) {
2067 n.ParentNode.RemoveChild (n);
2070 // Re-insert the parameter nodes at the top of the doc section.
2072 for (int pi = values.Length-1; pi >= 0; pi--)
2073 e.PrependChild(paramnodes[pi]);
2075 // Clear all existing param nodes
2076 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2077 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2078 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2079 Console.WriteLine(paramnode.OuterXml);
2081 paramnode.ParentNode.RemoveChild(paramnode);
2087 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2089 string existingName = pe.GetAttribute ("name");
2090 pe.SetAttribute ("name", newName);
2091 if (existingName == newName)
2093 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2094 if (paramref.GetAttribute ("name").Trim () == existingName)
2095 paramref.SetAttribute ("name", newName);
2098 class CrefComparer : XmlNodeComparer {
2100 public CrefComparer ()
2104 public override int Compare (XmlNode x, XmlNode y)
2106 string xType = x.Attributes ["cref"].Value;
2107 string yType = y.Attributes ["cref"].Value;
2108 string xNamespace = GetNamespace (xType);
2109 string yNamespace = GetNamespace (yType);
2111 int c = xNamespace.CompareTo (yNamespace);
2114 return xType.CompareTo (yType);
2117 static string GetNamespace (string type)
2119 int n = type.LastIndexOf ('.');
2121 return type.Substring (0, n);
2122 return string.Empty;
2126 private void UpdateExceptions (XmlNode docs, MemberReference member)
2128 string indent = new string (' ', 10);
2129 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2130 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2131 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2134 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2135 e.SetAttribute ("cref", cref);
2136 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
2137 string.Join ("\" />,\n" + indent + "<see cref=\"",
2138 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2139 .OrderBy (s => s)) +
2141 docs.AppendChild (e);
2143 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2144 new CrefComparer ());
2147 private static void NormalizeWhitespace(XmlElement e) {
2148 // Remove all text and whitespace nodes from the element so it
2149 // is outputted with nice indentation and no blank lines.
2150 ArrayList deleteNodes = new ArrayList();
2151 foreach (XmlNode n in e)
2152 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2154 foreach (XmlNode n in deleteNodes)
2155 n.ParentNode.RemoveChild(n);
2158 private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
2160 TypeDefinition type = member as TypeDefinition;
2162 type = member.DeclaringType as TypeDefinition;
2163 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
2166 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2168 return assembly.Name.Version.ToString();
2171 private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
2173 XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
2175 // AssemblyVersions is not part of the spec
2176 root.RemoveChild (av);
2179 string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='old']";
2180 string newNodeFilter = "AssemblyInfo[@apistyle='new']";
2181 string thisNodeFilter = MDocUpdater.HasDroppedNamespace () ? newNodeFilter : oldNodeFilter;
2182 string thatNodeFilter = MDocUpdater.HasDroppedNamespace () ? oldNodeFilter : newNodeFilter;
2184 XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
2186 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2188 if (MDocUpdater.HasDroppedNamespace ()) {
2189 e.SetAttribute ("apistyle", "new");
2192 root.AppendChild(e);
2195 var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
2196 if (MDocUpdater.HasDroppedNamespace () && thatNode != null) {
2197 // there's an old node, we should add apistyles
2198 e.SetAttribute ("apistyle", "new");
2199 thatNode.SetAttribute ("apistyle", "old");
2202 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
2203 .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
2205 // matches.Count > 0 && add: ignore -- already present
2206 if (matches.Count > 0 && !add) {
2207 foreach (XmlNode c in matches)
2210 else if (matches.Count == 0 && add) {
2211 foreach (string sv in assemblyVersions) {
2212 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2217 // matches.Count == 0 && !add: ignore -- already not present
2219 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2220 SortXmlNodes (e, avs, new VersionComparer ());
2222 bool anyNodesLeft = avs.Count != 0;
2223 if (!anyNodesLeft) {
2224 e.ParentNode.RemoveChild (e);
2226 return anyNodesLeft;
2229 // FIXME: get TypeReferences instead of string comparison?
2230 private static string[] IgnorableAttributes = {
2231 // Security related attributes
2232 "System.Reflection.AssemblyKeyFileAttribute",
2233 "System.Reflection.AssemblyDelaySignAttribute",
2234 // Present in @RefType
2235 "System.Runtime.InteropServices.OutAttribute",
2236 // For naming the indexer to use when not using indexers
2237 "System.Reflection.DefaultMemberAttribute",
2238 // for decimal constants
2239 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2240 // compiler generated code
2241 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2242 // more compiler generated code, e.g. iterator methods
2243 "System.Diagnostics.DebuggerHiddenAttribute",
2244 "System.Runtime.CompilerServices.FixedBufferAttribute",
2245 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2246 // extension methods
2247 "System.Runtime.CompilerServices.ExtensionAttribute",
2248 // Used to differentiate 'object' from C#4 'dynamic'
2249 "System.Runtime.CompilerServices.DynamicAttribute",
2252 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t=null)
2254 if (!attributes.Any ()) {
2255 ClearElement (root, "Attributes");
2259 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2263 e = root.OwnerDocument.CreateElement("Attributes");
2265 foreach (string attribute in attributes) {
2266 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2269 WriteElementText(ae, "AttributeName", attribute);
2272 if (e.ParentNode == null)
2273 root.AppendChild(e);
2275 NormalizeWhitespace(e);
2278 public static string MakeAttributesValueString (object v, TypeReference valueType)
2282 if (valueType.FullName == "System.Type") {
2283 var vTypeRef = v as TypeReference;
2284 if (vTypeRef != null)
2285 return "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
2287 return "typeof(" + v.ToString () + ")";
2289 if (valueType.FullName == "System.String")
2290 return "\"" + v.ToString () + "\"";
2291 if (valueType.FullName == "System.Char")
2292 return "'" + v.ToString () + "'";
2294 return (bool)v ? "true" : "false";
2295 TypeDefinition valueDef = valueType.Resolve ();
2296 if (valueDef == null || !valueDef.IsEnum)
2297 return v.ToString ();
2298 string typename = GetDocTypeFullName (valueType);
2299 var values = GetEnumerationValues (valueDef);
2300 long c = ToInt64 (v);
2301 if (values.ContainsKey (c))
2302 return typename + "." + values [c];
2303 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
2304 return string.Join (" | ",
2305 (from i in values.Keys
2307 select typename + "." + values [i])
2308 .DefaultIfEmpty (v.ToString ()).ToArray ());
2310 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
2313 private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
2315 var values = new Dictionary<long, string> ();
2317 (from f in type.Fields
2318 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2320 values [ToInt64 (f.Constant)] = f.Name;
2325 static long ToInt64 (object value)
2328 return (long) (ulong) value;
2329 return Convert.ToInt64 (value);
2332 private void MakeParameters (XmlElement root, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
2334 XmlElement e = WriteElement(root, "Parameters");
2337 foreach (ParameterDefinition p in parameters) {
2341 var ptype = GetDocParameterType (p.ParameterType);
2342 var newPType = ptype;
2344 if (MDocUpdater.SwitchingToMagicTypes) {
2345 newPType = NativeTypeManager.ConvertFromNativeType (ptype);
2348 // now find the existing node, if it's there so we can reuse it.
2349 var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2350 .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
2353 if (nodes.Count () == 0) {
2354 // wasn't found, let's make sure it wasn't just cause the param name was changed
2355 nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2356 .Cast<XmlElement> ()
2357 .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
2358 .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
2359 .Take(1) // there might be more than one that meets this parameter ... only take the first.
2364 x => x.GetAttribute ("Type") == ptype,
2365 x => x.SetAttribute ("Type", ptype),
2367 pe = root.OwnerDocument.CreateElement ("Parameter");
2370 pe.SetAttribute ("Name", p.Name);
2371 pe.SetAttribute ("Type", ptype);
2372 if (p.ParameterType is ByReferenceType) {
2374 pe.SetAttribute ("RefType", "out");
2376 pe.SetAttribute ("RefType", "ref");
2379 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2387 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, bool shouldDuplicateWithNew)
2389 if (typeParams == null || typeParams.Count == 0) {
2390 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2392 root.RemoveChild (f);
2395 XmlElement e = WriteElement(root, "TypeParameters");
2397 var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
2399 foreach (GenericParameter t in typeParams) {
2401 IList<TypeReference> constraints = t.Constraints;
2402 GenericParameterAttributes attrs = t.Attributes;
2408 var baseType = e.SelectSingleNode("BaseTypeName");
2409 // TODO: should this comparison take into account BaseTypeName?
2410 return x.GetAttribute("Name") == t.Name;
2412 x => {}, // no additional action required
2415 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2417 pe.SetAttribute("Name", t.Name);
2418 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
2419 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2420 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2428 ce = root.OwnerDocument.CreateElement ("Constraints");
2430 pe.AppendChild (ce);
2431 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2432 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2433 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2434 AppendElementText (ce, "ParameterAttribute", "Covariant");
2435 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2436 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2437 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2438 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2439 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2440 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2441 foreach (TypeReference c in constraints) {
2442 TypeDefinition cd = c.Resolve ();
2443 AppendElementText (ce,
2444 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2445 GetDocTypeFullName (c));
2453 private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
2455 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2456 MakeParameters (root, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
2457 else if (mi is MethodDefinition) {
2458 MethodDefinition mb = (MethodDefinition) mi;
2459 IList<ParameterDefinition> parameters = mb.Parameters;
2460 MakeParameters(root, parameters, shouldDuplicateWithNew);
2461 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2462 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2463 p.SetAttribute ("RefType", "this");
2466 else if (mi is PropertyDefinition) {
2467 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2468 if (parameters.Count > 0)
2469 MakeParameters(root, parameters, shouldDuplicateWithNew);
2473 else if (mi is FieldDefinition) return;
2474 else if (mi is EventDefinition) return;
2475 else throw new ArgumentException();
2478 internal static string GetDocParameterType (TypeReference type)
2480 return GetDocTypeFullName (type).Replace ("@", "&");
2483 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew=false)
2485 XmlElement e = WriteElement(root, "ReturnValue");
2486 var valueToUse = GetDocTypeFullName (type);
2488 AddXmlNode (e.SelectNodes("ReturnType").Cast<XmlElement> ().ToArray (),
2489 x => x.InnerText == valueToUse,
2490 x => x.InnerText = valueToUse,
2492 var newNode = WriteElementText(e, "ReturnType", valueToUse, forceNewElement: true);
2493 if (attributes != null)
2494 MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
2500 private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
2502 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2504 else if (mi is MethodDefinition)
2505 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
2506 else if (mi is PropertyDefinition)
2507 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
2508 else if (mi is FieldDefinition)
2509 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
2510 else if (mi is EventDefinition)
2511 MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
2513 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2516 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2518 MemberReference mi = info.Member;
2519 if (mi is TypeDefinition) return null;
2521 string sigs = memberFormatters [0].GetDeclaration (mi);
2522 if (sigs == null) return null; // not publicly visible
2524 // no documentation for property/event accessors. Is there a better way of doing this?
2525 if (mi.Name.StartsWith("get_")) return null;
2526 if (mi.Name.StartsWith("set_")) return null;
2527 if (mi.Name.StartsWith("add_")) return null;
2528 if (mi.Name.StartsWith("remove_")) return null;
2529 if (mi.Name.StartsWith("raise_")) return null;
2531 XmlElement me = doc.CreateElement("Member");
2532 me.SetAttribute("MemberName", GetMemberName (mi));
2536 if (exceptions.HasValue &&
2537 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2538 UpdateExceptions (info.Node, info.Member);
2540 if (since != null) {
2541 XmlNode docs = me.SelectSingleNode("Docs");
2542 docs.AppendChild (CreateSinceNode (doc));
2548 internal static string GetMemberName (MemberReference mi)
2550 MethodDefinition mb = mi as MethodDefinition;
2552 PropertyDefinition pi = mi as PropertyDefinition;
2555 return DocUtils.GetPropertyName (pi);
2557 StringBuilder sb = new StringBuilder (mi.Name.Length);
2558 if (!DocUtils.IsExplicitlyImplemented (mb))
2559 sb.Append (mi.Name);
2561 TypeReference iface;
2562 MethodReference ifaceMethod;
2563 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2564 sb.Append (GetDocTypeFullName (iface));
2566 sb.Append (ifaceMethod.Name);
2568 if (mb.IsGenericMethod ()) {
2569 IList<GenericParameter> typeParams = mb.GenericParameters;
2570 if (typeParams.Count > 0) {
2572 sb.Append (typeParams [0].Name);
2573 for (int i = 1; i < typeParams.Count; ++i)
2574 sb.Append (",").Append (typeParams [i].Name);
2578 return sb.ToString ();
2581 /// SIGNATURE GENERATION FUNCTIONS
2582 internal static bool IsPrivate (MemberReference mi)
2584 return memberFormatters [0].GetDeclaration (mi) == null;
2587 internal static string GetMemberType (MemberReference mi)
2589 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2590 return "Constructor";
2591 if (mi is MethodDefinition)
2593 if (mi is PropertyDefinition)
2595 if (mi is FieldDefinition)
2597 if (mi is EventDefinition)
2599 throw new ArgumentException();
2602 private static string GetDocTypeName (TypeReference type)
2604 return docTypeFormatter.GetName (type);
2607 internal static string GetDocTypeFullName (TypeReference type)
2609 return DocTypeFullMemberFormatter.Default.GetName (type);
2612 internal static string GetXPathForMember (DocumentationMember member)
2614 StringBuilder xpath = new StringBuilder ();
2615 xpath.Append ("//Members/Member[@MemberName=\"")
2616 .Append (member.MemberName)
2618 if (member.Parameters != null && member.Parameters.Count > 0) {
2619 xpath.Append ("/Parameters[count(Parameter) = ")
2620 .Append (member.Parameters.Count);
2621 for (int i = 0; i < member.Parameters.Count; ++i) {
2622 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2623 xpath.Append (member.Parameters [i]);
2624 xpath.Append ("\"");
2626 xpath.Append ("]/..");
2628 return xpath.ToString ();
2631 public static string GetXPathForMember (XPathNavigator member)
2633 StringBuilder xpath = new StringBuilder ();
2634 xpath.Append ("//Type[@FullName=\"")
2635 .Append (member.SelectSingleNode ("../../@FullName").Value)
2637 xpath.Append ("Members/Member[@MemberName=\"")
2638 .Append (member.SelectSingleNode ("@MemberName").Value)
2640 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2641 if (parameters.Count > 0) {
2642 xpath.Append ("/Parameters[count(Parameter) = ")
2643 .Append (parameters.Count);
2645 while (parameters.MoveNext ()) {
2647 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2648 xpath.Append (parameters.Current.Value);
2649 xpath.Append ("\"");
2651 xpath.Append ("]/..");
2653 return xpath.ToString ();
2656 public static string GetXPathForMember (MemberReference member)
2658 StringBuilder xpath = new StringBuilder ();
2659 xpath.Append ("//Type[@FullName=\"")
2660 .Append (member.DeclaringType.FullName)
2662 xpath.Append ("Members/Member[@MemberName=\"")
2663 .Append (GetMemberName (member))
2666 IList<ParameterDefinition> parameters = null;
2667 if (member is MethodDefinition)
2668 parameters = ((MethodDefinition) member).Parameters;
2669 else if (member is PropertyDefinition) {
2670 parameters = ((PropertyDefinition) member).Parameters;
2672 if (parameters != null && parameters.Count > 0) {
2673 xpath.Append ("/Parameters[count(Parameter) = ")
2674 .Append (parameters.Count);
2675 for (int i = 0; i < parameters.Count; ++i) {
2676 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2677 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2678 xpath.Append ("\"");
2680 xpath.Append ("]/..");
2682 return xpath.ToString ();
2686 static class CecilExtensions {
2687 public static string GetDeclaringType(this CustomAttribute attribute)
2689 var type = attribute.Constructor.DeclaringType;
2690 var typeName = type.FullName;
2692 string translatedType = NativeTypeManager.GetTranslatedName (type);
2693 return translatedType;
2696 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2698 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2699 yield return (MemberReference) c;
2700 foreach (var e in type.Events)
2701 yield return (MemberReference) e;
2702 foreach (var f in type.Fields)
2703 yield return (MemberReference) f;
2704 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2705 yield return (MemberReference) m;
2706 foreach (var t in type.NestedTypes)
2707 yield return (MemberReference) t;
2708 foreach (var p in type.Properties)
2709 yield return (MemberReference) p;
2712 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2714 return GetMembers (type).Where (m => m.Name == member);
2717 public static MemberReference GetMember (this TypeDefinition type, string member)
2719 return GetMembers (type, member).EnsureZeroOrOne ();
2722 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2724 if (source.Count () > 1)
2725 throw new InvalidOperationException ("too many matches");
2726 return source.FirstOrDefault ();
2729 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2732 .Where (m => m.Name == method)
2733 .EnsureZeroOrOne ();
2736 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2738 TypeDefinition def = type as TypeDefinition;
2740 return new MemberReference [0];
2741 CustomAttribute defMemberAttr = def.CustomAttributes
2742 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2743 if (defMemberAttr == null)
2744 return new MemberReference [0];
2745 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2746 return def.Properties
2747 .Where (p => p.Name == name)
2748 .Select (p => (MemberReference) p);
2751 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2753 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2756 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2758 return GetTypes (assembly)
2759 .Where (td => td.FullName == type)
2760 .EnsureZeroOrOne ();
2763 public static bool IsGenericType (this TypeReference type)
2765 return type.GenericParameters.Count > 0;
2768 public static bool IsGenericMethod (this MethodReference method)
2770 return method.GenericParameters.Count > 0;
2773 public static MemberReference Resolve (this MemberReference member)
2775 FieldReference fr = member as FieldReference;
2777 return fr.Resolve ();
2778 MethodReference mr = member as MethodReference;
2780 return mr.Resolve ();
2781 TypeReference tr = member as TypeReference;
2783 return tr.Resolve ();
2784 PropertyReference pr = member as PropertyReference;
2787 EventReference er = member as EventReference;
2790 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2793 public static TypeReference GetUnderlyingType (this TypeDefinition type)
2797 return type.Fields.First (f => f.Name == "value__").FieldType;
2800 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2802 return self.Types.SelectMany (t => t.GetAllTypes ());
2805 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2809 if (!self.HasNestedTypes)
2812 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2822 static class DocUtils {
2824 public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
2825 string styleString = style.ToString ().ToLower ();
2826 string apistylevalue = element.GetAttribute ("apistyle");
2827 return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
2829 public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
2830 string styleString = style.ToString ().ToLower ();
2831 return element.GetAttribute ("apistyle") == styleString;
2833 public static void AddApiStyle(this XmlElement element, ApiStyle style) {
2834 string styleString = style.ToString ().ToLower ();
2835 var existingValue = element.GetAttribute ("apistyle");
2836 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
2837 element.SetAttribute ("apistyle", styleString);
2841 public static bool IsExplicitlyImplemented (MethodDefinition method)
2843 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2846 public static string GetTypeDotMember (string name)
2848 int startType, startMethod;
2849 startType = startMethod = -1;
2850 for (int i = 0; i < name.Length; ++i) {
2851 if (name [i] == '.') {
2852 startType = startMethod;
2856 return name.Substring (startType+1);
2859 public static string GetMember (string name)
2861 int i = name.LastIndexOf ('.');
2864 return name.Substring (i+1);
2867 public static void GetInfoForExplicitlyImplementedMethod (
2868 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2872 if (method.Overrides.Count != 1)
2873 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2874 iface = method.Overrides [0].DeclaringType;
2875 ifaceMethod = method.Overrides [0];
2878 public static string GetPropertyName (PropertyDefinition pi)
2880 // Issue: (g)mcs-generated assemblies that explicitly implement
2881 // properties don't specify the full namespace, just the
2882 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2883 MethodDefinition method = pi.GetMethod;
2885 method = pi.SetMethod;
2886 if (!IsExplicitlyImplemented (method))
2889 // Need to determine appropriate namespace for this member.
2890 TypeReference iface;
2891 MethodReference ifaceMethod;
2892 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2893 return string.Join (".", new string[]{
2894 DocTypeFullMemberFormatter.Default.GetName (iface),
2895 GetMember (pi.Name)});
2898 public static string GetNamespace (TypeReference type)
2900 if (type.GetElementType ().IsNested)
2901 type = type.GetElementType ();
2902 while (type != null && type.IsNested)
2903 type = type.DeclaringType;
2905 return string.Empty;
2907 string typeNS = type.Namespace;
2909 // first, make sure this isn't a type reference to another assembly/module
2911 bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
2912 if (isInAssembly && MDocUpdater.HasDroppedNamespace () && !typeNS.StartsWith ("System")) {
2913 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
2918 public static string PathCombine (string dir, string path)
2924 return Path.Combine (dir, path);
2927 public static bool IsExtensionMethod (MethodDefinition method)
2930 method.CustomAttributes
2931 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2932 && method.DeclaringType.CustomAttributes
2933 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
2936 public static bool IsDelegate (TypeDefinition type)
2938 TypeReference baseRef = type.BaseType;
2939 if (baseRef == null)
2941 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2942 baseRef.FullName == "System.MulticastDelegate";
2945 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2947 List<TypeReference> decls = new List<TypeReference> ();
2949 while (type.DeclaringType != null) {
2950 decls.Add (type.DeclaringType);
2951 type = type.DeclaringType;
2957 public static int GetGenericArgumentCount (TypeReference type)
2959 GenericInstanceType inst = type as GenericInstanceType;
2961 ? inst.GenericArguments.Count
2962 : type.GenericParameters.Count;
2965 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2967 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2968 List<TypeReference> userInterfaces = new List<TypeReference> ();
2969 foreach (TypeReference iface in type.Interfaces) {
2970 TypeReference lookup = iface.Resolve () ?? iface;
2971 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2972 userInterfaces.Add (iface);
2974 return userInterfaces;
2977 private static string GetQualifiedTypeName (TypeReference type)
2979 return "[" + type.Scope.Name + "]" + type.FullName;
2982 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2984 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2985 Action<TypeDefinition> a = null;
2987 if (t == null) return;
2988 foreach (TypeReference r in t.Interfaces) {
2989 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2993 TypeReference baseRef = type.BaseType;
2994 while (baseRef != null) {
2995 TypeDefinition baseDef = baseRef.Resolve ();
2996 if (baseDef != null) {
2998 baseRef = baseDef.BaseType;
3003 foreach (TypeReference r in type.Interfaces)
3005 return inheritedInterfaces;
3009 class DocsNodeInfo {
3010 public DocsNodeInfo (XmlElement node)
3015 public DocsNodeInfo (XmlElement node, TypeDefinition type)
3021 public DocsNodeInfo (XmlElement node, MemberReference member)
3024 SetMemberInfo (member);
3027 void SetType (TypeDefinition type)
3030 throw new ArgumentNullException ("type");
3032 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3033 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3034 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3035 for (int i = 0; i < declTypes.Count - 1; ++i) {
3036 int remove = System.Math.Min (maxGenArgs,
3037 DocUtils.GetGenericArgumentCount (declTypes [i]));
3038 maxGenArgs -= remove;
3039 while (remove-- > 0)
3040 GenericParameters.RemoveAt (0);
3042 if (DocUtils.IsDelegate (type)) {
3043 Parameters = type.GetMethod("Invoke").Parameters;
3044 ReturnType = type.GetMethod("Invoke").ReturnType;
3045 ReturnIsReturn = true;
3049 void SetMemberInfo (MemberReference member)
3052 throw new ArgumentNullException ("member");
3053 ReturnIsReturn = true;
3057 if (member is MethodReference ) {
3058 MethodReference mr = (MethodReference) member;
3059 Parameters = mr.Parameters;
3060 if (mr.IsGenericMethod ()) {
3061 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3064 else if (member is PropertyDefinition) {
3065 Parameters = ((PropertyDefinition) member).Parameters;
3068 if (member is MethodDefinition) {
3069 ReturnType = ((MethodDefinition) member).ReturnType;
3070 } else if (member is PropertyDefinition) {
3071 ReturnType = ((PropertyDefinition) member).PropertyType;
3072 ReturnIsReturn = false;
3075 // no remarks section for enum members
3076 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3080 public TypeReference ReturnType;
3081 public List<GenericParameter> GenericParameters;
3082 public IList<ParameterDefinition> Parameters;
3083 public bool ReturnIsReturn;
3084 public XmlElement Node;
3085 public bool AddRemarks = true;
3086 public MemberReference Member;
3087 public TypeDefinition Type;
3089 public override string ToString ()
3091 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3095 class DocumentationEnumerator {
3097 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3099 return GetDocumentationTypes (assembly, forTypes, null);
3102 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3104 foreach (TypeDefinition type in assembly.GetTypes()) {
3105 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3107 if (seen != null && seen.Contains (type.FullName))
3110 foreach (TypeDefinition nested in type.NestedTypes)
3111 yield return nested;
3115 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3117 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3118 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3119 oldmember.RemoveAttribute ("__monodocer-seen__");
3122 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3124 yield return new DocsNodeInfo (oldmember);
3127 yield return new DocsNodeInfo (oldmember, m);
3132 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3134 string membertype = member.MemberType;
3136 string returntype = member.ReturnType;
3138 string docName = member.MemberName;
3140 string[] docTypeParams = GetTypeParameters (docName);
3142 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3143 MemberReference likelyCandidate = null;
3145 // Loop through all members in this type with the same name
3146 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3147 foreach (MemberReference mi in reflectedMembers) {
3148 bool matchedMagicType = false;
3149 if (mi is TypeDefinition) continue;
3150 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3152 if (MDocUpdater.IsPrivate (mi))
3155 IList<ParameterDefinition> pis = null;
3156 string[] typeParams = null;
3157 if (mi is MethodDefinition) {
3158 MethodDefinition mb = (MethodDefinition) mi;
3159 pis = mb.Parameters;
3160 if (docTypeParams != null && mb.IsGenericMethod ()) {
3161 IList<GenericParameter> args = mb.GenericParameters;
3162 if (args.Count == docTypeParams.Length) {
3163 typeParams = args.Select (p => p.Name).ToArray ();
3167 else if (mi is PropertyDefinition)
3168 pis = ((PropertyDefinition)mi).Parameters;
3170 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3171 int pcount = pis == null ? 0 : pis.Count;
3172 if (mcount != pcount)
3175 MethodDefinition mDef = mi as MethodDefinition;
3176 if (mDef != null && !mDef.IsConstructor) {
3177 // Casting operators can overload based on return type.
3178 string rtype = GetReplacedString (
3179 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3180 typeParams, docTypeParams);
3181 string originalRType = rtype;
3182 if (MDocUpdater.SwitchingToMagicTypes) {
3183 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3186 if ((returntype != rtype && originalRType == rtype) ||
3187 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3191 if (originalRType != rtype)
3192 matchedMagicType = true;
3198 for (int i = 0; i < pis.Count; i++) {
3199 string paramType = GetReplacedString (
3200 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3201 typeParams, docTypeParams);
3203 // if magictypes, replace paramType to "old value" ... so the comparison works
3204 string originalParamType = paramType;
3205 if (MDocUpdater.SwitchingToMagicTypes) {
3206 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3209 string xmlMemberType = member.Parameters [i];
3210 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3211 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3213 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3214 // namespace, we should see if it matches when added
3215 bool stillDoesntMatch = true;
3216 if (MDocUpdater.HasDroppedNamespace() && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3217 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3219 stillDoesntMatch = withDroppedNs != paramType;
3222 if (stillDoesntMatch) {
3228 if (originalParamType != paramType)
3229 matchedMagicType = true;
3231 if (!good) continue;
3233 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3234 // 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
3235 likelyCandidate = mi;
3242 return likelyCandidate;
3245 static string[] GetTypeParameters (string docName)
3247 if (docName [docName.Length-1] != '>')
3249 StringList types = new StringList ();
3250 int endToken = docName.Length-2;
3251 int i = docName.Length-2;
3253 if (docName [i] == ',' || docName [i] == '<') {
3254 types.Add (docName.Substring (i + 1, endToken - i));
3257 if (docName [i] == '<')
3262 return types.ToArray ();
3265 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3267 // In case of dropping the namespace, we have to remove the dropped NS
3268 // so that docName will match what's in the assembly/type
3269 if (MDocUpdater.HasDroppedNamespace () && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3270 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3271 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3274 // need to worry about 4 forms of //@MemberName values:
3275 // 1. "Normal" (non-generic) member names: GetEnumerator
3277 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3278 // - try as-is, and try type.member (due to "kludge" for property
3280 // 3. "Normal" Generic member names: Sort<T> (CSC)
3281 // - need to remove generic parameters --> "Sort"
3282 // 4. Explicitly-implemented interface members for generic interfaces:
3283 // -- System.Collections.Generic.IEnumerable<T>.Current
3284 // - Try as-is, and try type.member, *keeping* the generic parameters.
3285 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3286 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3287 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3288 // this as (1) or (2).
3289 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3291 foreach (MemberReference mi in type.GetMembers (docName))
3293 if (CountChars (docName, '.') > 0)
3294 // might be a property; try only type.member instead of
3295 // namespace.type.member.
3296 foreach (MemberReference mi in
3297 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3304 int startLt, startType, startMethod;
3305 startLt = startType = startMethod = -1;
3306 for (int i = 0; i < docName.Length; ++i) {
3307 switch (docName [i]) {
3316 if (numLt == 0 && (i + 1) < docName.Length)
3317 // there's another character in docName, so this <...> sequence is
3318 // probably part of a generic type -- case 4.
3322 startType = startMethod;
3328 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3330 foreach (MemberReference mi in type.GetMembers (refName))
3334 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3337 // If we _still_ haven't found it, we've hit another generic naming issue:
3338 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3339 // explicitly-implemented METHOD names (not properties), e.g.
3340 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3341 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3342 // which the XML docs will contain.
3344 // Alas, we can't derive the Mono name from docName, so we need to iterate
3345 // over all member names, convert them into CSC format, and compare... :-(
3348 foreach (MemberReference mi in type.GetMembers ()) {
3349 if (MDocUpdater.GetMemberName (mi) == docName)
3354 static string GetReplacedString (string typeName, string[] from, string[] to)
3358 for (int i = 0; i < from.Length; ++i)
3359 typeName = typeName.Replace (from [i], to [i]);
3363 private static int CountChars (string s, char c)
3366 for (int i = 0; i < s.Length; ++i) {
3374 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3379 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3382 this.ecmadocs = ecmaDocs;
3385 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3387 HashSet<string> seen = new HashSet<string> ();
3388 return GetDocumentationTypes (assembly, forTypes, seen)
3389 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3392 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3395 while (ecmadocs.Read ()) {
3396 switch (ecmadocs.Name) {
3398 if (typeDepth == -1)
3399 typeDepth = ecmadocs.Depth;
3400 if (ecmadocs.NodeType != XmlNodeType.Element)
3402 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3404 string typename = ecmadocs.GetAttribute ("FullName");
3405 string typename2 = MDocUpdater.GetTypeFileName (typename);
3406 if (forTypes != null &&
3407 forTypes.BinarySearch (typename) < 0 &&
3408 typename != typename2 &&
3409 forTypes.BinarySearch (typename2) < 0)
3412 if ((t = assembly.GetType (typename)) == null &&
3413 (t = assembly.GetType (typename2)) == null)
3415 seen.Add (typename);
3416 if (typename != typename2)
3417 seen.Add (typename2);
3418 Console.WriteLine (" Import: {0}", t.FullName);
3419 if (ecmadocs.Name != "Docs") {
3420 int depth = ecmadocs.Depth;
3421 while (ecmadocs.Read ()) {
3422 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3426 if (!ecmadocs.IsStartElement ("Docs"))
3427 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3437 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3439 return GetMembers (basefile, type)
3440 .Concat (base.GetDocumentationMembers (basefile, type));
3443 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3445 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3448 if (ecmadocs.IsEmptyElement)
3451 int membersDepth = ecmadocs.Depth;
3453 while (go && ecmadocs.Read ()) {
3454 switch (ecmadocs.Name) {
3456 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3458 DocumentationMember dm = new DocumentationMember (ecmadocs);
3459 string xp = MDocUpdater.GetXPathForMember (dm);
3460 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3462 if (oldmember == null) {
3463 m = GetMember (type, dm);
3465 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3466 type.FullName, dm.MemberSignatures ["C#"]);
3467 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3470 // oldmember lookup may have failed due to type parameter renames.
3472 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3473 if (oldmember == null) {
3474 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3475 oldmember = basefile.CreateElement ("Member");
3476 oldmember.SetAttribute ("MemberName", dm.MemberName);
3477 members.AppendChild (oldmember);
3478 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3479 XmlElement ms = basefile.CreateElement ("MemberSignature");
3480 ms.SetAttribute ("Language", key);
3481 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3482 oldmember.AppendChild (ms);
3484 oldmember.SetAttribute ("__monodocer-seen__", "true");
3485 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3490 m = GetMember (type, new DocumentationMember (oldmember));
3492 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3493 type.FullName, dm.MemberSignatures ["C#"]);
3496 oldmember.SetAttribute ("__monodocer-seen__", "true");
3498 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3499 if (ecmadocs.Name != "Docs")
3500 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3505 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3514 abstract class DocumentationImporter {
3516 public abstract void ImportDocumentation (DocsNodeInfo info);
3519 class MsxdocDocumentationImporter : DocumentationImporter {
3521 XmlDocument slashdocs;
3523 public MsxdocDocumentationImporter (string file)
3525 var xml = File.ReadAllText (file);
3527 // Ensure Unix line endings
3528 xml = xml.Replace ("\r", "");
3530 slashdocs = new XmlDocument();
3531 slashdocs.LoadXml (xml);
3534 public override void ImportDocumentation (DocsNodeInfo info)
3536 XmlNode elem = GetDocs (info.Member ?? info.Type);
3541 XmlElement e = info.Node;
3543 if (elem.SelectSingleNode("summary") != null)
3544 MDocUpdater.ClearElement(e, "summary");
3545 if (elem.SelectSingleNode("remarks") != null)
3546 MDocUpdater.ClearElement(e, "remarks");
3547 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3548 MDocUpdater.ClearElement(e, "value");
3549 MDocUpdater.ClearElement(e, "returns");
3552 foreach (XmlNode child in elem.ChildNodes) {
3553 switch (child.Name) {
3556 XmlAttribute name = child.Attributes ["name"];
3559 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3561 p2.InnerXml = child.InnerXml;
3564 // Occasionally XML documentation will use <returns/> on
3565 // properties, so let's try to normalize things.
3568 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3569 v.InnerXml = child.InnerXml;
3575 case "permission": {
3576 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3579 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3581 a = e.OwnerDocument.CreateElement (child.Name);
3582 a.SetAttribute ("cref", cref.Value);
3585 a.InnerXml = child.InnerXml;
3589 XmlAttribute cref = child.Attributes ["cref"];
3592 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3594 a = e.OwnerDocument.CreateElement ("altmember");
3595 a.SetAttribute ("cref", cref.Value);
3602 if (child.NodeType == XmlNodeType.Element &&
3603 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3606 MDocUpdater.CopyNode (child, e);
3613 private XmlNode GetDocs (MemberReference member)
3615 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3616 if (slashdocsig != null)
3617 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3622 class EcmaDocumentationImporter : DocumentationImporter {
3626 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3628 this.ecmadocs = ecmaDocs;
3631 public override void ImportDocumentation (DocsNodeInfo info)
3633 if (!ecmadocs.IsStartElement ("Docs")) {
3637 XmlElement e = info.Node;
3639 int depth = ecmadocs.Depth;
3640 ecmadocs.ReadStartElement ("Docs");
3641 while (ecmadocs.Read ()) {
3642 if (ecmadocs.Name == "Docs") {
3643 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3646 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3648 if (!ecmadocs.IsStartElement ())
3650 switch (ecmadocs.Name) {
3653 string name = ecmadocs.GetAttribute ("name");
3656 XmlNode doc = e.SelectSingleNode (
3657 ecmadocs.Name + "[@name='" + name + "']");
3658 string value = ecmadocs.ReadInnerXml ();
3660 doc.InnerXml = value.Replace ("\r", "");
3667 string name = ecmadocs.Name;
3668 string cref = ecmadocs.GetAttribute ("cref");
3671 XmlNode doc = e.SelectSingleNode (
3672 ecmadocs.Name + "[@cref='" + cref + "']");
3673 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3675 doc.InnerXml = value;
3677 XmlElement n = e.OwnerDocument.CreateElement (name);
3678 n.SetAttribute ("cref", cref);
3685 string name = ecmadocs.Name;
3686 string xpath = ecmadocs.Name;
3687 StringList attributes = new StringList (ecmadocs.AttributeCount);
3688 if (ecmadocs.MoveToFirstAttribute ()) {
3690 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3691 } while (ecmadocs.MoveToNextAttribute ());
3692 ecmadocs.MoveToContent ();
3694 if (attributes.Count > 0) {
3695 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3697 XmlNode doc = e.SelectSingleNode (xpath);
3698 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3700 doc.InnerXml = value;
3703 XmlElement n = e.OwnerDocument.CreateElement (name);
3705 foreach (string a in attributes) {
3706 int eq = a.IndexOf ('=');
3707 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3718 class DocumentationMember {
3719 public StringToStringMap MemberSignatures = new StringToStringMap ();
3720 public string ReturnType;
3721 public StringList Parameters;
3722 public string MemberName;
3723 public string MemberType;
3725 public DocumentationMember (XmlReader reader)
3727 MemberName = reader.GetAttribute ("MemberName");
3728 int depth = reader.Depth;
3730 StringList p = new StringList ();
3732 if (reader.NodeType != XmlNodeType.Element)
3735 bool shouldUse = true;
3737 string apistyle = reader.GetAttribute ("apistyle");
3738 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "old"; // only use this tag if it's an 'old' style node
3740 catch (Exception ex) {}
3741 switch (reader.Name) {
3742 case "MemberSignature":
3744 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3748 MemberType = reader.ReadElementString ();
3751 if (reader.Depth == depth + 2 && shouldUse)
3752 ReturnType = reader.ReadElementString ();
3755 if (reader.Depth == depth + 2 && shouldUse)
3756 p.Add (reader.GetAttribute ("Type"));
3759 if (reader.Depth == depth + 1)
3763 } while (go && reader.Read () && reader.Depth >= depth);
3769 public DocumentationMember (XmlNode node)
3771 MemberName = node.Attributes ["MemberName"].Value;
3772 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3773 XmlAttribute l = n.Attributes ["Language"];
3774 XmlAttribute v = n.Attributes ["Value"];
3775 XmlAttribute apistyle = n.Attributes ["apistyle"];
3776 bool shouldUse = apistyle == null || apistyle.Value == "old";
3777 if (l != null && v != null && shouldUse)
3778 MemberSignatures [l.Value] = v.Value;
3780 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3781 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='old']");
3783 ReturnType = rt.InnerText;
3784 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='old']");
3786 Parameters = new StringList (p.Count);
3787 for (int i = 0; i < p.Count; ++i)
3788 Parameters.Add (p [i].Attributes ["Type"].Value);
3793 public class DynamicParserContext {
3794 public ReadOnlyCollection<bool> TransformFlags;
3795 public int TransformIndex;
3797 public DynamicParserContext (ICustomAttributeProvider provider)
3800 if (provider.HasCustomAttributes &&
3801 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
3802 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
3803 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
3804 ? new CustomAttributeArgument [0]
3805 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
3807 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
3812 public enum MemberFormatterState {
3814 WithinGenericTypeParameters,
3817 public abstract class MemberFormatter {
3819 public virtual string Language {
3823 public string GetName (MemberReference member)
3825 return GetName (member, null);
3828 public virtual string GetName (MemberReference member, DynamicParserContext context)
3830 TypeReference type = member as TypeReference;
3832 return GetTypeName (type, context);
3833 MethodReference method = member as MethodReference;
3834 if (method != null && method.Name == ".ctor") // method.IsConstructor
3835 return GetConstructorName (method);
3837 return GetMethodName (method);
3838 PropertyReference prop = member as PropertyReference;
3840 return GetPropertyName (prop);
3841 FieldReference field = member as FieldReference;
3843 return GetFieldName (field);
3844 EventReference e = member as EventReference;
3846 return GetEventName (e);
3847 throw new NotSupportedException ("Can't handle: " +
3848 (member == null ? "null" : member.GetType().ToString()));
3851 protected virtual string GetTypeName (TypeReference type)
3853 return GetTypeName (type, null);
3856 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
3859 throw new ArgumentNullException ("type");
3860 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
3863 protected virtual char[] ArrayDelimeters {
3864 get {return new char[]{'[', ']'};}
3867 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3869 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3871 if (type is ArrayType) {
3872 TypeSpecification spec = type as TypeSpecification;
3873 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
3874 return AppendArrayModifiers (buf, (ArrayType) type);
3876 if (type is ByReferenceType) {
3877 return AppendRefTypeName (buf, type, context);
3879 if (type is PointerType) {
3880 return AppendPointerTypeName (buf, type, context);
3882 if (type is GenericParameter) {
3883 return AppendTypeName (buf, type, context);
3885 AppendNamespace (buf, type);
3886 GenericInstanceType genInst = type as GenericInstanceType;
3887 if (type.GenericParameters.Count == 0 &&
3888 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3889 return AppendFullTypeName (buf, type, context);
3891 return AppendGenericType (buf, type, context);
3894 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3896 string ns = DocUtils.GetNamespace (type);
3897 if (ns != null && ns.Length > 0)
3898 buf.Append (ns).Append ('.');
3902 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3904 if (type.DeclaringType != null)
3905 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
3906 return AppendTypeName (buf, type, context);
3909 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3911 if (context != null)
3912 context.TransformIndex++;
3913 return AppendTypeName (buf, type.Name);
3916 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3918 int n = typename.IndexOf ("`");
3920 return buf.Append (typename.Substring (0, n));
3921 return buf.Append (typename);
3924 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
3926 buf.Append (ArrayDelimeters [0]);
3927 int rank = array.Rank;
3929 buf.Append (new string (',', rank-1));
3930 return buf.Append (ArrayDelimeters [1]);
3933 protected virtual string RefTypeModifier {
3937 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3939 TypeSpecification spec = type as TypeSpecification;
3940 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3941 .Append (RefTypeModifier);
3944 protected virtual string PointerModifier {
3948 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
3950 TypeSpecification spec = type as TypeSpecification;
3951 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
3952 .Append (PointerModifier);
3955 protected virtual char[] GenericTypeContainer {
3956 get {return new char[]{'<', '>'};}
3959 protected virtual char NestedTypeSeparator {
3963 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
3965 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3966 type is GenericInstanceType ? type.GetElementType () : type);
3967 List<TypeReference> genArgs = GetGenericArguments (type);
3970 bool insertNested = false;
3971 foreach (var decl in decls) {
3972 TypeReference declDef = decl.Resolve () ?? decl;
3974 buf.Append (NestedTypeSeparator);
3976 insertNested = true;
3977 AppendTypeName (buf, declDef, context);
3978 int ac = DocUtils.GetGenericArgumentCount (declDef);
3982 buf.Append (GenericTypeContainer [0]);
3983 var origState = MemberFormatterState;
3984 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3985 _AppendTypeName (buf, genArgs [argIdx++], context);
3986 for (int i = 1; i < c; ++i) {
3987 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
3989 MemberFormatterState = origState;
3990 buf.Append (GenericTypeContainer [1]);
3996 protected List<TypeReference> GetGenericArguments (TypeReference type)
3998 var args = new List<TypeReference> ();
3999 GenericInstanceType inst = type as GenericInstanceType;
4001 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4003 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4007 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4012 protected virtual string GetConstructorName (MethodReference constructor)
4014 return constructor.Name;
4017 protected virtual string GetMethodName (MethodReference method)
4022 protected virtual string GetPropertyName (PropertyReference property)
4024 return property.Name;
4027 protected virtual string GetFieldName (FieldReference field)
4032 protected virtual string GetEventName (EventReference e)
4037 public virtual string GetDeclaration (MemberReference member)
4040 throw new ArgumentNullException ("member");
4041 TypeDefinition type = member as TypeDefinition;
4043 return GetTypeDeclaration (type);
4044 MethodDefinition method = member as MethodDefinition;
4045 if (method != null && method.IsConstructor)
4046 return GetConstructorDeclaration (method);
4048 return GetMethodDeclaration (method);
4049 PropertyDefinition prop = member as PropertyDefinition;
4051 return GetPropertyDeclaration (prop);
4052 FieldDefinition field = member as FieldDefinition;
4054 return GetFieldDeclaration (field);
4055 EventDefinition e = member as EventDefinition;
4057 return GetEventDeclaration (e);
4058 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4061 protected virtual string GetTypeDeclaration (TypeDefinition type)
4064 throw new ArgumentNullException ("type");
4065 StringBuilder buf = new StringBuilder (type.Name.Length);
4066 _AppendTypeName (buf, type, null);
4067 AppendGenericTypeConstraints (buf, type);
4068 return buf.ToString ();
4071 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4073 return GetConstructorName (constructor);
4076 protected virtual string GetMethodDeclaration (MethodDefinition method)
4078 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4079 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4082 // Special signature for destructors.
4083 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4084 return GetFinalizerName (method);
4086 StringBuilder buf = new StringBuilder ();
4088 AppendVisibility (buf, method);
4089 if (buf.Length == 0 &&
4090 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4093 AppendModifiers (buf, method);
4095 if (buf.Length != 0)
4097 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4099 AppendMethodName (buf, method);
4100 AppendGenericMethod (buf, method).Append (" ");
4101 AppendParameters (buf, method, method.Parameters);
4102 AppendGenericMethodConstraints (buf, method);
4103 return buf.ToString ();
4106 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4108 return buf.Append (method.Name);
4111 protected virtual string GetFinalizerName (MethodDefinition method)
4116 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4121 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4126 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4131 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4136 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4141 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4143 return GetPropertyName (property);
4146 protected virtual string GetFieldDeclaration (FieldDefinition field)
4148 return GetFieldName (field);
4151 protected virtual string GetEventDeclaration (EventDefinition e)
4153 return GetEventName (e);
4157 class ILFullMemberFormatter : MemberFormatter {
4159 public override string Language {
4160 get {return "ILAsm";}
4163 protected override char NestedTypeSeparator {
4169 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4171 if (GetBuiltinType (type.FullName) != null)
4173 string ns = DocUtils.GetNamespace (type);
4174 if (ns != null && ns.Length > 0) {
4175 if (type.IsValueType)
4176 buf.Append ("valuetype ");
4178 buf.Append ("class ");
4179 buf.Append (ns).Append ('.');
4184 protected static string GetBuiltinType (string t)
4187 case "System.Byte": return "unsigned int8";
4188 case "System.SByte": return "int8";
4189 case "System.Int16": return "int16";
4190 case "System.Int32": return "int32";
4191 case "System.Int64": return "int64";
4192 case "System.IntPtr": return "native int";
4194 case "System.UInt16": return "unsigned int16";
4195 case "System.UInt32": return "unsigned int32";
4196 case "System.UInt64": return "unsigned int64";
4197 case "System.UIntPtr": return "native unsigned int";
4199 case "System.Single": return "float32";
4200 case "System.Double": return "float64";
4201 case "System.Boolean": return "bool";
4202 case "System.Char": return "char";
4203 case "System.Void": return "void";
4204 case "System.String": return "string";
4205 case "System.Object": return "object";
4210 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4212 return buf.Append (typename);
4215 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4217 if (type is GenericParameter) {
4218 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4222 string s = GetBuiltinType (type.FullName);
4224 return buf.Append (s);
4226 return base.AppendTypeName (buf, type, context);
4229 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4231 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4232 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4234 GenericParameterAttributes attrs = type.Attributes;
4235 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4236 buf.Append ("class ");
4237 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4238 buf.Append ("struct ");
4239 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4240 buf.Append (".ctor ");
4241 IList<TypeReference> constraints = type.Constraints;
4242 MemberFormatterState = 0;
4243 if (constraints.Count > 0) {
4244 var full = new ILFullMemberFormatter ();
4245 buf.Append ("(").Append (full.GetName (constraints [0]));
4246 for (int i = 1; i < constraints.Count; ++i) {
4247 buf.Append (", ").Append (full.GetName (constraints [i]));
4251 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4253 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4255 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4260 protected override string GetTypeDeclaration (TypeDefinition type)
4262 string visibility = GetTypeVisibility (type.Attributes);
4263 if (visibility == null)
4266 StringBuilder buf = new StringBuilder ();
4268 buf.Append (".class ");
4270 buf.Append ("nested ");
4271 buf.Append (visibility).Append (" ");
4272 if (type.IsInterface)
4273 buf.Append ("interface ");
4274 if (type.IsSequentialLayout)
4275 buf.Append ("sequential ");
4276 if (type.IsAutoLayout)
4277 buf.Append ("auto ");
4278 if (type.IsAnsiClass)
4279 buf.Append ("ansi ");
4280 if (type.IsAbstract)
4281 buf.Append ("abstract ");
4282 if (type.IsSerializable)
4283 buf.Append ("serializable ");
4285 buf.Append ("sealed ");
4286 if (type.IsBeforeFieldInit)
4287 buf.Append ("beforefieldinit ");
4288 var state = MemberFormatterState;
4289 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4290 buf.Append (GetName (type));
4291 MemberFormatterState = state;
4292 var full = new ILFullMemberFormatter ();
4293 if (type.BaseType != null) {
4294 buf.Append (" extends ");
4295 if (type.BaseType.FullName == "System.Object")
4296 buf.Append ("System.Object");
4298 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4301 foreach (var name in type.Interfaces
4302 .Select (i => full.GetName (i))
4303 .OrderBy (n => n)) {
4305 buf.Append (" implements ");
4314 return buf.ToString ();
4317 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4319 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4320 type is GenericInstanceType ? type.GetElementType () : type);
4322 foreach (var decl in decls) {
4323 TypeReference declDef = decl.Resolve () ?? decl;
4325 buf.Append (NestedTypeSeparator);
4328 AppendTypeName (buf, declDef, context);
4332 foreach (TypeReference arg in GetGenericArguments (type)) {
4336 _AppendTypeName (buf, arg, context);
4342 static string GetTypeVisibility (TypeAttributes ta)
4344 switch (ta & TypeAttributes.VisibilityMask) {
4345 case TypeAttributes.Public:
4346 case TypeAttributes.NestedPublic:
4349 case TypeAttributes.NestedFamily:
4350 case TypeAttributes.NestedFamORAssem:
4358 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4360 return GetMethodDeclaration (constructor);
4363 protected override string GetMethodDeclaration (MethodDefinition method)
4365 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4368 var buf = new StringBuilder ();
4369 buf.Append (".method ");
4370 AppendVisibility (buf, method);
4371 if (method.IsStatic)
4372 buf.Append ("static ");
4373 if (method.IsHideBySig)
4374 buf.Append ("hidebysig ");
4375 if (method.IsPInvokeImpl) {
4376 var info = method.PInvokeInfo;
4377 buf.Append ("pinvokeimpl (\"")
4378 .Append (info.Module.Name)
4379 .Append ("\" as \"")
4380 .Append (info.EntryPoint)
4382 if (info.IsCharSetAuto)
4383 buf.Append (" auto");
4384 if (info.IsCharSetUnicode)
4385 buf.Append (" unicode");
4386 if (info.IsCharSetAnsi)
4387 buf.Append (" ansi");
4388 if (info.IsCallConvCdecl)
4389 buf.Append (" cdecl");
4390 if (info.IsCallConvStdCall)
4391 buf.Append (" stdcall");
4392 if (info.IsCallConvWinapi)
4393 buf.Append (" winapi");
4394 if (info.IsCallConvThiscall)
4395 buf.Append (" thiscall");
4396 if (info.SupportsLastError)
4397 buf.Append (" lasterr");
4400 if (method.IsSpecialName)
4401 buf.Append ("specialname ");
4402 if (method.IsRuntimeSpecialName)
4403 buf.Append ("rtspecialname ");
4404 if (method.IsNewSlot)
4405 buf.Append ("newslot ");
4406 if (method.IsVirtual)
4407 buf.Append ("virtual ");
4408 if (!method.IsStatic)
4409 buf.Append ("instance ");
4410 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4412 .Append (method.Name);
4413 if (method.IsGenericMethod ()) {
4414 var state = MemberFormatterState;
4415 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4416 IList<GenericParameter> args = method.GenericParameters;
4417 if (args.Count > 0) {
4419 _AppendTypeName (buf, args [0], null);
4420 for (int i = 1; i < args.Count; ++i)
4421 _AppendTypeName (buf.Append (", "), args [i], null);
4424 MemberFormatterState = state;
4429 for (int i = 0; i < method.Parameters.Count; ++i) {
4433 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4435 buf.Append (method.Parameters [i].Name);
4439 buf.Append (" cil");
4440 if (method.IsRuntime)
4441 buf.Append (" runtime");
4442 if (method.IsManaged)
4443 buf.Append (" managed");
4445 return buf.ToString ();
4448 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4450 if (DocUtils.IsExplicitlyImplemented (method)) {
4451 TypeReference iface;
4452 MethodReference ifaceMethod;
4453 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4454 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4456 .Append (ifaceMethod.Name);
4458 return base.AppendMethodName (buf, method);
4461 protected override string RefTypeModifier {
4465 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4467 if (method.IsPublic)
4468 return buf.Append ("public ");
4469 if (method.IsFamilyAndAssembly)
4470 return buf.Append ("familyandassembly");
4471 if (method.IsFamilyOrAssembly)
4472 return buf.Append ("familyorassembly");
4473 if (method.IsFamily)
4474 return buf.Append ("family");
4478 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4480 string modifiers = String.Empty;
4481 if (method.IsStatic) modifiers += " static";
4482 if (method.IsVirtual && !method.IsAbstract) {
4483 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4484 else modifiers += " override";
4486 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4487 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4488 if (method.IsFinal) modifiers += " sealed";
4489 if (modifiers == " virtual sealed") modifiers = "";
4491 return buf.Append (modifiers);
4494 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4496 if (method.IsGenericMethod ()) {
4497 IList<GenericParameter> args = method.GenericParameters;
4498 if (args.Count > 0) {
4500 buf.Append (args [0].Name);
4501 for (int i = 1; i < args.Count; ++i)
4502 buf.Append (",").Append (args [i].Name);
4509 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4511 return AppendParameters (buf, method, parameters, '(', ')');
4514 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4518 if (parameters.Count > 0) {
4519 if (DocUtils.IsExtensionMethod (method))
4520 buf.Append ("this ");
4521 AppendParameter (buf, parameters [0]);
4522 for (int i = 1; i < parameters.Count; ++i) {
4524 AppendParameter (buf, parameters [i]);
4528 return buf.Append (end);
4531 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4533 if (parameter.ParameterType is ByReferenceType) {
4534 if (parameter.IsOut)
4535 buf.Append ("out ");
4537 buf.Append ("ref ");
4539 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4540 return buf.Append (parameter.Name);
4543 protected override string GetPropertyDeclaration (PropertyDefinition property)
4545 MethodDefinition gm = null, sm = null;
4547 string get_visible = null;
4548 if ((gm = property.GetMethod) != null &&
4549 (DocUtils.IsExplicitlyImplemented (gm) ||
4550 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4551 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4552 string set_visible = null;
4553 if ((sm = property.SetMethod) != null &&
4554 (DocUtils.IsExplicitlyImplemented (sm) ||
4555 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4556 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4558 if ((set_visible == null) && (get_visible == null))
4561 StringBuilder buf = new StringBuilder ()
4562 .Append (".property ");
4563 if (!(gm ?? sm).IsStatic)
4564 buf.Append ("instance ");
4565 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4566 buf.Append (' ').Append (property.Name);
4567 if (!property.HasParameters || property.Parameters.Count == 0)
4568 return buf.ToString ();
4572 foreach (ParameterDefinition p in property.Parameters) {
4576 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4580 return buf.ToString ();
4583 protected override string GetFieldDeclaration (FieldDefinition field)
4585 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4586 if (declType.IsEnum && field.Name == "value__")
4587 return null; // This member of enums aren't documented.
4589 StringBuilder buf = new StringBuilder ();
4590 AppendFieldVisibility (buf, field);
4591 if (buf.Length == 0)
4594 buf.Insert (0, ".field ");
4597 buf.Append ("static ");
4598 if (field.IsInitOnly)
4599 buf.Append ("initonly ");
4600 if (field.IsLiteral)
4601 buf.Append ("literal ");
4602 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4603 buf.Append (' ').Append (field.Name);
4604 AppendFieldValue (buf, field);
4606 return buf.ToString ();
4609 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4612 return buf.Append ("public ");
4613 if (field.IsFamilyAndAssembly)
4614 return buf.Append ("familyandassembly ");
4615 if (field.IsFamilyOrAssembly)
4616 return buf.Append ("familyorassembly ");
4618 return buf.Append ("family ");
4622 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4624 // enums have a value__ field, which we ignore
4625 if (field.DeclaringType.IsGenericType ())
4627 if (field.HasConstant && field.IsLiteral) {
4630 val = field.Constant;
4635 buf.Append (" = ").Append ("null");
4636 else if (val is Enum)
4638 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4640 .Append (val.ToString ())
4642 else if (val is IFormattable) {
4643 string value = ((IFormattable)val).ToString();
4646 buf.Append ("\"" + value + "\"");
4648 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4657 protected override string GetEventDeclaration (EventDefinition e)
4659 StringBuilder buf = new StringBuilder ();
4660 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4665 buf.Append (".event ")
4666 .Append (GetName (e.EventType))
4670 return buf.ToString ();
4674 class ILMemberFormatter : ILFullMemberFormatter {
4675 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4681 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
4682 protected static string _GetBuiltinType (string t)
4684 //string moddedType = base.GetBuiltinType (t);
4686 //return moddedType;
4690 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
4691 protected override string GetCSharpType (string t) {
4692 string moddedType = base.GetCSharpType (t);
4694 switch (moddedType) {
4695 case "int": return "nint";
4700 case "System.Drawing.SizeF":
4701 return "CoreGraphics.CGSize";
4702 case "System.Drawing.PointF":
4703 return "CoreGraphics.CGPoint";
4704 case "System.Drawing.RectangleF":
4705 return "CoreGraphics.CGPoint";
4711 class CSharpFullMemberFormatter : MemberFormatter {
4713 public override string Language {
4717 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4720 string ns = DocUtils.GetNamespace (type);
4721 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4722 buf.Append (ns).Append ('.');
4726 protected virtual string GetCSharpType (string t)
4729 case "System.Byte": return "byte";
4730 case "System.SByte": return "sbyte";
4731 case "System.Int16": return "short";
4732 case "System.Int32": return "int";
4733 case "System.Int64": return "long";
4735 case "System.UInt16": return "ushort";
4736 case "System.UInt32": return "uint";
4737 case "System.UInt64": return "ulong";
4739 case "System.Single": return "float";
4740 case "System.Double": return "double";
4741 case "System.Decimal": return "decimal";
4742 case "System.Boolean": return "bool";
4743 case "System.Char": return "char";
4744 case "System.Void": return "void";
4745 case "System.String": return "string";
4746 case "System.Object": return "object";
4751 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4753 if (context != null && context.TransformFlags != null &&
4754 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
4755 context.TransformIndex++;
4756 return buf.Append ("dynamic");
4759 if (type is GenericParameter)
4760 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
4761 string t = type.FullName;
4762 if (!t.StartsWith ("System.")) {
4763 return base.AppendTypeName (buf, type, context);
4766 string s = GetCSharpType (t);
4768 if (context != null)
4769 context.TransformIndex++;
4770 return buf.Append (s);
4773 return base.AppendTypeName (buf, type, context);
4776 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
4778 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4780 GenericParameterAttributes attrs = type.Attributes;
4781 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4782 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
4786 buf.Append ("out ");
4790 protected override string GetTypeDeclaration (TypeDefinition type)
4792 string visibility = GetTypeVisibility (type.Attributes);
4793 if (visibility == null)
4796 StringBuilder buf = new StringBuilder ();
4798 buf.Append (visibility);
4801 MemberFormatter full = new CSharpFullMemberFormatter ();
4803 if (DocUtils.IsDelegate (type)) {
4804 buf.Append("delegate ");
4805 MethodDefinition invoke = type.GetMethod ("Invoke");
4806 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
4807 buf.Append (GetName (type));
4808 AppendParameters (buf, invoke, invoke.Parameters);
4809 AppendGenericTypeConstraints (buf, type);
4812 return buf.ToString();
4815 if (type.IsAbstract && !type.IsInterface)
4816 buf.Append("abstract ");
4817 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4818 buf.Append("sealed ");
4819 buf.Replace ("abstract sealed", "static");
4821 buf.Append (GetTypeKind (type));
4823 buf.Append (GetCSharpType (type.FullName) == null
4828 TypeReference basetype = type.BaseType;
4829 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
4832 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4833 .Select (iface => full.GetName (iface))
4837 if (basetype != null || interface_names.Count > 0)
4840 if (basetype != null) {
4841 buf.Append (full.GetName (basetype));
4842 if (interface_names.Count > 0)
4846 for (int i = 0; i < interface_names.Count; i++){
4849 buf.Append (interface_names [i]);
4851 AppendGenericTypeConstraints (buf, type);
4854 return buf.ToString ();
4857 static string GetTypeKind (TypeDefinition t)
4863 if (t.IsClass || t.FullName == "System.Enum")
4867 throw new ArgumentException(t.FullName);
4870 static string GetTypeVisibility (TypeAttributes ta)
4872 switch (ta & TypeAttributes.VisibilityMask) {
4873 case TypeAttributes.Public:
4874 case TypeAttributes.NestedPublic:
4877 case TypeAttributes.NestedFamily:
4878 case TypeAttributes.NestedFamORAssem:
4886 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4888 if (type.GenericParameters.Count == 0)
4890 return AppendConstraints (buf, type.GenericParameters);
4893 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4895 foreach (GenericParameter genArg in genArgs) {
4896 GenericParameterAttributes attrs = genArg.Attributes;
4897 IList<TypeReference> constraints = genArg.Constraints;
4898 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4901 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4902 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4903 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4906 if (!isref && !isvt && !isnew && constraints.Count == 0)
4908 buf.Append (" where ").Append (genArg.Name).Append (" : ");
4910 buf.Append ("class");
4914 buf.Append ("struct");
4917 if (constraints.Count > 0 && !isvt) {
4920 buf.Append (GetTypeName (constraints [0]));
4921 for (int i = 1; i < constraints.Count; ++i)
4922 buf.Append (", ").Append (GetTypeName (constraints [i]));
4924 if (isnew && !isvt) {
4927 buf.Append ("new()");
4933 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4935 StringBuilder buf = new StringBuilder ();
4936 AppendVisibility (buf, constructor);
4937 if (buf.Length == 0)
4941 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
4942 AppendParameters (buf, constructor, constructor.Parameters);
4945 return buf.ToString ();
4948 protected override string GetMethodDeclaration (MethodDefinition method)
4950 string decl = base.GetMethodDeclaration (method);
4956 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4958 if (DocUtils.IsExplicitlyImplemented (method)) {
4959 TypeReference iface;
4960 MethodReference ifaceMethod;
4961 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4962 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4964 .Append (ifaceMethod.Name);
4966 return base.AppendMethodName (buf, method);
4969 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4971 if (method.GenericParameters.Count == 0)
4973 return AppendConstraints (buf, method.GenericParameters);
4976 protected override string RefTypeModifier {
4980 protected override string GetFinalizerName (MethodDefinition method)
4982 return "~" + method.DeclaringType.Name + " ()";
4985 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4989 if (method.IsPublic)
4990 return buf.Append ("public");
4991 if (method.IsFamily || method.IsFamilyOrAssembly)
4992 return buf.Append ("protected");
4996 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4998 string modifiers = String.Empty;
4999 if (method.IsStatic) modifiers += " static";
5000 if (method.IsVirtual && !method.IsAbstract) {
5001 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5002 else modifiers += " override";
5004 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5005 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5006 if (method.IsFinal) modifiers += " sealed";
5007 if (modifiers == " virtual sealed") modifiers = "";
5009 return buf.Append (modifiers);
5012 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5014 if (method.IsGenericMethod ()) {
5015 IList<GenericParameter> args = method.GenericParameters;
5016 if (args.Count > 0) {
5018 buf.Append (args [0].Name);
5019 for (int i = 1; i < args.Count; ++i)
5020 buf.Append (",").Append (args [i].Name);
5027 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5029 return AppendParameters (buf, method, parameters, '(', ')');
5032 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5036 if (parameters.Count > 0) {
5037 if (DocUtils.IsExtensionMethod (method))
5038 buf.Append ("this ");
5039 AppendParameter (buf, parameters [0]);
5040 for (int i = 1; i < parameters.Count; ++i) {
5042 AppendParameter (buf, parameters [i]);
5046 return buf.Append (end);
5049 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5051 if (parameter.ParameterType is ByReferenceType) {
5052 if (parameter.IsOut)
5053 buf.Append ("out ");
5055 buf.Append ("ref ");
5057 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5058 buf.Append (parameter.Name);
5059 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5060 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5065 protected override string GetPropertyDeclaration (PropertyDefinition property)
5067 MethodDefinition method;
5069 string get_visible = null;
5070 if ((method = property.GetMethod) != null &&
5071 (DocUtils.IsExplicitlyImplemented (method) ||
5072 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5073 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5074 string set_visible = null;
5075 if ((method = property.SetMethod) != null &&
5076 (DocUtils.IsExplicitlyImplemented (method) ||
5077 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5078 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5080 if ((set_visible == null) && (get_visible == null))
5084 StringBuilder buf = new StringBuilder ();
5085 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5086 buf.Append (visibility = get_visible);
5087 else if (set_visible != null && get_visible == null)
5088 buf.Append (visibility = set_visible);
5090 buf.Append (visibility = "public");
5092 // Pick an accessor to use for static/virtual/override/etc. checks.
5093 method = property.SetMethod;
5095 method = property.GetMethod;
5097 string modifiers = String.Empty;
5098 if (method.IsStatic) modifiers += " static";
5099 if (method.IsVirtual && !method.IsAbstract) {
5100 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5101 modifiers += " virtual";
5103 modifiers += " override";
5105 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5106 if (method.IsAbstract && !declDef.IsInterface)
5107 modifiers += " abstract";
5109 modifiers += " sealed";
5110 if (modifiers == " virtual sealed")
5112 buf.Append (modifiers).Append (' ');
5114 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5116 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5117 string name = property.Name;
5118 foreach (MemberReference mi in defs) {
5119 if (mi == property) {
5124 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5126 if (property.Parameters.Count != 0) {
5127 AppendParameters (buf, method, property.Parameters, '[', ']');
5131 if (get_visible != null) {
5132 if (get_visible != visibility)
5133 buf.Append (' ').Append (get_visible);
5134 buf.Append (" get;");
5136 if (set_visible != null) {
5137 if (set_visible != visibility)
5138 buf.Append (' ').Append (set_visible);
5139 buf.Append (" set;");
5143 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5146 protected override string GetFieldDeclaration (FieldDefinition field)
5148 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5149 if (declType.IsEnum && field.Name == "value__")
5150 return null; // This member of enums aren't documented.
5152 StringBuilder buf = new StringBuilder ();
5153 AppendFieldVisibility (buf, field);
5154 if (buf.Length == 0)
5157 if (declType.IsEnum)
5160 if (field.IsStatic && !field.IsLiteral)
5161 buf.Append (" static");
5162 if (field.IsInitOnly)
5163 buf.Append (" readonly");
5164 if (field.IsLiteral)
5165 buf.Append (" const");
5167 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5168 buf.Append (field.Name);
5169 AppendFieldValue (buf, field);
5172 return buf.ToString ();
5175 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5178 return buf.Append ("public");
5179 if (field.IsFamily || field.IsFamilyOrAssembly)
5180 return buf.Append ("protected");
5184 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5186 // enums have a value__ field, which we ignore
5187 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5188 field.DeclaringType.IsGenericType ())
5190 if (field.HasConstant && field.IsLiteral) {
5193 val = field.Constant;
5198 buf.Append (" = ").Append ("null");
5199 else if (val is Enum)
5200 buf.Append (" = ").Append (val.ToString ());
5201 else if (val is IFormattable) {
5202 string value = ((IFormattable)val).ToString();
5204 value = "\"" + value + "\"";
5205 buf.Append (" = ").Append (value);
5211 protected override string GetEventDeclaration (EventDefinition e)
5213 StringBuilder buf = new StringBuilder ();
5214 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5218 AppendModifiers (buf, e.AddMethod);
5220 buf.Append (" event ");
5221 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5222 buf.Append (e.Name).Append (';');
5224 return buf.ToString ();
5228 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5229 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5235 class DocTypeFullMemberFormatter : MemberFormatter {
5236 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5238 protected override char NestedTypeSeparator {
5243 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5244 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5250 class SlashDocMemberFormatter : MemberFormatter {
5252 protected override char[] GenericTypeContainer {
5253 get {return new char[]{'{', '}'};}
5256 private bool AddTypeCount = true;
5258 private TypeReference genDeclType;
5259 private MethodReference genDeclMethod;
5261 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5263 if (type is GenericParameter) {
5265 if (genDeclType != null) {
5266 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5267 for (int i = 0; i < genArgs.Count; ++i) {
5268 if (genArgs [i].Name == type.Name) {
5269 buf.Append ('`').Append (i);
5274 if (genDeclMethod != null) {
5275 IList<GenericParameter> genArgs = null;
5276 if (genDeclMethod.IsGenericMethod ()) {
5277 genArgs = genDeclMethod.GenericParameters;
5278 for (int i = 0; i < genArgs.Count; ++i) {
5279 if (genArgs [i].Name == type.Name) {
5280 buf.Append ("``").Append (i);
5286 if (genDeclType == null && genDeclMethod == null) {
5287 // Probably from within an explicitly implemented interface member,
5288 // where CSC uses parameter names instead of indices (why?), e.g.
5289 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5290 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5291 buf.Append (type.Name);
5293 if (buf.Length == l) {
5294 throw new Exception (string.Format (
5295 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5296 type.Name, genDeclType, genDeclMethod));
5300 base.AppendTypeName (buf, type, context);
5302 int numArgs = type.GenericParameters.Count;
5303 if (type.DeclaringType != null)
5304 numArgs -= type.GenericParameters.Count;
5306 buf.Append ('`').Append (numArgs);
5313 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5315 buf.Append (ArrayDelimeters [0]);
5316 int rank = array.Rank;
5319 for (int i = 1; i < rank; ++i) {
5323 return buf.Append (ArrayDelimeters [1]);
5326 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5329 base.AppendGenericType (buf, type, context);
5331 AppendType (buf, type, context);
5335 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5337 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5338 bool insertNested = false;
5339 int prevParamCount = 0;
5340 foreach (var decl in decls) {
5342 buf.Append (NestedTypeSeparator);
5343 insertNested = true;
5344 base.AppendTypeName (buf, decl, context);
5345 int argCount = DocUtils.GetGenericArgumentCount (decl);
5346 int numArgs = argCount - prevParamCount;
5347 prevParamCount = argCount;
5349 buf.Append ('`').Append (numArgs);
5354 public override string GetDeclaration (MemberReference member)
5356 TypeReference r = member as TypeReference;
5358 return "T:" + GetTypeName (r);
5360 return base.GetDeclaration (member);
5363 protected override string GetConstructorName (MethodReference constructor)
5365 return GetMethodDefinitionName (constructor, "#ctor");
5368 protected override string GetMethodName (MethodReference method)
5371 MethodDefinition methodDef = method as MethodDefinition;
5372 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5375 TypeReference iface;
5376 MethodReference ifaceMethod;
5377 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5378 AddTypeCount = false;
5379 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5380 AddTypeCount = true;
5382 return GetMethodDefinitionName (method, name);
5385 private string GetMethodDefinitionName (MethodReference method, string name)
5387 StringBuilder buf = new StringBuilder ();
5388 buf.Append (GetTypeName (method.DeclaringType));
5390 buf.Append (name.Replace (".", "#"));
5391 if (method.IsGenericMethod ()) {
5392 IList<GenericParameter> genArgs = method.GenericParameters;
5393 if (genArgs.Count > 0)
5394 buf.Append ("``").Append (genArgs.Count);
5396 IList<ParameterDefinition> parameters = method.Parameters;
5398 genDeclType = method.DeclaringType;
5399 genDeclMethod = method;
5400 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5404 genDeclMethod = null;
5406 return buf.ToString ();
5409 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5411 if (parameters.Count == 0)
5416 AppendParameter (buf, genArgs, parameters [0]);
5417 for (int i = 1; i < parameters.Count; ++i) {
5419 AppendParameter (buf, genArgs, parameters [i]);
5422 return buf.Append (')');
5425 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5427 AddTypeCount = false;
5428 buf.Append (GetTypeName (parameter.ParameterType));
5429 AddTypeCount = true;
5433 protected override string GetPropertyName (PropertyReference property)
5437 PropertyDefinition propertyDef = property as PropertyDefinition;
5438 MethodDefinition method = null;
5439 if (propertyDef != null)
5440 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5441 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5442 name = property.Name;
5444 TypeReference iface;
5445 MethodReference ifaceMethod;
5446 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5447 AddTypeCount = false;
5448 name = string.Join ("#", new string[]{
5449 GetTypeName (iface).Replace (".", "#"),
5450 DocUtils.GetMember (property.Name)
5452 AddTypeCount = true;
5455 StringBuilder buf = new StringBuilder ();
5456 buf.Append (GetName (property.DeclaringType));
5459 IList<ParameterDefinition> parameters = property.Parameters;
5460 if (parameters.Count > 0) {
5461 genDeclType = property.DeclaringType;
5463 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5464 AppendParameter (buf, genArgs, parameters [0]);
5465 for (int i = 1; i < parameters.Count; ++i) {
5467 AppendParameter (buf, genArgs, parameters [i]);
5472 return buf.ToString ();
5475 protected override string GetFieldName (FieldReference field)
5477 return string.Format ("{0}.{1}",
5478 GetName (field.DeclaringType), field.Name);
5481 protected override string GetEventName (EventReference e)
5483 return string.Format ("{0}.{1}",
5484 GetName (e.DeclaringType), e.Name);
5487 protected override string GetTypeDeclaration (TypeDefinition type)
5489 string name = GetName (type);
5495 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5497 string name = GetName (constructor);
5503 protected override string GetMethodDeclaration (MethodDefinition method)
5505 string name = GetName (method);
5508 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5509 genDeclType = method.DeclaringType;
5510 genDeclMethod = method;
5511 name += "~" + GetName (method.ReturnType);
5513 genDeclMethod = null;
5518 protected override string GetPropertyDeclaration (PropertyDefinition property)
5520 string name = GetName (property);
5526 protected override string GetFieldDeclaration (FieldDefinition field)
5528 string name = GetName (field);
5534 protected override string GetEventDeclaration (EventDefinition e)
5536 string name = GetName (e);
5543 class FileNameMemberFormatter : SlashDocMemberFormatter {
5544 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5549 protected override char NestedTypeSeparator {