[runtime] Disable some tests in full-aot mode which cannot be AOTed because of type...
[mono-project.git] / mcs / tools / mdoc / Mono.Documentation / monodocer.cs
blob8b5b5ab08e4d440f58d3dd88d1b732cbd5ccc449
1 // Updater program for syncing Mono's ECMA-style documentation files
2 // with an assembly.
3 // By Joshua Tauberer <tauberer@for.net>
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Collections.ObjectModel;
9 using System.Diagnostics;
10 using System.Globalization;
11 using System.IO;
12 using System.Linq;
13 using System.Text;
14 using System.Xml;
15 using System.Xml.XPath;
17 using Mono.Cecil;
18 using Mono.Options;
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>(){
30 {"int", "nint"},
31 {"Int32", "nint"},
32 {"System.Int32", "System.nint"},
33 {"uint", "nuint"},
34 {"UInt32", "nuint"},
35 {"System.UInt32", "System.nuint"},
36 {"float", "nfloat"},
37 {"Single", "nfloat"},
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"}
45 };
47 static Dictionary<string, string> fromNativeType = new Dictionary<string,string>(){
49 {"nint", "int"},
50 {"System.nint", "System.Int32"},
51 {"nuint", "uint"},
52 {"System.nuint", "System.UInt32"},
53 {"nfloat", "float"},
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) {
64 string nvalue;
66 bool isOut=false;
67 bool isArray=false;
68 string valueToCompare = StripToComparableType (typename, ref isOut, ref isArray);
70 if (toNativeType.TryGetValue (valueToCompare, out nvalue)) {
72 if (isArray) {
73 nvalue += "[]";
75 if (isOut) {
76 nvalue += "&";
78 return nvalue;
80 return typename;
82 public static string ConvertFromNativeType(string typename) {
83 string nvalue;
85 bool isOut=false;
86 bool isArray=false;
87 string valueToCompare = StripToComparableType (typename, ref isOut, ref isArray);
89 if (fromNativeType.TryGetValue (valueToCompare, out nvalue)) {
90 if (isArray) {
91 nvalue += "[]";
93 if (isOut) {
94 nvalue += "&";
96 return nvalue;
98 // it wasn't one of the native types ... just return it
99 return typename;
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);
107 isArray = true;
109 if (typename.EndsWith ("&")) {
110 valueToCompare = typename.Substring (0, typename.Length - 1);
111 isOut = true;
113 if (typename.Contains ("<")) {
114 // TODO: Need to recursively process generic parameters
116 return valueToCompare;
119 public static string GetTranslatedName(TypeReference t) {
120 string typename = t.FullName;
122 bool isInAssembly = MDocUpdater.IsInAssemblies (t.Module.Name);
123 if (isInAssembly && !typename.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (t)) {
124 string nameWithDropped = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typename);
125 return nameWithDropped;
127 return typename;
130 class MDocUpdater : MDocCommand
132 string srcPath;
133 List<AssemblyDefinition> assemblies;
134 readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
136 string apistyle = string.Empty;
137 bool isClassicRun;
139 bool delete;
140 bool show_exceptions;
141 bool no_assembly_versions, ignore_missing_types;
142 ExceptionLocations? exceptions;
144 internal int additions = 0, deletions = 0;
146 List<DocumentationImporter> importers = new List<DocumentationImporter> ();
148 DocumentationEnumerator docEnum;
150 string since;
152 static readonly MemberFormatter docTypeFormatter = new DocTypeMemberFormatter ();
153 static readonly MemberFormatter filenameFormatter = new FileNameMemberFormatter ();
155 static MemberFormatter[] typeFormatters = new MemberFormatter[]{
156 new CSharpMemberFormatter (),
157 new ILMemberFormatter (),
160 static MemberFormatter[] memberFormatters = new MemberFormatter[]{
161 new CSharpFullMemberFormatter (),
162 new ILFullMemberFormatter (),
165 internal static readonly MemberFormatter slashdocFormatter = new SlashDocMemberFormatter ();
167 MyXmlNodeList extensionMethods = new MyXmlNodeList ();
169 HashSet<string> forwardedTypes = new HashSet<string> ();
171 public static string droppedNamespace = string.Empty;
173 public static bool HasDroppedNamespace(TypeDefinition forType)
175 return HasDroppedNamespace(forType.Module);
178 public static bool HasDroppedNamespace(MemberReference forMember)
180 return HasDroppedNamespace(forMember.Module);
183 public static bool HasDroppedNamespace(AssemblyDefinition forAssembly)
185 return HasDroppedNamespace(forAssembly.MainModule);
188 public static bool HasDroppedNamespace(ModuleDefinition forModule)
190 return !string.IsNullOrWhiteSpace (droppedNamespace) && droppedAssemblies.Any(da => da == forModule.Name);
193 public static bool HasDroppedAnyNamespace ()
195 return !string.IsNullOrWhiteSpace (droppedNamespace);
198 /// <summary>Logic flag to signify that we should list assemblies at the method level, since there are multiple
199 /// assemblies for a given type/method.</summary>
200 public bool IsMultiAssembly {
201 get {
202 return apistyle == "classic" || apistyle == "unified";
206 static List<string> droppedAssemblies = new List<string>();
208 public string PreserveTag { get; set; }
209 public static MDocUpdater Instance { get; private set; }
210 public static bool SwitchingToMagicTypes { get; private set; }
212 public override void Run (IEnumerable<string> args)
214 Instance = this;
215 show_exceptions = DebugOutput;
216 var types = new List<string> ();
217 var p = new OptionSet () {
218 { "delete",
219 "Delete removed members from the XML files.",
220 v => delete = v != null },
221 { "exceptions:",
222 "Document potential exceptions that members can generate. {SOURCES} " +
223 "is a comma-separated list of:\n" +
224 " asm Method calls in same assembly\n" +
225 " depasm Method calls in dependent assemblies\n" +
226 " all Record all possible exceptions\n" +
227 " added Modifier; only create <exception/>s\n" +
228 " for NEW types/members\n" +
229 "If nothing is specified, then only exceptions from the member will " +
230 "be listed.",
231 v => exceptions = ParseExceptionLocations (v) },
232 { "f=",
233 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
234 v => {
235 switch (v) {
236 case "ignore-missing-types":
237 ignore_missing_types = true;
238 break;
239 case "no-assembly-versions":
240 no_assembly_versions = true;
241 break;
242 default:
243 throw new Exception ("Unsupported flag `" + v + "'.");
245 } },
246 { "fignore-missing-types",
247 "Do not report an error if a --type=TYPE type\nwas not found.",
248 v => ignore_missing_types = v != null },
249 { "fno-assembly-versions",
250 "Do not generate //AssemblyVersion elements.",
251 v => no_assembly_versions = v != null },
252 { "i|import=",
253 "Import documentation from {FILE}.",
254 v => AddImporter (v) },
255 { "L|lib=",
256 "Check for assembly references in {DIRECTORY}.",
257 v => assemblyResolver.AddSearchDirectory (v) },
258 { "library=",
259 "Ignored for compatibility with update-ecma-xml.",
260 v => {} },
261 { "o|out=",
262 "Root {DIRECTORY} to generate/update documentation.",
263 v => srcPath = v },
264 { "r=",
265 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
266 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
267 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
268 { "since=",
269 "Manually specify the assembly {VERSION} that new members were added in.",
270 v => since = v },
271 { "type=",
272 "Only update documentation for {TYPE}.",
273 v => types.Add (v) },
274 { "dropns=",
275 "When processing assembly {ASSEMBLY}, strip off leading namespace {PREFIX}:\n" +
276 " e.g. --dropns ASSEMBLY=PREFIX",
277 v => {
278 var parts = v.Split ('=');
279 if (parts.Length != 2) { Console.Error.WriteLine ("Invalid dropns input"); return; }
280 var assembly = Path.GetFileName (parts [0].Trim ());
281 var prefix = parts [1].Trim();
282 droppedAssemblies.Add (assembly);
283 droppedNamespace = prefix;
284 } },
285 { "ntypes",
286 "If the new assembly is switching to 'magic types', then this switch should be defined.",
287 v => SwitchingToMagicTypes = true },
288 { "preserve",
289 "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
290 v => PreserveTag = "true" },
291 { "api-style=",
292 "Denotes the apistyle. Currently, only `classic` and `unified` are supported. `classic` set of assemblies should be run first, immediately followed by 'unified' assemblies with the `dropns` parameter.",
293 v => { apistyle = v.ToLowerInvariant (); }},
295 var assemblies = Parse (p, args, "update",
296 "[OPTIONS]+ ASSEMBLIES",
297 "Create or update documentation from ASSEMBLIES.");
298 if (assemblies == null)
299 return;
300 if (assemblies.Count == 0)
301 Error ("No assemblies specified.");
303 // validation for the api-style parameter
304 if (apistyle == "classic")
305 isClassicRun = true;
306 else if (apistyle == "unified") {
307 if (!droppedAssemblies.Any ())
308 Error ("api-style 'unified' must also supply the 'dropns' parameter with at least one assembly and dropped namespace.");
309 } else if (!string.IsNullOrWhiteSpace (apistyle))
310 Error ("api-style '{0}' is not currently supported", apistyle);
313 foreach (var dir in assemblies
314 .Where (a => a.Contains (Path.DirectorySeparatorChar))
315 .Select (a => Path.GetDirectoryName (a)))
316 assemblyResolver.AddSearchDirectory (dir);
318 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
320 if (srcPath == null)
321 throw new InvalidOperationException("The --out option is required.");
323 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
325 // Store types that have been forwarded to avoid duplicate generation
326 GatherForwardedTypes ();
328 docEnum = docEnum ?? new DocumentationEnumerator ();
330 // PERFORM THE UPDATES
332 if (types.Count > 0) {
333 types.Sort ();
334 DoUpdateTypes (srcPath, types, srcPath);
336 #if false
337 else if (opts.@namespace != null)
338 DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
339 Path.Combine (dest_dir, opts.@namespace));
340 #endif
341 else
342 DoUpdateAssemblies (srcPath, srcPath);
344 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
346 public static bool IsInAssemblies(string name) {
347 var query = Instance.assemblies.Where (a => a.MainModule.Name == name).ToArray ();
348 return query.Length > 0;
350 void AddImporter (string path)
352 try {
353 XmlReader r = new XmlTextReader (path);
354 if (r.Read ()) {
355 while (r.NodeType != XmlNodeType.Element) {
356 if (!r.Read ())
357 Error ("Unable to read XML file: {0}.", path);
359 if (r.LocalName == "doc") {
360 importers.Add (new MsxdocDocumentationImporter (path));
362 else if (r.LocalName == "Libraries") {
363 var ecmadocs = new XmlTextReader (path);
364 docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
365 importers.Add (new EcmaDocumentationImporter (ecmadocs));
367 else
368 Error ("Unsupported XML format within {0}.", path);
370 r.Close ();
371 } catch (Exception e) {
372 Environment.ExitCode = 1;
373 Error ("Could not load XML file: {0}.", e.Message);
377 void GatherForwardedTypes ()
379 foreach (var asm in assemblies)
380 foreach (var type in asm.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName))
381 forwardedTypes.Add (type);
384 static ExceptionLocations ParseExceptionLocations (string s)
386 ExceptionLocations loc = ExceptionLocations.Member;
387 if (s == null)
388 return loc;
389 foreach (var type in s.Split (',')) {
390 switch (type) {
391 case "added": loc |= ExceptionLocations.AddedMembers; break;
392 case "all": loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
393 case "asm": loc |= ExceptionLocations.Assembly; break;
394 case "depasm": loc |= ExceptionLocations.DependentAssemblies; break;
395 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type);
398 return loc;
401 internal void Warning (string format, params object[] args)
403 Message (TraceLevel.Warning, "mdoc: " + format, args);
406 private AssemblyDefinition LoadAssembly (string name)
408 AssemblyDefinition assembly = null;
409 try {
410 assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
411 } catch (System.IO.FileNotFoundException) { }
413 if (assembly == null)
414 throw new InvalidOperationException("Assembly " + name + " not found.");
416 return assembly;
419 private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
420 OrderTypeAttributes (element);
421 XmlTextWriter writer = new XmlTextWriter(output);
422 writer.Formatting = Formatting.Indented;
423 writer.Indentation = 2;
424 writer.IndentChar = ' ';
425 element.WriteTo(writer);
426 output.WriteLine();
429 private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
431 Action<string> creator = file => {
432 using (var writer = OpenWrite (file, mode))
433 action (writer);
436 MdocFile.UpdateFile (filename, creator);
439 private static void OrderTypeAttributes (XmlElement e)
441 foreach (XmlElement type in e.SelectNodes ("//Type")) {
442 OrderTypeAttributes (type.Attributes);
446 static readonly string[] TypeAttributeOrder = {
447 "Name", "FullName", "FullNameSP", "Maintainer"
450 private static void OrderTypeAttributes (XmlAttributeCollection c)
452 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
453 for (int i = 0; i < c.Count; ++i) {
454 XmlAttribute a = c [i];
455 for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
456 if (a.Name == TypeAttributeOrder [j]) {
457 attrs [j] = a;
458 break;
462 for (int i = attrs.Length-1; i >= 0; --i) {
463 XmlAttribute n = attrs [i];
464 if (n == null)
465 continue;
466 XmlAttribute r = null;
467 for (int j = i+1; j < attrs.Length; ++j) {
468 if (attrs [j] != null) {
469 r = attrs [j];
470 break;
473 if (r == null)
474 continue;
475 if (c [n.Name] != null) {
476 c.RemoveNamedItem (n.Name);
477 c.InsertBefore (n, r);
482 private XmlDocument CreateIndexStub()
484 XmlDocument index = new XmlDocument();
486 XmlElement index_root = index.CreateElement("Overview");
487 index.AppendChild(index_root);
489 if (assemblies.Count == 0)
490 throw new Exception ("No assembly");
492 XmlElement index_assemblies = index.CreateElement("Assemblies");
493 index_root.AppendChild(index_assemblies);
495 XmlElement index_remarks = index.CreateElement("Remarks");
496 index_remarks.InnerText = "To be added.";
497 index_root.AppendChild(index_remarks);
499 XmlElement index_copyright = index.CreateElement("Copyright");
500 index_copyright.InnerText = "To be added.";
501 index_root.AppendChild(index_copyright);
503 XmlElement index_types = index.CreateElement("Types");
504 index_root.AppendChild(index_types);
506 return index;
509 private static void WriteNamespaceStub(string ns, string outdir) {
510 XmlDocument index = new XmlDocument();
512 XmlElement index_root = index.CreateElement("Namespace");
513 index.AppendChild(index_root);
515 index_root.SetAttribute("Name", ns);
517 XmlElement index_docs = index.CreateElement("Docs");
518 index_root.AppendChild(index_docs);
520 XmlElement index_summary = index.CreateElement("summary");
521 index_summary.InnerText = "To be added.";
522 index_docs.AppendChild(index_summary);
524 XmlElement index_remarks = index.CreateElement("remarks");
525 index_remarks.InnerText = "To be added.";
526 index_docs.AppendChild(index_remarks);
528 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew,
529 writer => WriteXml (index.DocumentElement, writer));
532 public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
534 var index = CreateIndexForTypes (dest);
536 var found = new HashSet<string> ();
537 foreach (AssemblyDefinition assembly in assemblies) {
538 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
539 string relpath = DoUpdateType (type, basepath, dest);
540 if (relpath == null)
541 continue;
543 found.Add (type.FullName);
545 if (index == null)
546 continue;
548 index.Add (assembly);
549 index.Add (type);
553 if (index != null)
554 index.Write ();
556 if (ignore_missing_types)
557 return;
559 var notFound = from n in typenames where !found.Contains (n) select n;
560 if (notFound.Any ())
561 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
564 class IndexForTypes {
566 MDocUpdater app;
567 string indexFile;
569 XmlDocument index;
570 XmlElement index_types;
571 XmlElement index_assemblies;
573 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
575 this.app = app;
576 this.indexFile = indexFile;
577 this.index = index;
579 index_types = WriteElement (index.DocumentElement, "Types");
580 index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
583 public void Add (AssemblyDefinition assembly)
585 if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
586 return;
588 app.AddIndexAssembly (assembly, index_assemblies);
591 public void Add (TypeDefinition type)
593 app.AddIndexType (type, index_types);
596 public void Write ()
598 SortIndexEntries (index_types);
599 WriteFile (indexFile, FileMode.Create,
600 writer => WriteXml (index.DocumentElement, writer));
604 IndexForTypes CreateIndexForTypes (string dest)
606 string indexFile = Path.Combine (dest, "index.xml");
607 if (File.Exists (indexFile))
608 return null;
609 return new IndexForTypes (this, indexFile, CreateIndexStub ());
612 /// <summary>Constructs the presumed path to the type's documentation file</summary>
613 /// <returns><c>true</c>, if the type file was found, <c>false</c> otherwise.</returns>
614 /// <param name="result">A typle that contains 1) the 'reltypefile', 2) the 'typefile', and 3) the file info</param>
615 bool TryFindTypeFile(string nsname, string typename, string basepath, out Tuple<string, string, FileInfo> result) {
616 string reltypefile = DocUtils.PathCombine (nsname, typename + ".xml");
617 string typefile = Path.Combine (basepath, reltypefile);
618 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
620 result = new Tuple<string, string, FileInfo> (reltypefile, typefile, file);
622 return file.Exists;
625 public string DoUpdateType (TypeDefinition type, string basepath, string dest)
627 if (type.Namespace == null)
628 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
629 type.FullName);
630 if (!IsPublic (type))
631 return null;
633 // Must get the A+B form of the type name.
634 string typename = GetTypeFileName(type);
635 string nsname = DocUtils.GetNamespace (type);
637 // Find the file, if it exists
638 string[] searchLocations = new string[] {
639 nsname
642 if (MDocUpdater.HasDroppedNamespace (type)) {
643 // If dropping namespace, types may have moved into a couple of different places.
644 var newSearchLocations = searchLocations.Union (new string[] {
645 string.Format ("{0}.{1}", droppedNamespace, nsname),
646 nsname.Replace (droppedNamespace + ".", string.Empty),
647 MDocUpdater.droppedNamespace
650 searchLocations = newSearchLocations.ToArray ();
653 string reltypefile="", typefile="";
654 System.IO.FileInfo file = null;
656 foreach (var f in searchLocations) {
657 Tuple<string, string, FileInfo> result;
658 bool fileExists = TryFindTypeFile (f, typename, basepath, out result);
660 if (fileExists) {
661 reltypefile = result.Item1;
662 typefile = result.Item2;
663 file = result.Item3;
665 break;
669 if (file == null || !file.Exists) {
670 // we were not able to find a file, let's use the original type informatio.
671 // so that we create the stub in the right place.
672 Tuple<string, string, FileInfo> result;
673 TryFindTypeFile (nsname, typename, basepath, out result);
675 reltypefile = result.Item1;
676 typefile = result.Item2;
677 file = result.Item3;
680 string output = null;
681 if (dest == null) {
682 output = typefile;
683 } else if (dest == "-") {
684 output = null;
685 } else {
686 output = Path.Combine (dest, reltypefile);
689 if (file != null && file.Exists) {
690 // Update
691 XmlDocument basefile = new XmlDocument();
692 try {
693 basefile.Load(typefile);
694 } catch (Exception e) {
695 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
698 DoUpdateType2("Updating", basefile, type, output, false);
699 } else {
700 // Stub
701 XmlElement td = StubType(type, output);
702 if (td == null)
703 return null;
705 return reltypefile;
708 public void DoUpdateNS (string ns, string nspath, string outpath)
710 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
711 AssemblyDefinition assembly = assemblies [0];
713 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
714 XmlDocument basefile = new XmlDocument();
715 string typefile = Path.Combine(nspath, file.Name);
716 try {
717 basefile.Load(typefile);
718 } catch (Exception e) {
719 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
722 string typename =
723 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
724 TypeDefinition type = assembly.GetType(typename);
725 if (type == null) {
726 // --
727 if (!string.IsNullOrWhiteSpace (droppedNamespace)) {
728 string nameWithNs = string.Format ("{0}.{1}", droppedNamespace, typename);
729 type = assembly.GetType (nameWithNs);
730 if (type == null) {
731 Warning ("Type no longer in assembly: " + typename);
732 continue;
735 //--
738 seenTypes[type] = seenTypes;
739 DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
742 // Stub types not in the directory
743 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
744 if (type.Namespace != ns || seenTypes.ContainsKey(type))
745 continue;
747 XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
748 if (td == null) continue;
752 private static string GetTypeFileName (TypeReference type)
754 return filenameFormatter.GetName (type);
757 public static string GetTypeFileName (string typename)
759 StringBuilder filename = new StringBuilder (typename.Length);
760 int numArgs = 0;
761 int numLt = 0;
762 bool copy = true;
763 for (int i = 0; i < typename.Length; ++i) {
764 char c = typename [i];
765 switch (c) {
766 case '<':
767 copy = false;
768 ++numLt;
769 break;
770 case '>':
771 --numLt;
772 if (numLt == 0) {
773 filename.Append ('`').Append ((numArgs+1).ToString());
774 numArgs = 0;
775 copy = true;
777 break;
778 case ',':
779 if (numLt == 1)
780 ++numArgs;
781 break;
782 default:
783 if (copy)
784 filename.Append (c);
785 break;
788 return filename.ToString ();
791 private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
793 XmlElement index_assembly = null;
794 if (IsMultiAssembly)
795 index_assembly = (XmlElement)parent.SelectSingleNode ("Assembly[@Name='"+ assembly.Name.Name +"']");
797 if (index_assembly == null)
798 index_assembly = parent.OwnerDocument.CreateElement ("Assembly");
800 index_assembly.SetAttribute ("Name", assembly.Name.Name);
801 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
803 AssemblyNameDefinition name = assembly.Name;
804 if (name.HasPublicKey) {
805 XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
806 var key = new StringBuilder (name.PublicKey.Length*3 + 2);
807 key.Append ("[");
808 foreach (byte b in name.PublicKey)
809 key.AppendFormat ("{0,2:x2} ", b);
810 key.Append ("]");
811 pubkey.InnerText = key.ToString ();
812 index_assembly.AppendChild (pubkey);
815 if (!string.IsNullOrEmpty (name.Culture)) {
816 XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
817 culture.InnerText = name.Culture;
818 index_assembly.AppendChild (culture);
821 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
822 parent.AppendChild(index_assembly);
825 private void AddIndexType (TypeDefinition type, XmlElement index_types)
827 string typename = GetTypeFileName(type);
829 // Add namespace and type nodes into the index file as needed
830 string ns = DocUtils.GetNamespace (type);
831 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
832 if (nsnode == null) {
833 nsnode = index_types.OwnerDocument.CreateElement("Namespace");
834 nsnode.SetAttribute ("Name", ns);
835 index_types.AppendChild (nsnode);
837 string doc_typename = GetDocTypeName (type);
838 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
839 if (typenode == null) {
840 typenode = index_types.OwnerDocument.CreateElement ("Type");
841 typenode.SetAttribute ("Name", typename);
842 nsnode.AppendChild (typenode);
844 if (typename != doc_typename)
845 typenode.SetAttribute("DisplayName", doc_typename);
846 else
847 typenode.RemoveAttribute("DisplayName");
849 typenode.SetAttribute ("Kind", GetTypeKind (type));
852 private void DoUpdateAssemblies (string source, string dest)
854 string indexfile = dest + "/index.xml";
855 XmlDocument index;
856 if (System.IO.File.Exists(indexfile)) {
857 index = new XmlDocument();
858 index.Load(indexfile);
860 // Format change
861 ClearElement(index.DocumentElement, "Assembly");
862 ClearElement(index.DocumentElement, "Attributes");
863 } else {
864 index = CreateIndexStub();
867 string defaultTitle = "Untitled";
868 if (assemblies.Count == 1)
869 defaultTitle = assemblies[0].Name.Name;
870 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
872 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
873 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
874 if (!IsMultiAssembly)
875 index_assemblies.RemoveAll ();
878 HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
880 foreach (AssemblyDefinition assm in assemblies) {
881 AddIndexAssembly (assm, index_assemblies);
882 DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
885 SortIndexEntries (index_types);
887 CleanupFiles (dest, goodfiles);
888 CleanupIndexTypes (index_types, goodfiles);
889 CleanupExtensions (index_types);
891 WriteFile (indexfile, FileMode.Create,
892 writer => WriteXml(index.DocumentElement, writer));
895 private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
897 private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles)
899 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
900 string typename = GetTypeFileName(type);
901 if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0 || forwardedTypes.Contains (type.FullName))
902 continue;
904 string reltypepath = DoUpdateType (type, source, dest);
905 if (reltypepath == null)
906 continue;
908 // Add namespace and type nodes into the index file as needed
909 AddIndexType (type, index_types);
911 // Ensure the namespace index file exists
912 string namespaceToUse = type.Namespace;
913 if (HasDroppedNamespace(assembly)) {
914 namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
916 string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
917 string nsdoc = DocUtils.PathCombine (dest, "ns-" + namespaceToUse + ".xml");
918 if (File.Exists (onsdoc)) {
919 File.Move (onsdoc, nsdoc);
922 if (!File.Exists (nsdoc)) {
923 Console.WriteLine("New Namespace File: " + type.Namespace);
924 WriteNamespaceStub(namespaceToUse, dest);
927 goodfiles.Add (reltypepath);
931 private static void SortIndexEntries (XmlElement indexTypes)
933 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
934 XmlNodeComparer c = new AttributeNameComparer ();
935 SortXmlNodes (indexTypes, namespaces, c);
937 for (int i = 0; i < namespaces.Count; ++i)
938 SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
941 private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
943 MyXmlNodeList l = new MyXmlNodeList (children.Count);
944 for (int i = 0; i < children.Count; ++i)
945 l.Add (children [i]);
946 l.Sort (comparer);
947 for (int i = l.Count - 1; i > 0; --i) {
948 parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
952 abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
954 public abstract int Compare (XmlNode x, XmlNode y);
956 public int Compare (object x, object y)
958 return Compare ((XmlNode) x, (XmlNode) y);
962 class AttributeNameComparer : XmlNodeComparer {
963 string attribute;
965 public AttributeNameComparer ()
966 : this ("Name")
970 public AttributeNameComparer (string attribute)
972 this.attribute = attribute;
975 public override int Compare (XmlNode x, XmlNode y)
977 return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
981 class VersionComparer : XmlNodeComparer {
982 public override int Compare (XmlNode x, XmlNode y)
984 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
985 string a = GetVersion (x.InnerText);
986 string b = GetVersion (y.InnerText);
987 return new Version (a).CompareTo (new Version (b));
990 static string GetVersion (string v)
992 int n = v.IndexOf ("x");
993 if (n < 0)
994 return v;
995 return v.Substring (0, n-1);
999 private static string GetTypeKind (TypeDefinition type)
1001 if (type.IsEnum)
1002 return "Enumeration";
1003 if (type.IsValueType)
1004 return "Structure";
1005 if (type.IsInterface)
1006 return "Interface";
1007 if (DocUtils.IsDelegate (type))
1008 return "Delegate";
1009 if (type.IsClass || type.FullName == "System.Enum") // FIXME
1010 return "Class";
1011 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
1014 public static bool IsPublic (TypeDefinition type)
1016 TypeDefinition decl = type;
1017 while (decl != null) {
1018 if (!(decl.IsPublic || decl.IsNestedPublic ||
1019 decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly)) {
1020 return false;
1022 decl = (TypeDefinition) decl.DeclaringType;
1024 return true;
1027 private void CleanupFiles (string dest, HashSet<string> goodfiles)
1029 // Look for files that no longer correspond to types
1030 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
1031 foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
1032 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
1033 if (!goodfiles.Contains (relTypeFile)) {
1034 XmlDocument doc = new XmlDocument ();
1035 doc.Load (typefile.FullName);
1036 XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
1037 var assemblyNameNode = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName");
1038 if (assemblyNameNode == null){
1039 Warning ("Did not find /Type/AssemblyInfo/AssemblyName on {0}", typefile.FullName);
1040 continue;
1042 string assemblyName = assemblyNameNode.InnerText;
1043 AssemblyDefinition assembly = assemblies.FirstOrDefault (a => a.Name.Name == assemblyName);
1045 Action saveDoc = () => {
1046 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
1047 WriteXml(doc.DocumentElement, writer);
1050 if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions (e, assembly, GetAssemblyVersions(assemblyName), false)) {
1051 saveDoc ();
1052 goodfiles.Add (relTypeFile);
1053 continue;
1056 Action actuallyDelete = () => {
1057 string newname = typefile.FullName + ".remove";
1058 try { System.IO.File.Delete (newname); } catch (Exception) { Warning ("Unable to delete existing file: {0}", newname); }
1059 try { typefile.MoveTo (newname); } catch (Exception) { Warning ("Unable to rename to: {0}", newname); }
1060 Console.WriteLine ("Class no longer present; file renamed: " + Path.Combine (nsdir.Name, typefile.Name));
1063 if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was not a -preserve
1064 saveDoc ();
1066 var unifiedAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='unified']");
1067 var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[not(@apistyle) or @apistyle='classic']");
1068 var unifiedMembers = doc.SelectNodes ("//Member[@apistyle='unified']|//Member/AssemblyInfo[@apistyle='unified']");
1069 var classicMembers = doc.SelectNodes ("//Member[@apistyle='classic']|//Member/AssemblyInfo[@apistyle='classic']");
1070 bool isUnifiedRun = HasDroppedAnyNamespace ();
1071 bool isClassicOrNormalRun = !isUnifiedRun;
1073 Action<XmlNode, ApiStyle> removeStyles = (x, style) => {
1074 var styledNodes = doc.SelectNodes("//*[@apistyle='"+ style.ToString ().ToLowerInvariant () +"']");
1075 if (styledNodes != null && styledNodes.Count > 0) {
1076 foreach(var node in styledNodes.Cast<XmlNode> ()) {
1077 node.ParentNode.RemoveChild (node);
1080 saveDoc ();
1082 if (isClassicOrNormalRun) {
1083 if (unifiedAssemblyNode != null || unifiedMembers.Count > 0) {
1084 Warning ("*** this type is marked as unified, not deleting during this run: {0}", typefile.FullName);
1085 // if truly removed from both assemblies, it will be removed fully during the unified run
1086 removeStyles (doc, ApiStyle.Classic);
1087 continue;
1088 } else {
1089 // we should be safe to delete here because it was not marked as a unified assembly
1090 actuallyDelete ();
1093 if (isUnifiedRun) {
1094 if (classicAssemblyNode != null || classicMembers.Count > 0) {
1095 Warning ("*** this type is marked as classic, not deleting {0}", typefile.FullName);
1096 continue;
1097 } else {
1098 // safe to delete because it wasn't marked as a classic assembly, so the type is gone in both.
1099 actuallyDelete ();
1108 private static TextWriter OpenWrite (string path, FileMode mode)
1110 var w = new StreamWriter (
1111 new FileStream (path, mode),
1112 new UTF8Encoding (false)
1114 w.NewLine = "\n";
1115 return w;
1118 private string[] GetAssemblyVersions (string assemblyName)
1120 return (from a in assemblies
1121 where a.Name.Name == assemblyName
1122 select GetAssemblyVersion (a)).ToArray ();
1125 private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
1127 // Look for type nodes that no longer correspond to types
1128 MyXmlNodeList remove = new MyXmlNodeList ();
1129 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
1130 string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
1131 if (!goodfiles.Contains (fulltypename)) {
1132 remove.Add (typenode);
1135 foreach (XmlNode n in remove)
1136 n.ParentNode.RemoveChild (n);
1139 private void CleanupExtensions (XmlElement index_types)
1141 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
1142 if (extensionMethods.Count == 0) {
1143 if (e == null)
1144 return;
1145 index_types.SelectSingleNode ("/Overview").RemoveChild (e);
1146 return;
1148 if (e == null) {
1149 e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
1150 index_types.SelectSingleNode ("/Overview").AppendChild (e);
1152 else
1153 e.RemoveAll ();
1154 extensionMethods.Sort (DefaultExtensionMethodComparer);
1155 foreach (XmlNode m in extensionMethods) {
1156 e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
1160 class ExtensionMethodComparer : XmlNodeComparer {
1161 public override int Compare (XmlNode x, XmlNode y)
1163 XmlNode xLink = x.SelectSingleNode ("Member/Link");
1164 XmlNode yLink = y.SelectSingleNode ("Member/Link");
1166 int n = xLink.Attributes ["Type"].Value.CompareTo (
1167 yLink.Attributes ["Type"].Value);
1168 if (n != 0)
1169 return n;
1170 n = xLink.Attributes ["Member"].Value.CompareTo (
1171 yLink.Attributes ["Member"].Value);
1172 if (n == 0 && !object.ReferenceEquals (x, y))
1173 throw new InvalidOperationException ("Duplicate extension method found!");
1174 return n;
1178 static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
1180 public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
1182 Console.WriteLine(message + ": " + type.FullName);
1184 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
1186 // Update type metadata
1187 UpdateType(basefile.DocumentElement, type);
1189 // Update existing members. Delete member nodes that no longer should be there,
1190 // and remember what members are already documented so we don't add them again.
1192 MyXmlNodeList todelete = new MyXmlNodeList ();
1194 foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
1195 XmlElement oldmember = info.Node;
1196 MemberReference oldmember2 = info.Member;
1198 if (info.Member != null && info.Node != null) {
1199 // Check for an error condition where the xml MemberName doesn't match the matched member
1200 var memberName = GetMemberName (info.Member);
1201 var memberAttribute = info.Node.Attributes ["MemberName"];
1202 if (memberAttribute == null || (memberAttribute.Value != memberName && memberAttribute.Value.Split (',').Length != memberName.Split (',').Length)) {
1203 oldmember.SetAttribute ("MemberName", memberName);
1207 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
1209 // Interface implementations and overrides are deleted from the docs
1210 // unless the overrides option is given.
1211 if (oldmember2 != null && sig == null)
1212 oldmember2 = null;
1214 // Deleted (or signature changed)
1215 if (oldmember2 == null) {
1216 if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
1217 continue;
1219 DeleteMember ("Member Removed", output, oldmember, todelete, type);
1220 continue;
1223 // Duplicated
1224 if (seenmembers.ContainsKey (sig)) {
1225 if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
1226 // ignore, already seen
1228 else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
1229 DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
1230 else
1231 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
1232 continue;
1235 // Update signature information
1236 UpdateMember(info);
1238 // get all apistyles of sig from info.Node
1239 var styles = oldmember.GetElementsByTagName ("MemberSignature").Cast<XmlElement> ()
1240 .Where (x => x.GetAttribute ("Language") == "C#" && !seenmembers.ContainsKey(x.GetAttribute("Value")))
1241 .Select (x => x.GetAttribute ("Value"));
1243 foreach (var stylesig in styles) {
1244 seenmembers.Add (stylesig, oldmember);
1247 foreach (XmlElement oldmember in todelete)
1248 oldmember.ParentNode.RemoveChild (oldmember);
1251 if (!DocUtils.IsDelegate (type)) {
1252 XmlNode members = WriteElement (basefile.DocumentElement, "Members");
1253 var typemembers = type.GetMembers()
1254 .Where(m => {
1255 if (m is TypeDefinition) return false;
1256 string sig = memberFormatters [0].GetDeclaration (m);
1257 if (sig == null) return false;
1258 if (seenmembers.ContainsKey(sig)) return false;
1260 // Verify that the member isn't an explicitly implemented
1261 // member of an internal interface, in which case we shouldn't return true.
1262 MethodDefinition methdef = null;
1263 if (m is MethodDefinition)
1264 methdef = m as MethodDefinition;
1265 else if (m is PropertyDefinition) {
1266 var prop = m as PropertyDefinition;
1267 methdef = prop.GetMethod ?? prop.SetMethod;
1270 if (methdef != null) {
1271 TypeReference iface;
1272 MethodReference imethod;
1274 if (methdef.Overrides.Count == 1) {
1275 DocUtils.GetInfoForExplicitlyImplementedMethod (methdef, out iface, out imethod);
1276 if (!IsPublic (iface.Resolve ())) return false;
1280 return true;
1282 .ToArray();
1283 foreach (MemberReference m in typemembers) {
1284 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
1285 if (mm == null) continue;
1287 if (MDocUpdater.SwitchingToMagicTypes || MDocUpdater.HasDroppedNamespace (m)) {
1288 // this is a unified style API that obviously doesn't exist in the classic API. Let's mark
1289 // it with apistyle="unified", so that it's not displayed for classic style APIs
1290 mm.AddApiStyle (ApiStyle.Unified);
1293 members.AppendChild( mm );
1295 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
1296 additions++;
1300 // Import code snippets from files
1301 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
1302 if (!(code is XmlElement)) continue;
1303 string file = ((XmlElement)code).GetAttribute("src");
1304 string lang = ((XmlElement)code).GetAttribute("lang");
1305 if (file != "") {
1306 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
1307 if (src != null)
1308 code.InnerText = src;
1312 if (insertSince && since != null) {
1313 XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
1314 docs.AppendChild (CreateSinceNode (basefile));
1317 do {
1318 XmlElement d = basefile.DocumentElement ["Docs"];
1319 XmlElement m = basefile.DocumentElement ["Members"];
1320 if (d != null && m != null)
1321 basefile.DocumentElement.InsertBefore (
1322 basefile.DocumentElement.RemoveChild (d), m);
1323 SortTypeMembers (m);
1324 } while (false);
1326 if (output == null)
1327 WriteXml(basefile.DocumentElement, Console.Out);
1328 else {
1329 FileInfo file = new FileInfo (output);
1330 if (!file.Directory.Exists) {
1331 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
1332 file.Directory.Create ();
1334 WriteFile (output, FileMode.Create,
1335 writer => WriteXml(basefile.DocumentElement, writer));
1339 private string GetCodeSource (string lang, string file)
1341 int anchorStart;
1342 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
1343 // Grab the specified region
1344 string region = "#region " + file.Substring (anchorStart + 4);
1345 file = file.Substring (0, anchorStart + 3);
1346 try {
1347 using (StreamReader reader = new StreamReader (file)) {
1348 string line;
1349 StringBuilder src = new StringBuilder ();
1350 int indent = -1;
1351 while ((line = reader.ReadLine ()) != null) {
1352 if (line.Trim() == region) {
1353 indent = line.IndexOf (region);
1354 continue;
1356 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
1357 break;
1359 if (indent >= 0)
1360 src.Append (
1361 (line.Length > 0 ? line.Substring (indent) : string.Empty) +
1362 "\n");
1364 return src.ToString ();
1366 } catch (Exception e) {
1367 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
1368 file, region, show_exceptions ? e.ToString () : e.Message);
1369 return null;
1372 try {
1373 using (StreamReader reader = new StreamReader (file))
1374 return reader.ReadToEnd ();
1375 } catch (Exception e) {
1376 Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1378 return null;
1381 void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete, TypeDefinition type)
1383 string format = output != null
1384 ? "{0}: File='{1}'; Signature='{4}'"
1385 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1386 string signature = member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value;
1387 Warning (format,
1388 reason,
1389 output,
1390 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1391 member.Attributes ["MemberName"].Value,
1392 signature);
1394 // Identify all of the different states that could affect our decision to delete the member
1395 bool shouldPreserve = !string.IsNullOrWhiteSpace (PreserveTag);
1396 bool hasContent = MemberDocsHaveUserContent (member);
1397 bool shouldDelete = !shouldPreserve && (delete || !hasContent);
1399 bool unifiedRun = HasDroppedNamespace (type);
1401 var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
1402 bool nodeIsClassic = classicAssemblyInfo != null || member.HasApiStyle (ApiStyle.Classic);
1403 var unifiedAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
1404 bool nodeIsUnified = unifiedAssemblyInfo != null || member.HasApiStyle (ApiStyle.Unified);
1406 Action actuallyDelete = () => {
1407 todelete.Add (member);
1408 deletions++;
1411 if (!shouldDelete) {
1412 // explicitly not deleting
1413 string message = shouldPreserve ?
1414 "Not deleting '{0}' due to --preserve." :
1415 "Not deleting '{0}'; must be enabled with the --delete option";
1416 Warning (message, signature);
1417 } else if (unifiedRun && nodeIsClassic) {
1418 // this is a unified run, and the member doesn't exist, but is marked as being in the classic assembly.
1419 member.RemoveApiStyle (ApiStyle.Unified);
1420 member.AddApiStyle (ApiStyle.Classic);
1421 Warning ("Not removing '{0}' since it's still in the classic assembly.", signature);
1422 } else if (unifiedRun && !nodeIsClassic) {
1423 // unified run, and the node is not classic, which means it doesn't exist anywhere.
1424 actuallyDelete ();
1425 } else {
1426 if (!isClassicRun || (isClassicRun && !nodeIsClassic && !nodeIsUnified)) { // regular codepath (ie. not classic/unified)
1427 actuallyDelete ();
1428 } else { // this is a classic run
1429 Warning ("Removing classic from '{0}' ... will be removed in the unified run if not present there.", signature);
1430 member.RemoveApiStyle (ApiStyle.Classic);
1431 if (classicAssemblyInfo != null) {
1432 member.RemoveChild (classicAssemblyInfo);
1438 class MemberComparer : XmlNodeComparer {
1439 public override int Compare (XmlNode x, XmlNode y)
1441 int r;
1442 string xMemberName = x.Attributes ["MemberName"].Value;
1443 string yMemberName = y.Attributes ["MemberName"].Value;
1445 // generic methods *end* with '>'
1446 // it's possible for explicitly implemented generic interfaces to
1447 // contain <...> without being a generic method
1448 if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1449 (r = xMemberName.CompareTo (yMemberName)) != 0)
1450 return r;
1452 int lt;
1453 if ((lt = xMemberName.IndexOf ("<")) >= 0)
1454 xMemberName = xMemberName.Substring (0, lt);
1455 if ((lt = yMemberName.IndexOf ("<")) >= 0)
1456 yMemberName = yMemberName.Substring (0, lt);
1457 if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1458 return r;
1460 // if @MemberName matches, then it's either two different types of
1461 // members sharing the same name, e.g. field & property, or it's an
1462 // overloaded method.
1463 // for different type, sort based on MemberType value.
1464 r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1465 y.SelectSingleNode ("MemberType").InnerText);
1466 if (r != 0)
1467 return r;
1469 // same type -- must be an overloaded method. Sort based on type
1470 // parameter count, then parameter count, then by the parameter
1471 // type names.
1472 XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1473 XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1474 if (xTypeParams.Count != yTypeParams.Count)
1475 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1476 for (int i = 0; i < xTypeParams.Count; ++i) {
1477 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1478 yTypeParams [i].Attributes ["Name"].Value);
1479 if (r != 0)
1480 return r;
1483 XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1484 XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1485 if (xParams.Count != yParams.Count)
1486 return xParams.Count <= yParams.Count ? -1 : 1;
1487 for (int i = 0; i < xParams.Count; ++i) {
1488 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1489 yParams [i].Attributes ["Type"].Value);
1490 if (r != 0)
1491 return r;
1493 // all parameters match, but return value might not match if it was
1494 // changed between one version and another.
1495 XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1496 XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1497 if (xReturn != null && yReturn != null) {
1498 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1499 if (r != 0)
1500 return r;
1503 return 0;
1507 static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1509 private static void SortTypeMembers (XmlNode members)
1511 if (members == null)
1512 return;
1513 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1516 private static bool MemberDocsHaveUserContent (XmlNode e)
1518 e = (XmlElement)e.SelectSingleNode("Docs");
1519 if (e == null) return false;
1520 foreach (XmlElement d in e.SelectNodes("*"))
1521 if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1522 return true;
1523 return false;
1526 // UPDATE HELPER FUNCTIONS
1528 // CREATE A STUB DOCUMENTATION FILE
1530 public XmlElement StubType (TypeDefinition type, string output)
1532 string typesig = typeFormatters [0].GetDeclaration (type);
1533 if (typesig == null) return null; // not publicly visible
1535 XmlDocument doc = new XmlDocument();
1536 XmlElement root = doc.CreateElement("Type");
1537 doc.AppendChild (root);
1539 DoUpdateType2 ("New Type", doc, type, output, true);
1541 return root;
1544 private XmlElement CreateSinceNode (XmlDocument doc)
1546 XmlElement s = doc.CreateElement ("since");
1547 s.SetAttribute ("version", since);
1548 return s;
1551 // STUBBING/UPDATING FUNCTIONS
1553 public void UpdateType (XmlElement root, TypeDefinition type)
1555 root.SetAttribute("Name", GetDocTypeName (type));
1556 root.SetAttribute("FullName", GetDocTypeFullName (type));
1558 foreach (MemberFormatter f in typeFormatters) {
1559 string element = "TypeSignature[@Language='" + f.Language + "']";
1560 string valueToUse = f.GetDeclaration (type);
1562 AddXmlNode (
1563 root.SelectNodes (element).Cast<XmlElement> ().ToArray (),
1564 x => x.GetAttribute ("Value") == valueToUse,
1565 x => x.SetAttribute ("Value", valueToUse),
1566 () => {
1567 var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
1568 var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
1569 return newnode;
1571 type);
1574 AddAssemblyNameToNode (root, type);
1576 string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
1577 Func<XmlElement, bool> assemblyFilter = x => x.SelectSingleNode ("AssemblyName").InnerText == type.Module.Assembly.Name.Name;
1578 foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().Where (assemblyFilter))
1580 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1581 if (!no_assembly_versions) {
1582 UpdateAssemblyVersions (ass, type, true);
1584 else {
1585 var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1586 foreach (var version in versions)
1587 ass.RemoveChild (version);
1589 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1590 WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1591 else
1592 ClearElement(ass, "AssemblyCulture");
1595 // Why-oh-why do we put assembly attributes in each type file?
1596 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1597 // since they're outdated in current docs, and a waste of space.
1598 //MakeAttributes(ass, type.Assembly, true);
1599 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1600 if (assattrs != null)
1601 ass.RemoveChild(assattrs);
1603 NormalizeWhitespace(ass);
1606 if (type.IsGenericType ()) {
1607 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1608 } else {
1609 ClearElement(root, "TypeParameters");
1612 if (type.BaseType != null) {
1613 XmlElement basenode = WriteElement(root, "Base");
1615 string basetypename = GetDocTypeFullName (type.BaseType);
1616 if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1617 WriteElementText(root, "Base/BaseTypeName", basetypename);
1619 // Document how this type instantiates the generic parameters of its base type
1620 TypeReference origBase = type.BaseType.GetElementType ();
1621 if (origBase.IsGenericType ()) {
1622 ClearElement(basenode, "BaseTypeArguments");
1623 GenericInstanceType baseInst = type.BaseType as GenericInstanceType;
1624 IList<TypeReference> baseGenArgs = baseInst == null ? null : baseInst.GenericArguments;
1625 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1626 if (baseGenArgs.Count != baseGenParams.Count)
1627 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1628 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1629 GenericParameter param = baseGenParams [i];
1630 TypeReference value = baseGenArgs [i];
1632 XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1633 XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1634 bta.AppendChild(arg);
1635 arg.SetAttribute ("TypeParamName", param.Name);
1636 arg.InnerText = GetDocTypeFullName (value);
1639 } else {
1640 ClearElement(root, "Base");
1643 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1644 IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1645 List<string> interface_names = userInterfaces
1646 .Select (iface => GetDocTypeFullName (iface))
1647 .OrderBy (s => s)
1648 .ToList ();
1650 XmlElement interfaces = WriteElement(root, "Interfaces");
1651 interfaces.RemoveAll();
1652 foreach (string iname in interface_names) {
1653 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1654 interfaces.AppendChild(iface);
1655 WriteElementText(iface, "InterfaceName", iname);
1657 } else {
1658 ClearElement(root, "Interfaces");
1661 MakeAttributes (root, GetCustomAttributes (type), type);
1663 if (DocUtils.IsDelegate (type)) {
1664 MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
1665 var member = type.GetMethod ("Invoke");
1666 MakeParameters(root, member, member.Parameters);
1667 MakeReturnValue(root, member);
1670 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1671 MakeDocNode (typeInfo);
1673 if (!DocUtils.IsDelegate (type))
1674 WriteElement (root, "Members");
1676 OrderTypeNodes (root, root.ChildNodes);
1677 NormalizeWhitespace(root);
1680 /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
1681 /// <returns>The assembly that was either added, or was already present</returns>
1682 XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type)
1684 return AddAssemblyNameToNode (root, type.Module);
1687 /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
1688 /// <returns>The assembly that was either added, or was already present</returns>
1689 XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module)
1691 Func<XmlElement, bool> assemblyFilter = x => {
1692 var existingName = x.SelectSingleNode ("AssemblyName");
1694 bool apiStyleMatches = true;
1695 string currentApiStyle = x.GetAttribute ("apistyle");
1696 if ((HasDroppedNamespace (module) && !string.IsNullOrWhiteSpace (currentApiStyle) && currentApiStyle != "unified") ||
1697 (isClassicRun && (string.IsNullOrWhiteSpace (currentApiStyle) || currentApiStyle != "classic"))) {
1698 apiStyleMatches = false;
1700 return apiStyleMatches && (existingName == null || (existingName != null && existingName.InnerText == module.Assembly.Name.Name));
1703 return AddAssemblyXmlNode (
1704 root.SelectNodes ("AssemblyInfo").Cast<XmlElement> ().ToArray (),
1705 assemblyFilter, x => WriteElementText (x, "AssemblyName", module.Assembly.Name.Name),
1706 () => {
1707 XmlElement ass = WriteElement (root, "AssemblyInfo", forceNewElement: true);
1709 if (MDocUpdater.HasDroppedNamespace (module))
1710 ass.AddApiStyle (ApiStyle.Unified);
1711 if (isClassicRun)
1712 ass.AddApiStyle (ApiStyle.Classic);
1713 return ass;
1714 }, module);
1717 static readonly string[] TypeNodeOrder = {
1718 "TypeSignature",
1719 "MemberOfLibrary",
1720 "AssemblyInfo",
1721 "ThreadingSafetyStatement",
1722 "ThreadSafetyStatement",
1723 "TypeParameters",
1724 "Base",
1725 "Interfaces",
1726 "Attributes",
1727 "Parameters",
1728 "ReturnValue",
1729 "Docs",
1730 "Members",
1731 "TypeExcluded",
1734 static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1736 ReorderNodes (member, children, TypeNodeOrder);
1739 internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1741 List<T> l = new List<T> (list);
1742 l.Sort ();
1743 return l;
1746 private void UpdateMember (DocsNodeInfo info)
1748 XmlElement me = (XmlElement) info.Node;
1749 MemberReference mi = info.Member;
1751 foreach (MemberFormatter f in memberFormatters) {
1752 string element = "MemberSignature[@Language='" + f.Language + "']";
1754 var valueToUse = f.GetDeclaration (mi);
1756 AddXmlNode (
1757 me.SelectNodes (element).Cast<XmlElement> ().ToArray(),
1758 x => x.GetAttribute("Value") == valueToUse,
1759 x => x.SetAttribute ("Value", valueToUse),
1760 () => {
1761 var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
1762 var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
1763 return newNode;
1765 mi);
1769 WriteElementText(me, "MemberType", GetMemberType(mi));
1771 if (!no_assembly_versions) {
1772 if (!IsMultiAssembly)
1773 UpdateAssemblyVersions (me, mi, true);
1774 else {
1775 var node = AddAssemblyNameToNode (me, mi.Module);
1777 UpdateAssemblyVersionForAssemblyInfo (node, me, new[] { GetAssemblyVersion (mi.Module.Assembly) }, add: true);
1780 else {
1781 ClearElement (me, "AssemblyInfo");
1784 MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
1786 MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1787 if (mi is MethodReference) {
1788 MethodReference mb = (MethodReference) mi;
1789 if (mb.IsGenericMethod ())
1790 MakeTypeParameters (me, mb.GenericParameters, mi, MDocUpdater.HasDroppedNamespace(mi));
1792 MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace(mi));
1794 string fieldValue;
1795 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1796 WriteElementText(me, "MemberValue", fieldValue);
1798 info.Node = WriteElement (me, "Docs");
1799 MakeDocNode (info);
1800 OrderMemberNodes (me, me.ChildNodes);
1801 UpdateExtensionMethods (me, info);
1804 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, MemberReference member) {
1805 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, member.Module);
1808 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, TypeDefinition type) {
1809 AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
1812 static XmlElement AddAssemblyXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1814 bool isUnified = MDocUpdater.HasDroppedNamespace (module);
1815 XmlElement thisAssemblyNode = relevant.FirstOrDefault (valueMatches);
1816 if (thisAssemblyNode == null) {
1817 thisAssemblyNode = makeNewNode ();
1819 setValue (thisAssemblyNode);
1821 if (isUnified) {
1822 thisAssemblyNode.AddApiStyle (ApiStyle.Unified);
1824 foreach (var otherNodes in relevant.Where (n => n != thisAssemblyNode && n.DoesNotHaveApiStyle (ApiStyle.Unified))) {
1825 otherNodes.AddApiStyle (ApiStyle.Classic);
1828 return thisAssemblyNode;
1831 /// <summary>Adds an xml node, reusing the node if it's available</summary>
1832 /// <param name="relevant">The existing set of nodes</param>
1833 /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
1834 /// <param name="setValue">Sets the node's value</param>
1835 /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
1836 static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
1838 bool shouldDuplicate = MDocUpdater.HasDroppedNamespace (module);
1839 var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
1840 var existing = relevant;
1841 bool done = false;
1842 bool addedOldApiStyle = false;
1844 if (shouldDuplicate) {
1845 existing = existing.Where (n => n.HasApiStyle (styleToUse)).ToArray ();
1846 foreach (var n in relevant.Where (n => n.DoesNotHaveApiStyle (styleToUse))) {
1847 if (valueMatches (n)) {
1848 done = true;
1850 else {
1851 n.AddApiStyle (ApiStyle.Classic);
1852 addedOldApiStyle = true;
1856 if (!done) {
1857 if (!existing.Any ()) {
1858 var newNode = makeNewNode ();
1859 if (shouldDuplicate && addedOldApiStyle) {
1860 newNode.AddApiStyle (ApiStyle.Unified);
1863 else {
1864 var itemToReuse = existing.First ();
1865 setValue (itemToReuse);
1867 if (shouldDuplicate && addedOldApiStyle) {
1868 itemToReuse.AddApiStyle (styleToUse);
1874 static readonly string[] MemberNodeOrder = {
1875 "MemberSignature",
1876 "MemberType",
1877 "AssemblyInfo",
1878 "Attributes",
1879 "ReturnValue",
1880 "TypeParameters",
1881 "Parameters",
1882 "MemberValue",
1883 "Docs",
1884 "Excluded",
1885 "ExcludedLibrary",
1886 "Link",
1889 static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1891 ReorderNodes (member, children, MemberNodeOrder);
1894 static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1896 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1897 for (int i = 0; i < ordering.Length; ++i) {
1898 for (int j = 0; j < children.Count; ++j) {
1899 XmlNode c = children [j];
1900 if (c.Name == ordering [i]) {
1901 newChildren.Add (c);
1905 if (newChildren.Count >= 0)
1906 node.PrependChild ((XmlNode) newChildren [0]);
1907 for (int i = 1; i < newChildren.Count; ++i) {
1908 XmlNode prev = (XmlNode) newChildren [i-1];
1909 XmlNode cur = (XmlNode) newChildren [i];
1910 node.RemoveChild (cur);
1911 node.InsertAfter (cur, prev);
1915 IEnumerable<string> GetCustomAttributes (MemberReference mi)
1917 IEnumerable<string> attrs = Enumerable.Empty<string>();
1919 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1920 if (p != null)
1921 attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1923 PropertyDefinition pd = mi as PropertyDefinition;
1924 if (pd != null) {
1925 if (pd.GetMethod != null)
1926 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1927 if (pd.SetMethod != null)
1928 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1931 EventDefinition ed = mi as EventDefinition;
1932 if (ed != null) {
1933 if (ed.AddMethod != null)
1934 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1935 if (ed.RemoveMethod != null)
1936 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1939 return attrs;
1942 IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1944 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1946 TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1947 if (attrType != null && !IsPublic (attrType))
1948 continue;
1949 if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1950 continue;
1952 if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1953 continue;
1955 StringList fields = new StringList ();
1957 for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1958 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1959 fields.Add (MakeAttributesValueString (
1960 argument.Value,
1961 argument.Type));
1963 var namedArgs =
1964 (from namedArg in attribute.Fields
1965 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1966 .Concat (
1967 (from namedArg in attribute.Properties
1968 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1969 .OrderBy (v => v.Name);
1970 foreach (var d in namedArgs)
1971 fields.Add (string.Format ("{0}={1}", d.Name,
1972 MakeAttributesValueString (d.Value, d.Type)));
1974 string a2 = String.Join(", ", fields.ToArray ());
1975 if (a2 != "") a2 = "(" + a2 + ")";
1977 string name = attribute.GetDeclaringType();
1978 if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1979 yield return prefix + name + a2;
1983 static readonly string[] ValidExtensionMembers = {
1984 "Docs",
1985 "MemberSignature",
1986 "MemberType",
1987 "Parameters",
1988 "ReturnValue",
1989 "TypeParameters",
1992 static readonly string[] ValidExtensionDocMembers = {
1993 "param",
1994 "summary",
1995 "typeparam",
1998 private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
2000 MethodDefinition me = info.Member as MethodDefinition;
2001 if (me == null)
2002 return;
2003 if (info.Parameters.Count < 1)
2004 return;
2005 if (!DocUtils.IsExtensionMethod (me))
2006 return;
2008 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
2009 XmlNode member = e.CloneNode (true);
2010 em.AppendChild (member);
2011 RemoveExcept (member, ValidExtensionMembers);
2012 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
2013 WriteElementText (member, "MemberType", "ExtensionMethod");
2014 XmlElement link = member.OwnerDocument.CreateElement ("Link");
2015 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
2016 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
2017 member.AppendChild (link);
2018 AddTargets (em, info);
2020 var sig = em.SelectSingleNode ("Member/MemberSignature[@Language='C#']/@Value");
2021 if (!IsMultiAssembly || (IsMultiAssembly && sig != null && !extensionMethods.Any (ex => ex.SelectSingleNode ("Member/MemberSignature[@Language='C#']/@Value").Value == sig.Value))) {
2022 extensionMethods.Add (em);
2026 private static void RemoveExcept (XmlNode node, string[] except)
2028 if (node == null)
2029 return;
2030 MyXmlNodeList remove = null;
2031 foreach (XmlNode n in node.ChildNodes) {
2032 if (Array.BinarySearch (except, n.Name) < 0) {
2033 if (remove == null)
2034 remove = new MyXmlNodeList ();
2035 remove.Add (n);
2038 if (remove != null)
2039 foreach (XmlNode n in remove)
2040 node.RemoveChild (n);
2043 private static void AddTargets (XmlNode member, DocsNodeInfo info)
2045 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
2046 member.PrependChild (targets);
2047 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
2048 AppendElementAttributeText (targets, "Target", "Type",
2049 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
2051 else {
2052 GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
2053 IList<TypeReference> constraints = gp.Constraints;
2054 if (constraints.Count == 0)
2055 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
2056 else
2057 foreach (TypeReference c in constraints)
2058 AppendElementAttributeText(targets, "Target", "Type",
2059 slashdocFormatter.GetDeclaration (c));
2063 private static bool GetFieldConstValue (FieldDefinition field, out string value)
2065 value = null;
2066 TypeDefinition type = field.DeclaringType.Resolve ();
2067 if (type != null && type.IsEnum) return false;
2069 if (type != null && type.IsGenericType ()) return false;
2070 if (!field.HasConstant)
2071 return false;
2072 if (field.IsLiteral) {
2073 object val = field.Constant;
2074 if (val == null) value = "null";
2075 else if (val is Enum) value = val.ToString();
2076 else if (val is IFormattable) {
2077 value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
2078 if (val is string)
2079 value = "\"" + value + "\"";
2081 if (value != null && value != "")
2082 return true;
2084 return false;
2087 // XML HELPER FUNCTIONS
2089 internal static XmlElement WriteElement(XmlNode parent, string element, bool forceNewElement = false) {
2090 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
2091 if (ret == null || forceNewElement) {
2092 string[] path = element.Split('/');
2093 foreach (string p in path) {
2094 ret = (XmlElement)parent.SelectSingleNode(p);
2095 if (ret == null || forceNewElement) {
2096 string ename = p;
2097 if (ename.IndexOf('[') >= 0) // strip off XPath predicate
2098 ename = ename.Substring(0, ename.IndexOf('['));
2099 ret = parent.OwnerDocument.CreateElement(ename);
2100 parent.AppendChild(ret);
2101 parent = ret;
2102 } else {
2103 parent = ret;
2107 return ret;
2109 private static XmlElement WriteElementText(XmlNode parent, string element, string value, bool forceNewElement = false) {
2110 XmlElement node = WriteElement(parent, element, forceNewElement: forceNewElement);
2111 node.InnerText = value;
2112 return node;
2115 static XmlElement AppendElementText (XmlNode parent, string element, string value)
2117 XmlElement n = parent.OwnerDocument.CreateElement (element);
2118 parent.AppendChild (n);
2119 n.InnerText = value;
2120 return n;
2123 static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
2125 XmlElement n = parent.OwnerDocument.CreateElement (element);
2126 parent.AppendChild (n);
2127 n.SetAttribute (attribute, value);
2128 return n;
2131 internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
2133 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
2134 dest.AppendChild (copy);
2135 return copy;
2138 private static void WriteElementInitialText(XmlElement parent, string element, string value) {
2139 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
2140 if (node != null)
2141 return;
2142 node = WriteElement(parent, element);
2143 node.InnerText = value;
2145 private static XmlElement WriteElementAttribute(XmlElement parent, string element, string attribute, string value, bool forceNewElement = false) {
2146 XmlElement node = WriteElement(parent, element, forceNewElement:forceNewElement);
2147 return WriteElementAttribute (parent, node, attribute, value);
2149 private static XmlElement WriteElementAttribute(XmlElement parent, XmlElement node, string attribute, string value) {
2150 if (node.GetAttribute (attribute) != value) {
2151 node.SetAttribute (attribute, value);
2153 return node;
2155 internal static void ClearElement(XmlElement parent, string name) {
2156 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
2157 if (node != null)
2158 parent.RemoveChild(node);
2161 // DOCUMENTATION HELPER FUNCTIONS
2163 private void MakeDocNode (DocsNodeInfo info)
2165 List<GenericParameter> genericParams = info.GenericParameters;
2166 IList<ParameterDefinition> parameters = info.Parameters;
2167 TypeReference returntype = info.ReturnType;
2168 bool returnisreturn = info.ReturnIsReturn;
2169 XmlElement e = info.Node;
2170 bool addremarks = info.AddRemarks;
2172 WriteElementInitialText(e, "summary", "To be added.");
2174 if (parameters != null) {
2175 string[] values = new string [parameters.Count];
2176 for (int i = 0; i < values.Length; ++i)
2177 values [i] = parameters [i].Name;
2178 UpdateParameters (e, "param", values);
2181 if (genericParams != null) {
2182 string[] values = new string [genericParams.Count];
2183 for (int i = 0; i < values.Length; ++i)
2184 values [i] = genericParams [i].Name;
2185 UpdateParameters (e, "typeparam", values);
2188 string retnodename = null;
2189 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
2190 retnodename = returnisreturn ? "returns" : "value";
2191 string retnodename_other = !returnisreturn ? "returns" : "value";
2193 // If it has a returns node instead of a value node, change its name.
2194 XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
2195 if (retother != null) {
2196 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
2197 foreach (XmlNode node in retother)
2198 retnode.AppendChild(node.CloneNode(true));
2199 e.ReplaceChild(retnode, retother);
2200 } else {
2201 WriteElementInitialText(e, retnodename, "To be added.");
2203 } else {
2204 ClearElement(e, "returns");
2205 ClearElement(e, "value");
2208 if (addremarks)
2209 WriteElementInitialText(e, "remarks", "To be added.");
2211 if (exceptions.HasValue && info.Member != null &&
2212 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
2213 UpdateExceptions (e, info.Member);
2216 foreach (DocumentationImporter importer in importers)
2217 importer.ImportDocumentation (info);
2219 OrderDocsNodes (e, e.ChildNodes);
2220 NormalizeWhitespace(e);
2223 static readonly string[] DocsNodeOrder = {
2224 "typeparam", "param", "summary", "returns", "value", "remarks",
2227 private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
2229 ReorderNodes (docs, children, DocsNodeOrder);
2233 private void UpdateParameters (XmlElement e, string element, string[] values)
2235 if (values != null) {
2236 XmlNode[] paramnodes = new XmlNode[values.Length];
2238 // Some documentation had param nodes with leading spaces.
2239 foreach (XmlElement paramnode in e.SelectNodes(element)){
2240 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
2243 // If a member has only one parameter, we can track changes to
2244 // the name of the parameter easily.
2245 if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
2246 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
2249 bool reinsert = false;
2251 // Pick out existing and still-valid param nodes, and
2252 // create nodes for parameters not in the file.
2253 Hashtable seenParams = new Hashtable();
2254 for (int pi = 0; pi < values.Length; pi++) {
2255 string p = values [pi];
2256 seenParams[p] = pi;
2258 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
2259 if (paramnodes[pi] != null) continue;
2261 XmlElement pe = e.OwnerDocument.CreateElement(element);
2262 pe.SetAttribute("name", p);
2263 pe.InnerText = "To be added.";
2264 paramnodes[pi] = pe;
2265 reinsert = true;
2268 // Remove parameters that no longer exist and check all params are in the right order.
2269 int idx = 0;
2270 MyXmlNodeList todelete = new MyXmlNodeList ();
2271 foreach (XmlElement paramnode in e.SelectNodes(element)) {
2272 string name = paramnode.GetAttribute("name");
2273 if (!seenParams.ContainsKey(name)) {
2274 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2275 Warning ("The following param node can only be deleted if the --delete option is given: ");
2276 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
2277 // delegate type
2278 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2279 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2280 name);
2282 else {
2283 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2284 e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
2285 e.ParentNode.Attributes ["MemberName"].Value,
2286 name);
2288 Warning ("\tValue={0}", paramnode.OuterXml);
2289 } else {
2290 todelete.Add (paramnode);
2292 continue;
2295 if ((int)seenParams[name] != idx)
2296 reinsert = true;
2298 idx++;
2301 foreach (XmlNode n in todelete) {
2302 n.ParentNode.RemoveChild (n);
2305 // Re-insert the parameter nodes at the top of the doc section.
2306 if (reinsert)
2307 for (int pi = values.Length-1; pi >= 0; pi--)
2308 e.PrependChild(paramnodes[pi]);
2309 } else {
2310 // Clear all existing param nodes
2311 foreach (XmlNode paramnode in e.SelectNodes(element)) {
2312 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
2313 Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
2314 Console.WriteLine(paramnode.OuterXml);
2315 } else {
2316 paramnode.ParentNode.RemoveChild(paramnode);
2322 private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
2324 string existingName = pe.GetAttribute ("name");
2325 pe.SetAttribute ("name", newName);
2326 if (existingName == newName)
2327 return;
2328 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
2329 if (paramref.GetAttribute ("name").Trim () == existingName)
2330 paramref.SetAttribute ("name", newName);
2333 class CrefComparer : XmlNodeComparer {
2335 public CrefComparer ()
2339 public override int Compare (XmlNode x, XmlNode y)
2341 string xType = x.Attributes ["cref"].Value;
2342 string yType = y.Attributes ["cref"].Value;
2343 string xNamespace = GetNamespace (xType);
2344 string yNamespace = GetNamespace (yType);
2346 int c = xNamespace.CompareTo (yNamespace);
2347 if (c != 0)
2348 return c;
2349 return xType.CompareTo (yType);
2352 static string GetNamespace (string type)
2354 int n = type.LastIndexOf ('.');
2355 if (n >= 0)
2356 return type.Substring (0, n);
2357 return string.Empty;
2361 private void UpdateExceptions (XmlNode docs, MemberReference member)
2363 string indent = new string (' ', 10);
2364 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
2365 string cref = slashdocFormatter.GetDeclaration (source.Exception);
2366 var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
2367 if (node != null)
2368 continue;
2369 XmlElement e = docs.OwnerDocument.CreateElement ("exception");
2370 e.SetAttribute ("cref", cref);
2371 e.InnerXml = "To be added; from:\n" + indent + "<see cref=\"" +
2372 string.Join ("\" />,\n" + indent + "<see cref=\"",
2373 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
2374 .OrderBy (s => s)) +
2375 "\" />";
2376 docs.AppendChild (e);
2378 SortXmlNodes (docs, docs.SelectNodes ("exception"),
2379 new CrefComparer ());
2382 private static void NormalizeWhitespace(XmlElement e) {
2383 // Remove all text and whitespace nodes from the element so it
2384 // is outputted with nice indentation and no blank lines.
2385 ArrayList deleteNodes = new ArrayList();
2386 foreach (XmlNode n in e)
2387 if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
2388 deleteNodes.Add(n);
2389 foreach (XmlNode n in deleteNodes)
2390 n.ParentNode.RemoveChild(n);
2393 private bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
2395 TypeDefinition type = member as TypeDefinition;
2396 if (type == null)
2397 type = member.DeclaringType as TypeDefinition;
2399 var versions = new string[] { GetAssemblyVersion (type.Module.Assembly) };
2401 if (root.LocalName == "AssemblyInfo")
2402 return UpdateAssemblyVersionForAssemblyInfo (root, root.ParentNode as XmlElement, versions, add: true);
2403 else
2404 return UpdateAssemblyVersions (root, type.Module.Assembly, versions, add);
2407 private static string GetAssemblyVersion (AssemblyDefinition assembly)
2409 return assembly.Name.Version.ToString();
2412 private bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
2414 if (IsMultiAssembly)
2415 return false;
2417 XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
2418 if (av != null) {
2419 // AssemblyVersions is not part of the spec
2420 root.RemoveChild (av);
2423 string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
2424 string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
2425 string thisNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? newNodeFilter : oldNodeFilter;
2426 string thatNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? oldNodeFilter : newNodeFilter;
2428 XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
2429 if (e == null) {
2430 e = root.OwnerDocument.CreateElement("AssemblyInfo");
2432 if (MDocUpdater.HasDroppedNamespace (assembly)) {
2433 e.AddApiStyle (ApiStyle.Unified);
2436 root.AppendChild(e);
2439 var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
2440 if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
2441 // there's a classic node, we should add apistyles
2442 e.AddApiStyle (ApiStyle.Unified);
2443 thatNode.AddApiStyle (ApiStyle.Classic);
2446 return UpdateAssemblyVersionForAssemblyInfo (e, root, assemblyVersions, add);
2449 static bool UpdateAssemblyVersionForAssemblyInfo (XmlElement e, XmlElement root, string[] assemblyVersions, bool add)
2451 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().Where (v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0).ToList ();
2452 // matches.Count > 0 && add: ignore -- already present
2453 if (matches.Count > 0 && !add) {
2454 foreach (XmlNode c in matches)
2455 e.RemoveChild (c);
2457 else if (matches.Count == 0 && add) {
2458 foreach (string sv in assemblyVersions) {
2459 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
2460 c.InnerText = sv;
2461 e.AppendChild(c);
2465 // matches.Count == 0 && !add: ignore -- already not present
2466 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
2467 SortXmlNodes (e, avs, new VersionComparer ());
2469 bool anyNodesLeft = avs.Count != 0;
2470 if (!anyNodesLeft) {
2471 e.ParentNode.RemoveChild (e);
2473 return anyNodesLeft;
2476 // FIXME: get TypeReferences instead of string comparison?
2477 private static string[] IgnorableAttributes = {
2478 // Security related attributes
2479 "System.Reflection.AssemblyKeyFileAttribute",
2480 "System.Reflection.AssemblyDelaySignAttribute",
2481 // Present in @RefType
2482 "System.Runtime.InteropServices.OutAttribute",
2483 // For naming the indexer to use when not using indexers
2484 "System.Reflection.DefaultMemberAttribute",
2485 // for decimal constants
2486 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2487 // compiler generated code
2488 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2489 // more compiler generated code, e.g. iterator methods
2490 "System.Diagnostics.DebuggerHiddenAttribute",
2491 "System.Runtime.CompilerServices.FixedBufferAttribute",
2492 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2493 // extension methods
2494 "System.Runtime.CompilerServices.ExtensionAttribute",
2495 // Used to differentiate 'object' from C#4 'dynamic'
2496 "System.Runtime.CompilerServices.DynamicAttribute",
2499 private void MakeAttributes (XmlElement root, IEnumerable<string> attributes, TypeReference t=null)
2501 if (!attributes.Any ()) {
2502 ClearElement (root, "Attributes");
2503 return;
2506 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
2507 if (e != null)
2508 e.RemoveAll();
2509 else if (e == null)
2510 e = root.OwnerDocument.CreateElement("Attributes");
2512 foreach (string attribute in attributes) {
2513 XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
2514 e.AppendChild(ae);
2516 WriteElementText(ae, "AttributeName", attribute);
2519 if (e.ParentNode == null)
2520 root.AppendChild(e);
2522 NormalizeWhitespace(e);
2525 public static string MakeAttributesValueString (object v, TypeReference valueType)
2527 var formatters = new [] {
2528 new AttributeValueFormatter (),
2529 new ApplePlatformEnumFormatter (),
2530 new StandardFlagsEnumFormatter (),
2531 new DefaultAttributeValueFormatter (),
2534 ResolvedTypeInfo type = new ResolvedTypeInfo (valueType);
2535 foreach (var formatter in formatters) {
2536 string formattedValue;
2537 if (formatter.TryFormatValue (v, type, out formattedValue)) {
2538 return formattedValue;
2542 // this should never occur because the DefaultAttributeValueFormatter will always
2543 // successfully format the value ... but this is needed to satisfy the compiler :)
2544 throw new InvalidDataException (string.Format ("Unable to format attribute value ({0})", v.ToString ()));
2547 internal static IDictionary<long, string> GetEnumerationValues (TypeDefinition type)
2549 var values = new Dictionary<long, string> ();
2550 foreach (var f in
2551 (from f in type.Fields
2552 where !(f.IsRuntimeSpecialName || f.IsSpecialName)
2553 select f)) {
2554 values [ToInt64 (f.Constant)] = f.Name;
2556 return values;
2559 internal static long ToInt64 (object value)
2561 if (value is ulong)
2562 return (long) (ulong) value;
2563 return Convert.ToInt64 (value);
2566 private void MakeParameters (XmlElement root, MemberReference member, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
2568 XmlElement e = WriteElement(root, "Parameters");
2570 int i = 0;
2571 foreach (ParameterDefinition p in parameters) {
2572 XmlElement pe;
2574 // param info
2575 var ptype = GetDocParameterType (p.ParameterType);
2576 var newPType = ptype;
2578 if (MDocUpdater.SwitchingToMagicTypes) {
2579 newPType = NativeTypeManager.ConvertFromNativeType (ptype);
2582 // now find the existing node, if it's there so we can reuse it.
2583 var nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2584 .Cast<XmlElement> ().Where (x => x.GetAttribute ("Name") == p.Name)
2585 .ToArray();
2587 if (nodes.Count () == 0) {
2588 // wasn't found, let's make sure it wasn't just cause the param name was changed
2589 nodes = root.SelectSingleNode ("Parameters").SelectNodes ("Parameter")
2590 .Cast<XmlElement> ()
2591 .Skip (i) // this makes sure we don't inadvertently "reuse" nodes when adding new ones
2592 .Where (x => x.GetAttribute ("Name") != p.Name && (x.GetAttribute ("Type") == ptype || x.GetAttribute ("Type") == newPType))
2593 .Take(1) // there might be more than one that meets this parameter ... only take the first.
2594 .ToArray();
2597 AddXmlNode (nodes,
2598 x => x.GetAttribute ("Type") == ptype,
2599 x => x.SetAttribute ("Type", ptype),
2600 () => {
2601 pe = root.OwnerDocument.CreateElement ("Parameter");
2602 e.AppendChild (pe);
2604 pe.SetAttribute ("Name", p.Name);
2605 pe.SetAttribute ("Type", ptype);
2606 if (p.ParameterType is ByReferenceType) {
2607 if (p.IsOut)
2608 pe.SetAttribute ("RefType", "out");
2609 else
2610 pe.SetAttribute ("RefType", "ref");
2613 MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
2614 return pe;
2616 member);
2618 i++;
2622 private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, MemberReference member, bool shouldDuplicateWithNew)
2624 if (typeParams == null || typeParams.Count == 0) {
2625 XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
2626 if (f != null)
2627 root.RemoveChild (f);
2628 return;
2630 XmlElement e = WriteElement(root, "TypeParameters");
2632 var nodes = e.SelectNodes ("TypeParameter").Cast<XmlElement> ().ToArray ();
2634 foreach (GenericParameter t in typeParams) {
2636 IList<TypeReference> constraints = t.Constraints;
2637 GenericParameterAttributes attrs = t.Attributes;
2640 AddXmlNode (
2641 nodes,
2642 x => {
2643 var baseType = e.SelectSingleNode("BaseTypeName");
2644 // TODO: should this comparison take into account BaseTypeName?
2645 return x.GetAttribute("Name") == t.Name;
2647 x => {}, // no additional action required
2648 () => {
2650 XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
2651 e.AppendChild(pe);
2652 pe.SetAttribute("Name", t.Name);
2653 MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""), t.DeclaringType);
2654 XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2655 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2656 if (ce != null)
2657 e.RemoveChild (ce);
2658 return pe;
2660 if (ce != null)
2661 ce.RemoveAll();
2662 else {
2663 ce = root.OwnerDocument.CreateElement ("Constraints");
2665 pe.AppendChild (ce);
2666 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2667 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2668 if ((attrs & GenericParameterAttributes.Covariant) != 0)
2669 AppendElementText (ce, "ParameterAttribute", "Covariant");
2670 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2671 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2672 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2673 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2674 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2675 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2676 foreach (TypeReference c in constraints) {
2677 TypeDefinition cd = c.Resolve ();
2678 AppendElementText (ce,
2679 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2680 GetDocTypeFullName (c));
2683 return pe;
2685 member);
2689 private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
2691 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2692 MakeParameters (root, mi, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
2693 else if (mi is MethodDefinition) {
2694 MethodDefinition mb = (MethodDefinition) mi;
2695 IList<ParameterDefinition> parameters = mb.Parameters;
2696 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2697 if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2698 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2699 p.SetAttribute ("RefType", "this");
2702 else if (mi is PropertyDefinition) {
2703 IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2704 if (parameters.Count > 0)
2705 MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
2706 else
2707 return;
2709 else if (mi is FieldDefinition) return;
2710 else if (mi is EventDefinition) return;
2711 else throw new ArgumentException();
2714 internal static string GetDocParameterType (TypeReference type)
2716 return GetDocTypeFullName (type).Replace ("@", "&");
2719 private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes, bool shouldDuplicateWithNew=false)
2721 XmlElement e = WriteElement(root, "ReturnValue");
2722 var valueToUse = GetDocTypeFullName (type);
2724 AddXmlNode (e.SelectNodes("ReturnType").Cast<XmlElement> ().ToArray (),
2725 x => x.InnerText == valueToUse,
2726 x => x.InnerText = valueToUse,
2727 () => {
2728 var newNode = WriteElementText(e, "ReturnType", valueToUse, forceNewElement: true);
2729 if (attributes != null)
2730 MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
2732 return newNode;
2734 type);
2737 private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
2739 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2740 return;
2741 else if (mi is MethodDefinition)
2742 MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes, shouldDuplicateWithNew);
2743 else if (mi is PropertyDefinition)
2744 MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null, shouldDuplicateWithNew);
2745 else if (mi is FieldDefinition)
2746 MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null, shouldDuplicateWithNew);
2747 else if (mi is EventDefinition)
2748 MakeReturnValue (root, ((EventDefinition)mi).EventType, null, shouldDuplicateWithNew);
2749 else
2750 throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2753 private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2755 MemberReference mi = info.Member;
2756 if (mi is TypeDefinition) return null;
2758 string sigs = memberFormatters [0].GetDeclaration (mi);
2759 if (sigs == null) return null; // not publicly visible
2761 // no documentation for property/event accessors. Is there a better way of doing this?
2762 if (mi.Name.StartsWith("get_")) return null;
2763 if (mi.Name.StartsWith("set_")) return null;
2764 if (mi.Name.StartsWith("add_")) return null;
2765 if (mi.Name.StartsWith("remove_")) return null;
2766 if (mi.Name.StartsWith("raise_")) return null;
2768 XmlElement me = doc.CreateElement("Member");
2769 me.SetAttribute("MemberName", GetMemberName (mi));
2771 info.Node = me;
2772 UpdateMember(info);
2773 if (exceptions.HasValue &&
2774 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2775 UpdateExceptions (info.Node, info.Member);
2777 if (since != null) {
2778 XmlNode docs = me.SelectSingleNode("Docs");
2779 docs.AppendChild (CreateSinceNode (doc));
2782 return me;
2785 internal static string GetMemberName (MemberReference mi)
2787 MethodDefinition mb = mi as MethodDefinition;
2788 if (mb == null) {
2789 PropertyDefinition pi = mi as PropertyDefinition;
2790 if (pi == null)
2791 return mi.Name;
2792 return DocUtils.GetPropertyName (pi);
2794 StringBuilder sb = new StringBuilder (mi.Name.Length);
2795 if (!DocUtils.IsExplicitlyImplemented (mb))
2796 sb.Append (mi.Name);
2797 else {
2798 TypeReference iface;
2799 MethodReference ifaceMethod;
2800 DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2801 sb.Append (GetDocTypeFullName (iface));
2802 sb.Append ('.');
2803 sb.Append (ifaceMethod.Name);
2805 if (mb.IsGenericMethod ()) {
2806 IList<GenericParameter> typeParams = mb.GenericParameters;
2807 if (typeParams.Count > 0) {
2808 sb.Append ("<");
2809 sb.Append (typeParams [0].Name);
2810 for (int i = 1; i < typeParams.Count; ++i)
2811 sb.Append (",").Append (typeParams [i].Name);
2812 sb.Append (">");
2815 return sb.ToString ();
2818 /// SIGNATURE GENERATION FUNCTIONS
2819 internal static bool IsPrivate (MemberReference mi)
2821 return memberFormatters [0].GetDeclaration (mi) == null;
2824 internal static string GetMemberType (MemberReference mi)
2826 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2827 return "Constructor";
2828 if (mi is MethodDefinition)
2829 return "Method";
2830 if (mi is PropertyDefinition)
2831 return "Property";
2832 if (mi is FieldDefinition)
2833 return "Field";
2834 if (mi is EventDefinition)
2835 return "Event";
2836 throw new ArgumentException();
2839 private static string GetDocTypeName (TypeReference type)
2841 return docTypeFormatter.GetName (type);
2844 internal static string GetDocTypeFullName (TypeReference type)
2846 return DocTypeFullMemberFormatter.Default.GetName (type);
2849 internal static string GetXPathForMember (DocumentationMember member)
2851 StringBuilder xpath = new StringBuilder ();
2852 xpath.Append ("//Members/Member[@MemberName=\"")
2853 .Append (member.MemberName)
2854 .Append ("\"]");
2855 if (member.Parameters != null && member.Parameters.Count > 0) {
2856 xpath.Append ("/Parameters[count(Parameter) = ")
2857 .Append (member.Parameters.Count);
2858 for (int i = 0; i < member.Parameters.Count; ++i) {
2859 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2860 xpath.Append (member.Parameters [i]);
2861 xpath.Append ("\"");
2863 xpath.Append ("]/..");
2865 return xpath.ToString ();
2868 public static string GetXPathForMember (XPathNavigator member)
2870 StringBuilder xpath = new StringBuilder ();
2871 xpath.Append ("//Type[@FullName=\"")
2872 .Append (member.SelectSingleNode ("../../@FullName").Value)
2873 .Append ("\"]/");
2874 xpath.Append ("Members/Member[@MemberName=\"")
2875 .Append (member.SelectSingleNode ("@MemberName").Value)
2876 .Append ("\"]");
2877 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2878 if (parameters.Count > 0) {
2879 xpath.Append ("/Parameters[count(Parameter) = ")
2880 .Append (parameters.Count);
2881 int i = 0;
2882 while (parameters.MoveNext ()) {
2883 ++i;
2884 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2885 xpath.Append (parameters.Current.Value);
2886 xpath.Append ("\"");
2888 xpath.Append ("]/..");
2890 return xpath.ToString ();
2893 public static string GetXPathForMember (MemberReference member)
2895 StringBuilder xpath = new StringBuilder ();
2896 xpath.Append ("//Type[@FullName=\"")
2897 .Append (member.DeclaringType.FullName)
2898 .Append ("\"]/");
2899 xpath.Append ("Members/Member[@MemberName=\"")
2900 .Append (GetMemberName (member))
2901 .Append ("\"]");
2903 IList<ParameterDefinition> parameters = null;
2904 if (member is MethodDefinition)
2905 parameters = ((MethodDefinition) member).Parameters;
2906 else if (member is PropertyDefinition) {
2907 parameters = ((PropertyDefinition) member).Parameters;
2909 if (parameters != null && parameters.Count > 0) {
2910 xpath.Append ("/Parameters[count(Parameter) = ")
2911 .Append (parameters.Count);
2912 for (int i = 0; i < parameters.Count; ++i) {
2913 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2914 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2915 xpath.Append ("\"");
2917 xpath.Append ("]/..");
2919 return xpath.ToString ();
2923 static class CecilExtensions {
2924 public static string GetDeclaringType(this CustomAttribute attribute)
2926 var type = attribute.Constructor.DeclaringType;
2927 var typeName = type.FullName;
2929 string translatedType = NativeTypeManager.GetTranslatedName (type);
2930 return translatedType;
2933 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2935 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2936 yield return (MemberReference) c;
2937 foreach (var e in type.Events)
2938 yield return (MemberReference) e;
2939 foreach (var f in type.Fields)
2940 yield return (MemberReference) f;
2941 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2942 yield return (MemberReference) m;
2943 foreach (var t in type.NestedTypes)
2944 yield return (MemberReference) t;
2945 foreach (var p in type.Properties)
2946 yield return (MemberReference) p;
2949 public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2951 return GetMembers (type).Where (m => m.Name == member);
2954 public static MemberReference GetMember (this TypeDefinition type, string member)
2956 return GetMembers (type, member).EnsureZeroOrOne ();
2959 static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2961 if (source.Count () > 1)
2962 throw new InvalidOperationException ("too many matches");
2963 return source.FirstOrDefault ();
2966 public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2968 return type.Methods
2969 .Where (m => m.Name == method)
2970 .EnsureZeroOrOne ();
2973 public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2975 TypeDefinition def = type as TypeDefinition;
2976 if (def == null)
2977 return new MemberReference [0];
2978 CustomAttribute defMemberAttr = def.CustomAttributes
2979 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2980 if (defMemberAttr == null)
2981 return new MemberReference [0];
2982 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2983 return def.Properties
2984 .Where (p => p.Name == name)
2985 .Select (p => (MemberReference) p);
2988 public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2990 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2993 public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2995 return GetTypes (assembly)
2996 .Where (td => td.FullName == type)
2997 .EnsureZeroOrOne ();
3000 public static bool IsGenericType (this TypeReference type)
3002 return type.GenericParameters.Count > 0;
3005 public static bool IsGenericMethod (this MethodReference method)
3007 return method.GenericParameters.Count > 0;
3010 public static TypeReference GetUnderlyingType (this TypeDefinition type)
3012 if (!type.IsEnum)
3013 return type;
3014 return type.Fields.First (f => f.Name == "value__").FieldType;
3017 public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
3019 return self.Types.SelectMany (t => t.GetAllTypes ());
3022 static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
3024 yield return self;
3026 if (!self.HasNestedTypes)
3027 yield break;
3029 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
3030 yield return type;
3034 enum ApiStyle {
3035 Classic,
3036 Unified
3039 static class DocUtils {
3041 public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
3042 string styleString = style.ToString ().ToLowerInvariant ();
3043 string apistylevalue = element.GetAttribute ("apistyle");
3044 return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
3046 public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
3047 string styleString = style.ToString ().ToLowerInvariant ();
3048 return element.GetAttribute ("apistyle") == styleString;
3050 public static bool HasApiStyle(this XmlNode node, ApiStyle style)
3052 var attribute = node.Attributes ["apistyle"];
3053 return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant ();
3055 public static void AddApiStyle(this XmlElement element, ApiStyle style) {
3056 string styleString = style.ToString ().ToLowerInvariant ();
3057 var existingValue = element.GetAttribute ("apistyle");
3058 if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
3059 element.SetAttribute ("apistyle", styleString);
3062 // Propagate the API style up to the membernode if necessary
3063 if (element.LocalName == "AssemblyInfo" && element.ParentNode != null && element.ParentNode.LocalName == "Member") {
3064 var member = element.ParentNode;
3065 var unifiedAssemblyNode = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
3066 var classicAssemblyNode = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
3068 var parentAttribute = element.ParentNode.Attributes ["apistyle"];
3069 Action removeStyle = () => element.ParentNode.Attributes.Remove (parentAttribute);
3070 Action propagateStyle = () => {
3071 if (parentAttribute == null) {
3072 // if it doesn't have the attribute, then add it
3073 parentAttribute = element.OwnerDocument.CreateAttribute ("apistyle");
3074 parentAttribute.Value = styleString;
3075 element.ParentNode.Attributes.Append (parentAttribute);
3079 if ((style == ApiStyle.Classic && unifiedAssemblyNode != null) || (style == ApiStyle.Unified && classicAssemblyNode != null))
3080 removeStyle ();
3081 else
3082 propagateStyle ();
3085 public static void AddApiStyle (this XmlNode node, ApiStyle style)
3087 string styleString = style.ToString ().ToLowerInvariant ();
3088 var existingAttribute = node.Attributes ["apistyle"];
3089 if (existingAttribute == null) {
3090 existingAttribute = node.OwnerDocument.CreateAttribute ("apistyle");
3091 node.Attributes.Append (existingAttribute);
3093 existingAttribute.Value = styleString;
3095 public static void RemoveApiStyle (this XmlElement element, ApiStyle style)
3097 string styleString = style.ToString ().ToLowerInvariant ();
3098 string existingValue = element.GetAttribute ("apistyle");
3099 if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString) {
3100 element.RemoveAttribute ("apistyle");
3103 public static void RemoveApiStyle (this XmlNode node, ApiStyle style)
3105 var styleAttribute = node.Attributes ["apistyle"];
3106 if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ()) {
3107 node.Attributes.Remove (styleAttribute);
3111 public static bool IsExplicitlyImplemented (MethodDefinition method)
3113 return method.IsPrivate && method.IsFinal && method.IsVirtual;
3116 public static string GetTypeDotMember (string name)
3118 int startType, startMethod;
3119 startType = startMethod = -1;
3120 for (int i = 0; i < name.Length; ++i) {
3121 if (name [i] == '.') {
3122 startType = startMethod;
3123 startMethod = i;
3126 return name.Substring (startType+1);
3129 public static string GetMember (string name)
3131 int i = name.LastIndexOf ('.');
3132 if (i == -1)
3133 return name;
3134 return name.Substring (i+1);
3137 public static void GetInfoForExplicitlyImplementedMethod (
3138 MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
3140 iface = null;
3141 ifaceMethod = null;
3142 if (method.Overrides.Count != 1)
3143 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
3144 iface = method.Overrides [0].DeclaringType;
3145 ifaceMethod = method.Overrides [0];
3148 public static string GetPropertyName (PropertyDefinition pi)
3150 // Issue: (g)mcs-generated assemblies that explicitly implement
3151 // properties don't specify the full namespace, just the
3152 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
3153 MethodDefinition method = pi.GetMethod;
3154 if (method == null)
3155 method = pi.SetMethod;
3156 if (!IsExplicitlyImplemented (method))
3157 return pi.Name;
3159 // Need to determine appropriate namespace for this member.
3160 TypeReference iface;
3161 MethodReference ifaceMethod;
3162 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3163 return string.Join (".", new string[]{
3164 DocTypeFullMemberFormatter.Default.GetName (iface),
3165 GetMember (pi.Name)});
3168 public static string GetNamespace (TypeReference type)
3170 if (type.GetElementType ().IsNested)
3171 type = type.GetElementType ();
3172 while (type != null && type.IsNested)
3173 type = type.DeclaringType;
3174 if (type == null)
3175 return string.Empty;
3177 string typeNS = type.Namespace;
3179 // first, make sure this isn't a type reference to another assembly/module
3181 bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
3182 if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
3183 typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
3185 return typeNS;
3188 public static string PathCombine (string dir, string path)
3190 if (dir == null)
3191 dir = "";
3192 if (path == null)
3193 path = "";
3194 return Path.Combine (dir, path);
3197 public static bool IsExtensionMethod (MethodDefinition method)
3199 return
3200 method.CustomAttributes
3201 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
3202 && method.DeclaringType.CustomAttributes
3203 .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
3206 public static bool IsDelegate (TypeDefinition type)
3208 TypeReference baseRef = type.BaseType;
3209 if (baseRef == null)
3210 return false;
3211 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
3212 baseRef.FullName == "System.MulticastDelegate";
3215 public static List<TypeReference> GetDeclaringTypes (TypeReference type)
3217 List<TypeReference> decls = new List<TypeReference> ();
3218 decls.Add (type);
3219 while (type.DeclaringType != null) {
3220 decls.Add (type.DeclaringType);
3221 type = type.DeclaringType;
3223 decls.Reverse ();
3224 return decls;
3227 public static int GetGenericArgumentCount (TypeReference type)
3229 GenericInstanceType inst = type as GenericInstanceType;
3230 return inst != null
3231 ? inst.GenericArguments.Count
3232 : type.GenericParameters.Count;
3235 public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
3237 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
3238 List<TypeReference> userInterfaces = new List<TypeReference> ();
3239 foreach (var ii in type.Interfaces) {
3240 var iface = ii.InterfaceType;
3241 TypeReference lookup = iface.Resolve () ?? iface;
3242 if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
3243 userInterfaces.Add (iface);
3245 return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
3248 private static string GetQualifiedTypeName (TypeReference type)
3250 return "[" + type.Scope.Name + "]" + type.FullName;
3253 private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
3255 HashSet<string> inheritedInterfaces = new HashSet<string> ();
3256 Action<TypeDefinition> a = null;
3257 a = t => {
3258 if (t == null) return;
3259 foreach (var r in t.Interfaces) {
3260 inheritedInterfaces.Add (GetQualifiedTypeName (r.InterfaceType));
3261 a (r.InterfaceType.Resolve ());
3264 TypeReference baseRef = type.BaseType;
3265 while (baseRef != null) {
3266 TypeDefinition baseDef = baseRef.Resolve ();
3267 if (baseDef != null) {
3268 a (baseDef);
3269 baseRef = baseDef.BaseType;
3271 else
3272 baseRef = null;
3274 foreach (var r in type.Interfaces)
3275 a (r.InterfaceType.Resolve ());
3276 return inheritedInterfaces;
3280 class DocsNodeInfo {
3281 public DocsNodeInfo (XmlElement node)
3283 this.Node = node;
3286 public DocsNodeInfo (XmlElement node, TypeDefinition type)
3287 : this (node)
3289 SetType (type);
3292 public DocsNodeInfo (XmlElement node, MemberReference member)
3293 : this (node)
3295 SetMemberInfo (member);
3298 void SetType (TypeDefinition type)
3300 if (type == null)
3301 throw new ArgumentNullException ("type");
3302 Type = type;
3303 GenericParameters = new List<GenericParameter> (type.GenericParameters);
3304 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
3305 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
3306 for (int i = 0; i < declTypes.Count - 1; ++i) {
3307 int remove = System.Math.Min (maxGenArgs,
3308 DocUtils.GetGenericArgumentCount (declTypes [i]));
3309 maxGenArgs -= remove;
3310 while (remove-- > 0)
3311 GenericParameters.RemoveAt (0);
3313 if (DocUtils.IsDelegate (type)) {
3314 Parameters = type.GetMethod("Invoke").Parameters;
3315 ReturnType = type.GetMethod("Invoke").ReturnType;
3316 ReturnIsReturn = true;
3320 void SetMemberInfo (MemberReference member)
3322 if (member == null)
3323 throw new ArgumentNullException ("member");
3324 ReturnIsReturn = true;
3325 AddRemarks = true;
3326 Member = member;
3328 if (member is MethodReference ) {
3329 MethodReference mr = (MethodReference) member;
3330 Parameters = mr.Parameters;
3331 if (mr.IsGenericMethod ()) {
3332 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
3335 else if (member is PropertyDefinition) {
3336 Parameters = ((PropertyDefinition) member).Parameters;
3339 if (member is MethodDefinition) {
3340 ReturnType = ((MethodDefinition) member).ReturnType;
3341 } else if (member is PropertyDefinition) {
3342 ReturnType = ((PropertyDefinition) member).PropertyType;
3343 ReturnIsReturn = false;
3346 // no remarks section for enum members
3347 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
3348 AddRemarks = false;
3351 public TypeReference ReturnType;
3352 public List<GenericParameter> GenericParameters;
3353 public IList<ParameterDefinition> Parameters;
3354 public bool ReturnIsReturn;
3355 public XmlElement Node;
3356 public bool AddRemarks = true;
3357 public MemberReference Member;
3358 public TypeDefinition Type;
3360 public override string ToString ()
3362 return string.Format ("{0} - {1} - {2}", Type, Member, Node == null ? "no xml" : "with xml");
3366 class DocumentationEnumerator {
3368 public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3370 return GetDocumentationTypes (assembly, forTypes, null);
3373 protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3375 foreach (TypeDefinition type in assembly.GetTypes()) {
3376 if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
3377 continue;
3378 if (seen != null && seen.Contains (type.FullName))
3379 continue;
3380 yield return type;
3381 foreach (TypeDefinition nested in type.NestedTypes)
3382 yield return nested;
3386 public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3388 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
3389 if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
3390 oldmember.RemoveAttribute ("__monodocer-seen__");
3391 continue;
3393 MemberReference m = GetMember (type, new DocumentationMember (oldmember));
3394 if (m == null) {
3395 yield return new DocsNodeInfo (oldmember);
3397 else {
3398 yield return new DocsNodeInfo (oldmember, m);
3403 protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
3405 string membertype = member.MemberType;
3407 string returntype = member.ReturnType;
3409 string docName = member.MemberName;
3411 string[] docTypeParams = GetTypeParameters (docName, member.TypeParameters);
3413 // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
3414 MemberReference likelyCandidate = null;
3416 // Loop through all members in this type with the same name
3417 var reflectedMembers = GetReflectionMembers (type, docName).ToArray ();
3418 foreach (MemberReference mi in reflectedMembers) {
3419 bool matchedMagicType = false;
3420 if (mi is TypeDefinition) continue;
3421 if (MDocUpdater.GetMemberType(mi) != membertype) continue;
3423 if (MDocUpdater.IsPrivate (mi))
3424 continue;
3426 IList<ParameterDefinition> pis = null;
3427 string[] typeParams = null;
3428 if (mi is MethodDefinition) {
3429 MethodDefinition mb = (MethodDefinition) mi;
3430 pis = mb.Parameters;
3431 if (mb.IsGenericMethod ()) {
3432 IList<GenericParameter> args = mb.GenericParameters;
3433 typeParams = args.Select (p => p.Name).ToArray ();
3436 else if (mi is PropertyDefinition)
3437 pis = ((PropertyDefinition)mi).Parameters;
3439 // check type parameters
3440 int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
3441 int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
3442 if (methodTcount != reflectionTcount)
3443 continue;
3445 // check member parameters
3446 int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
3447 int pcount = pis == null ? 0 : pis.Count;
3448 if (mcount != pcount)
3449 continue;
3451 MethodDefinition mDef = mi as MethodDefinition;
3452 if (mDef != null && !mDef.IsConstructor) {
3453 // Casting operators can overload based on return type.
3454 string rtype = GetReplacedString (
3455 MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType),
3456 typeParams, docTypeParams);
3457 string originalRType = rtype;
3458 if (MDocUpdater.SwitchingToMagicTypes) {
3459 rtype = NativeTypeManager.ConvertFromNativeType (rtype);
3462 if ((returntype != rtype && originalRType == rtype) ||
3463 (MDocUpdater.SwitchingToMagicTypes && returntype != originalRType && returntype != rtype && originalRType != rtype)) {
3464 continue;
3467 if (originalRType != rtype)
3468 matchedMagicType = true;
3471 if (pcount == 0)
3472 return mi;
3473 bool good = true;
3474 for (int i = 0; i < pis.Count; i++) {
3475 string paramType = GetReplacedString (
3476 MDocUpdater.GetDocParameterType (pis [i].ParameterType),
3477 typeParams, docTypeParams);
3479 // if magictypes, replace paramType to "classic value" ... so the comparison works
3480 string originalParamType = paramType;
3481 if (MDocUpdater.SwitchingToMagicTypes) {
3482 paramType = NativeTypeManager.ConvertFromNativeType (paramType);
3485 string xmlMemberType = member.Parameters [i];
3486 if ((!paramType.Equals(xmlMemberType) && paramType.Equals(originalParamType)) ||
3487 (MDocUpdater.SwitchingToMagicTypes && !originalParamType.Equals(xmlMemberType) && !paramType.Equals(xmlMemberType) && !paramType.Equals(originalParamType))) {
3489 // did not match ... if we're dropping the namespace, and the paramType has the dropped
3490 // namespace, we should see if it matches when added
3491 bool stillDoesntMatch = true;
3492 if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
3493 string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
3495 stillDoesntMatch = withDroppedNs != paramType;
3498 if (stillDoesntMatch) {
3499 good = false;
3500 break;
3504 if (originalParamType != paramType)
3505 matchedMagicType = true;
3507 if (!good) continue;
3509 if (MDocUpdater.SwitchingToMagicTypes && likelyCandidate == null && matchedMagicType) {
3510 // 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
3511 likelyCandidate = mi;
3512 continue;
3515 return mi;
3518 return likelyCandidate;
3521 static string[] GetTypeParameters (string docName, IEnumerable<string> knownParameters)
3523 if (docName [docName.Length-1] != '>')
3524 return null;
3525 StringList types = new StringList ();
3526 int endToken = docName.Length-2;
3527 int i = docName.Length-2;
3528 do {
3529 if (docName [i] == ',' || docName [i] == '<') {
3530 types.Add (docName.Substring (i + 1, endToken - i));
3531 endToken = i-1;
3533 if (docName [i] == '<')
3534 break;
3535 } while (--i >= 0);
3537 types.Reverse ();
3538 var arrayTypes = types.ToArray ();
3540 if (knownParameters != null && knownParameters.Any () && arrayTypes.Length != knownParameters.Count ())
3541 return knownParameters.ToArray ();
3542 else
3543 return arrayTypes;
3546 protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
3548 // In case of dropping the namespace, we have to remove the dropped NS
3549 // so that docName will match what's in the assembly/type
3550 if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
3551 int droppedNsLength = MDocUpdater.droppedNamespace.Length;
3552 docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
3555 // need to worry about 4 forms of //@MemberName values:
3556 // 1. "Normal" (non-generic) member names: GetEnumerator
3557 // - Lookup as-is.
3558 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3559 // - try as-is, and try type.member (due to "kludge" for property
3560 // support.
3561 // 3. "Normal" Generic member names: Sort<T> (CSC)
3562 // - need to remove generic parameters --> "Sort"
3563 // 4. Explicitly-implemented interface members for generic interfaces:
3564 // -- System.Collections.Generic.IEnumerable<T>.Current
3565 // - Try as-is, and try type.member, *keeping* the generic parameters.
3566 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
3567 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
3568 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
3569 // this as (1) or (2).
3570 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
3571 // Cases 1 & 2
3572 foreach (MemberReference mi in type.GetMembers (docName))
3573 yield return mi;
3574 if (CountChars (docName, '.') > 0)
3575 // might be a property; try only type.member instead of
3576 // namespace.type.member.
3577 foreach (MemberReference mi in
3578 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
3579 yield return mi;
3580 yield break;
3582 // cases 3 & 4
3583 int numLt = 0;
3584 int numDot = 0;
3585 int startLt, startType, startMethod;
3586 startLt = startType = startMethod = -1;
3587 for (int i = 0; i < docName.Length; ++i) {
3588 switch (docName [i]) {
3589 case '<':
3590 if (numLt == 0) {
3591 startLt = i;
3593 ++numLt;
3594 break;
3595 case '>':
3596 --numLt;
3597 if (numLt == 0 && (i + 1) < docName.Length)
3598 // there's another character in docName, so this <...> sequence is
3599 // probably part of a generic type -- case 4.
3600 startLt = -1;
3601 break;
3602 case '.':
3603 startType = startMethod;
3604 startMethod = i;
3605 ++numDot;
3606 break;
3609 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
3610 // case 3
3611 foreach (MemberReference mi in type.GetMembers (refName))
3612 yield return mi;
3614 // case 4
3615 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
3616 yield return mi;
3618 // If we _still_ haven't found it, we've hit another generic naming issue:
3619 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
3620 // explicitly-implemented METHOD names (not properties), e.g.
3621 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
3622 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
3623 // which the XML docs will contain.
3625 // Alas, we can't derive the Mono name from docName, so we need to iterate
3626 // over all member names, convert them into CSC format, and compare... :-(
3627 if (numDot == 0)
3628 yield break;
3629 foreach (MemberReference mi in type.GetMembers ()) {
3630 if (MDocUpdater.GetMemberName (mi) == docName)
3631 yield return mi;
3635 static string GetReplacedString (string typeName, string[] from, string[] to)
3637 if (from == null)
3638 return typeName;
3639 for (int i = 0; i < from.Length; ++i)
3640 typeName = typeName.Replace (from [i], to [i]);
3641 return typeName;
3644 private static int CountChars (string s, char c)
3646 int count = 0;
3647 for (int i = 0; i < s.Length; ++i) {
3648 if (s [i] == c)
3649 ++count;
3651 return count;
3655 class EcmaDocumentationEnumerator : DocumentationEnumerator {
3657 XmlReader ecmadocs;
3658 MDocUpdater app;
3660 public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
3662 this.app = app;
3663 this.ecmadocs = ecmaDocs;
3666 public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
3668 HashSet<string> seen = new HashSet<string> ();
3669 return GetDocumentationTypes (assembly, forTypes, seen)
3670 .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
3673 new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
3675 int typeDepth = -1;
3676 while (ecmadocs.Read ()) {
3677 switch (ecmadocs.Name) {
3678 case "Type": {
3679 if (typeDepth == -1)
3680 typeDepth = ecmadocs.Depth;
3681 if (ecmadocs.NodeType != XmlNodeType.Element)
3682 continue;
3683 if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
3684 continue;
3685 string typename = ecmadocs.GetAttribute ("FullName");
3686 string typename2 = MDocUpdater.GetTypeFileName (typename);
3687 if (forTypes != null &&
3688 forTypes.BinarySearch (typename) < 0 &&
3689 typename != typename2 &&
3690 forTypes.BinarySearch (typename2) < 0)
3691 continue;
3692 TypeDefinition t;
3693 if ((t = assembly.GetType (typename)) == null &&
3694 (t = assembly.GetType (typename2)) == null)
3695 continue;
3696 seen.Add (typename);
3697 if (typename != typename2)
3698 seen.Add (typename2);
3699 Console.WriteLine (" Import: {0}", t.FullName);
3700 if (ecmadocs.Name != "Docs") {
3701 int depth = ecmadocs.Depth;
3702 while (ecmadocs.Read ()) {
3703 if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
3704 break;
3707 if (!ecmadocs.IsStartElement ("Docs"))
3708 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
3709 yield return t;
3710 break;
3712 default:
3713 break;
3718 public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
3720 return GetMembers (basefile, type)
3721 .Concat (base.GetDocumentationMembers (basefile, type));
3724 private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
3726 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
3727 // do nothing
3729 if (ecmadocs.IsEmptyElement)
3730 yield break;
3732 int membersDepth = ecmadocs.Depth;
3733 bool go = true;
3734 while (go && ecmadocs.Read ()) {
3735 switch (ecmadocs.Name) {
3736 case "Member": {
3737 if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
3738 continue;
3739 DocumentationMember dm = new DocumentationMember (ecmadocs);
3741 string xp = MDocUpdater.GetXPathForMember (dm);
3742 XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
3743 MemberReference m;
3744 if (oldmember == null) {
3745 m = GetMember (type, dm);
3746 if (m == null) {
3747 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3748 type.FullName, dm.MemberSignatures ["C#"]);
3749 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
3750 continue;
3752 // oldmember lookup may have failed due to type parameter renames.
3753 // Try again.
3754 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
3755 if (oldmember == null) {
3756 XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
3757 oldmember = basefile.CreateElement ("Member");
3758 oldmember.SetAttribute ("MemberName", dm.MemberName);
3759 members.AppendChild (oldmember);
3760 foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
3761 XmlElement ms = basefile.CreateElement ("MemberSignature");
3762 ms.SetAttribute ("Language", key);
3763 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
3764 oldmember.AppendChild (ms);
3766 oldmember.SetAttribute ("__monodocer-seen__", "true");
3767 Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
3768 app.additions++;
3771 else {
3772 m = GetMember (type, new DocumentationMember (oldmember));
3773 if (m == null) {
3774 app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3775 type.FullName, dm.MemberSignatures ["C#"]);
3776 continue;
3778 oldmember.SetAttribute ("__monodocer-seen__", "true");
3780 DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
3781 if (ecmadocs.Name != "Docs")
3782 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
3783 yield return node;
3784 break;
3786 case "Members":
3787 if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
3788 go = false;
3790 break;
3796 abstract class DocumentationImporter {
3798 public abstract void ImportDocumentation (DocsNodeInfo info);
3801 class MsxdocDocumentationImporter : DocumentationImporter {
3803 XmlDocument slashdocs;
3805 public MsxdocDocumentationImporter (string file)
3807 var xml = File.ReadAllText (file);
3809 // Ensure Unix line endings
3810 xml = xml.Replace ("\r", "");
3812 slashdocs = new XmlDocument();
3813 slashdocs.LoadXml (xml);
3816 public override void ImportDocumentation (DocsNodeInfo info)
3818 XmlNode elem = GetDocs (info.Member ?? info.Type);
3820 if (elem == null)
3821 return;
3823 XmlElement e = info.Node;
3825 if (elem.SelectSingleNode("summary") != null)
3826 MDocUpdater.ClearElement(e, "summary");
3827 if (elem.SelectSingleNode("remarks") != null)
3828 MDocUpdater.ClearElement(e, "remarks");
3829 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3830 MDocUpdater.ClearElement(e, "value");
3831 MDocUpdater.ClearElement(e, "returns");
3834 foreach (XmlNode child in elem.ChildNodes) {
3835 switch (child.Name) {
3836 case "param":
3837 case "typeparam": {
3838 XmlAttribute name = child.Attributes ["name"];
3839 if (name == null)
3840 break;
3841 XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3842 if (p2 != null)
3843 p2.InnerXml = child.InnerXml;
3844 break;
3846 // Occasionally XML documentation will use <returns/> on
3847 // properties, so let's try to normalize things.
3848 case "value":
3849 case "returns": {
3850 XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3851 v.InnerXml = child.InnerXml;
3852 e.AppendChild (v);
3853 break;
3855 case "altmember":
3856 case "exception":
3857 case "permission": {
3858 XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3859 if (cref == null)
3860 break;
3861 XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3862 if (a == null) {
3863 a = e.OwnerDocument.CreateElement (child.Name);
3864 a.SetAttribute ("cref", cref.Value);
3865 e.AppendChild (a);
3867 a.InnerXml = child.InnerXml;
3868 break;
3870 case "seealso": {
3871 XmlAttribute cref = child.Attributes ["cref"];
3872 if (cref == null)
3873 break;
3874 XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3875 if (a == null) {
3876 a = e.OwnerDocument.CreateElement ("altmember");
3877 a.SetAttribute ("cref", cref.Value);
3878 e.AppendChild (a);
3880 break;
3882 default: {
3883 bool add = true;
3884 if (child.NodeType == XmlNodeType.Element &&
3885 e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3886 add = false;
3887 if (add)
3888 MDocUpdater.CopyNode (child, e);
3889 break;
3895 private XmlNode GetDocs (MemberReference member)
3897 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3898 if (slashdocsig != null)
3899 return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3900 return null;
3904 class EcmaDocumentationImporter : DocumentationImporter {
3906 XmlReader ecmadocs;
3908 public EcmaDocumentationImporter (XmlReader ecmaDocs)
3910 this.ecmadocs = ecmaDocs;
3913 public override void ImportDocumentation (DocsNodeInfo info)
3915 if (!ecmadocs.IsStartElement ("Docs")) {
3916 return;
3919 XmlElement e = info.Node;
3921 int depth = ecmadocs.Depth;
3922 ecmadocs.ReadStartElement ("Docs");
3923 while (ecmadocs.Read ()) {
3924 if (ecmadocs.Name == "Docs") {
3925 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3926 break;
3927 else
3928 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3930 if (!ecmadocs.IsStartElement ())
3931 continue;
3932 switch (ecmadocs.Name) {
3933 case "param":
3934 case "typeparam": {
3935 string name = ecmadocs.GetAttribute ("name");
3936 if (name == null)
3937 break;
3938 XmlNode doc = e.SelectSingleNode (
3939 ecmadocs.Name + "[@name='" + name + "']");
3940 string value = ecmadocs.ReadInnerXml ();
3941 if (doc != null)
3942 doc.InnerXml = value.Replace ("\r", "");
3943 break;
3945 case "altmember":
3946 case "exception":
3947 case "permission":
3948 case "seealso": {
3949 string name = ecmadocs.Name;
3950 string cref = ecmadocs.GetAttribute ("cref");
3951 if (cref == null)
3952 break;
3953 XmlNode doc = e.SelectSingleNode (
3954 ecmadocs.Name + "[@cref='" + cref + "']");
3955 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3956 if (doc != null)
3957 doc.InnerXml = value;
3958 else {
3959 XmlElement n = e.OwnerDocument.CreateElement (name);
3960 n.SetAttribute ("cref", cref);
3961 n.InnerXml = value;
3962 e.AppendChild (n);
3964 break;
3966 default: {
3967 string name = ecmadocs.Name;
3968 string xpath = ecmadocs.Name;
3969 StringList attributes = new StringList (ecmadocs.AttributeCount);
3970 if (ecmadocs.MoveToFirstAttribute ()) {
3971 do {
3972 attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3973 } while (ecmadocs.MoveToNextAttribute ());
3974 ecmadocs.MoveToContent ();
3976 if (attributes.Count > 0) {
3977 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3979 XmlNode doc = e.SelectSingleNode (xpath);
3980 string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3981 if (doc != null) {
3982 doc.InnerXml = value;
3984 else {
3985 XmlElement n = e.OwnerDocument.CreateElement (name);
3986 n.InnerXml = value;
3987 foreach (string a in attributes) {
3988 int eq = a.IndexOf ('=');
3989 n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3991 e.AppendChild (n);
3993 break;
4000 class DocumentationMember {
4001 public StringToStringMap MemberSignatures = new StringToStringMap ();
4002 public string ReturnType;
4003 public StringList Parameters;
4004 public StringList TypeParameters;
4005 public string MemberName;
4006 public string MemberType;
4008 public DocumentationMember (XmlReader reader)
4010 MemberName = reader.GetAttribute ("MemberName");
4011 int depth = reader.Depth;
4012 bool go = true;
4013 StringList p = new StringList ();
4014 StringList tp = new StringList ();
4015 do {
4016 if (reader.NodeType != XmlNodeType.Element)
4017 continue;
4019 bool shouldUse = true;
4020 try {
4021 string apistyle = reader.GetAttribute ("apistyle");
4022 shouldUse = string.IsNullOrWhiteSpace(apistyle) || apistyle == "classic"; // only use this tag if it's an 'classic' style node
4024 catch (Exception ex) {}
4025 switch (reader.Name) {
4026 case "MemberSignature":
4027 if (shouldUse) {
4028 MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
4030 break;
4031 case "MemberType":
4032 MemberType = reader.ReadElementString ();
4033 break;
4034 case "ReturnType":
4035 if (reader.Depth == depth + 2 && shouldUse)
4036 ReturnType = reader.ReadElementString ();
4037 break;
4038 case "Parameter":
4039 if (reader.Depth == depth + 2 && shouldUse)
4040 p.Add (reader.GetAttribute ("Type"));
4041 break;
4042 case "TypeParameter":
4043 if (reader.Depth == depth + 2 && shouldUse)
4044 tp.Add (reader.GetAttribute ("Name"));
4045 break;
4046 case "Docs":
4047 if (reader.Depth == depth + 1)
4048 go = false;
4049 break;
4051 } while (go && reader.Read () && reader.Depth >= depth);
4052 if (p.Count > 0) {
4053 Parameters = p;
4055 if (tp.Count > 0) {
4056 TypeParameters = tp;
4057 } else {
4058 DiscernTypeParameters ();
4062 public DocumentationMember (XmlNode node)
4064 MemberName = node.Attributes ["MemberName"].Value;
4065 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
4066 XmlAttribute l = n.Attributes ["Language"];
4067 XmlAttribute v = n.Attributes ["Value"];
4068 XmlAttribute apistyle = n.Attributes ["apistyle"];
4069 bool shouldUse = apistyle == null || apistyle.Value == "classic";
4070 if (l != null && v != null && shouldUse)
4071 MemberSignatures [l.Value] = v.Value;
4073 MemberType = node.SelectSingleNode ("MemberType").InnerText;
4074 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType[not(@apistyle) or @apistyle='classic']");
4075 if (rt != null)
4076 ReturnType = rt.InnerText;
4077 XmlNodeList p = node.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
4078 if (p.Count > 0) {
4079 Parameters = new StringList (p.Count);
4080 for (int i = 0; i < p.Count; ++i)
4081 Parameters.Add (p [i].Attributes ["Type"].Value);
4083 XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
4084 if (tp.Count > 0) {
4085 TypeParameters = new StringList (tp.Count);
4086 for (int i = 0; i < tp.Count; ++i)
4087 TypeParameters.Add (tp [i].Attributes ["Name"].Value);
4089 else {
4090 DiscernTypeParameters ();
4094 void DiscernTypeParameters ()
4096 // see if we can discern the param list from the name
4097 if (MemberName.Contains ("<") && MemberName.EndsWith (">")) {
4098 var starti = MemberName.IndexOf ("<") + 1;
4099 var endi = MemberName.LastIndexOf (">");
4100 var paramlist = MemberName.Substring (starti, endi - starti);
4101 var tparams = paramlist.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
4102 TypeParameters = new StringList (tparams);
4107 public class DynamicParserContext {
4108 public ReadOnlyCollection<bool> TransformFlags;
4109 public int TransformIndex;
4111 public DynamicParserContext (ICustomAttributeProvider provider)
4113 CustomAttribute da;
4114 if (provider.HasCustomAttributes &&
4115 (da = (provider.CustomAttributes.Cast<CustomAttribute>()
4116 .SingleOrDefault (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))) != null) {
4117 CustomAttributeArgument[] values = da.ConstructorArguments.Count == 0
4118 ? new CustomAttributeArgument [0]
4119 : (CustomAttributeArgument[]) da.ConstructorArguments [0].Value;
4121 TransformFlags = new ReadOnlyCollection<bool> (values.Select (t => (bool) t.Value).ToArray());
4126 public enum MemberFormatterState {
4127 None,
4128 WithinGenericTypeParameters,
4131 public abstract class MemberFormatter {
4133 public virtual string Language {
4134 get {return "";}
4137 public string GetName (MemberReference member)
4139 return GetName (member, null);
4142 public virtual string GetName (MemberReference member, DynamicParserContext context)
4144 TypeReference type = member as TypeReference;
4145 if (type != null)
4146 return GetTypeName (type, context);
4147 MethodReference method = member as MethodReference;
4148 if (method != null && method.Name == ".ctor") // method.IsConstructor
4149 return GetConstructorName (method);
4150 if (method != null)
4151 return GetMethodName (method);
4152 PropertyReference prop = member as PropertyReference;
4153 if (prop != null)
4154 return GetPropertyName (prop);
4155 FieldReference field = member as FieldReference;
4156 if (field != null)
4157 return GetFieldName (field);
4158 EventReference e = member as EventReference;
4159 if (e != null)
4160 return GetEventName (e);
4161 throw new NotSupportedException ("Can't handle: " +
4162 (member == null ? "null" : member.GetType().ToString()));
4165 protected virtual string GetTypeName (TypeReference type)
4167 return GetTypeName (type, null);
4170 protected virtual string GetTypeName (TypeReference type, DynamicParserContext context)
4172 if (type == null)
4173 throw new ArgumentNullException ("type");
4174 return _AppendTypeName (new StringBuilder (type.Name.Length), type, context).ToString ();
4177 protected virtual char[] ArrayDelimeters {
4178 get {return new char[]{'[', ']'};}
4181 protected virtual MemberFormatterState MemberFormatterState { get; set; }
4183 protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4185 if (type is ArrayType) {
4186 TypeSpecification spec = type as TypeSpecification;
4187 _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context);
4188 return AppendArrayModifiers (buf, (ArrayType) type);
4190 if (type is ByReferenceType) {
4191 return AppendRefTypeName (buf, type, context);
4193 if (type is PointerType) {
4194 return AppendPointerTypeName (buf, type, context);
4196 if (type is GenericParameter) {
4197 return AppendTypeName (buf, type, context);
4199 AppendNamespace (buf, type);
4200 GenericInstanceType genInst = type as GenericInstanceType;
4201 if (type.GenericParameters.Count == 0 &&
4202 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
4203 return AppendFullTypeName (buf, type, context);
4205 return AppendGenericType (buf, type, context);
4208 protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4210 string ns = DocUtils.GetNamespace (type);
4211 if (ns != null && ns.Length > 0)
4212 buf.Append (ns).Append ('.');
4213 return buf;
4216 protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4218 if (type.DeclaringType != null)
4219 AppendFullTypeName (buf, type.DeclaringType, context).Append (NestedTypeSeparator);
4220 return AppendTypeName (buf, type, context);
4223 protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4225 if (context != null)
4226 context.TransformIndex++;
4227 return AppendTypeName (buf, type.Name);
4230 protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
4232 int n = typename.IndexOf ("`");
4233 if (n >= 0)
4234 return buf.Append (typename.Substring (0, n));
4235 return buf.Append (typename);
4238 protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4240 buf.Append (ArrayDelimeters [0]);
4241 int rank = array.Rank;
4242 if (rank > 1)
4243 buf.Append (new string (',', rank-1));
4244 return buf.Append (ArrayDelimeters [1]);
4247 protected virtual string RefTypeModifier {
4248 get {return "@";}
4251 protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4253 TypeSpecification spec = type as TypeSpecification;
4254 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4255 .Append (RefTypeModifier);
4258 protected virtual string PointerModifier {
4259 get {return "*";}
4262 protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4264 TypeSpecification spec = type as TypeSpecification;
4265 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType (), context)
4266 .Append (PointerModifier);
4269 protected virtual char[] GenericTypeContainer {
4270 get {return new char[]{'<', '>'};}
4273 protected virtual char NestedTypeSeparator {
4274 get {return '.';}
4277 protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4279 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4280 type is GenericInstanceType ? type.GetElementType () : type);
4281 List<TypeReference> genArgs = GetGenericArguments (type);
4282 int argIdx = 0;
4283 int prev = 0;
4284 bool insertNested = false;
4285 foreach (var decl in decls) {
4286 TypeReference declDef = decl.Resolve () ?? decl;
4287 if (insertNested) {
4288 buf.Append (NestedTypeSeparator);
4290 insertNested = true;
4291 AppendTypeName (buf, declDef, context);
4292 int ac = DocUtils.GetGenericArgumentCount (declDef);
4293 int c = ac - prev;
4294 prev = ac;
4295 if (c > 0) {
4296 buf.Append (GenericTypeContainer [0]);
4297 var origState = MemberFormatterState;
4298 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4299 _AppendTypeName (buf, genArgs [argIdx++], context);
4300 for (int i = 1; i < c; ++i) {
4301 _AppendTypeName (buf.Append (","), genArgs [argIdx++], context);
4303 MemberFormatterState = origState;
4304 buf.Append (GenericTypeContainer [1]);
4307 return buf;
4310 protected List<TypeReference> GetGenericArguments (TypeReference type)
4312 var args = new List<TypeReference> ();
4313 GenericInstanceType inst = type as GenericInstanceType;
4314 if (inst != null)
4315 args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
4316 else
4317 args.AddRange (type.GenericParameters.Cast<TypeReference> ());
4318 return args;
4321 protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4323 return buf;
4326 protected virtual string GetConstructorName (MethodReference constructor)
4328 return constructor.Name;
4331 protected virtual string GetMethodName (MethodReference method)
4333 return method.Name;
4336 protected virtual string GetPropertyName (PropertyReference property)
4338 return property.Name;
4341 protected virtual string GetFieldName (FieldReference field)
4343 return field.Name;
4346 protected virtual string GetEventName (EventReference e)
4348 return e.Name;
4351 public string GetDeclaration (MemberReference mreference)
4353 return GetDeclaration (mreference.Resolve ());
4356 string GetDeclaration (IMemberDefinition member)
4358 if (member == null)
4359 throw new ArgumentNullException ("member");
4360 TypeDefinition type = member as TypeDefinition;
4361 if (type != null)
4362 return GetTypeDeclaration (type);
4363 MethodDefinition method = member as MethodDefinition;
4364 if (method != null && method.IsConstructor)
4365 return GetConstructorDeclaration (method);
4366 if (method != null)
4367 return GetMethodDeclaration (method);
4368 PropertyDefinition prop = member as PropertyDefinition;
4369 if (prop != null)
4370 return GetPropertyDeclaration (prop);
4371 FieldDefinition field = member as FieldDefinition;
4372 if (field != null)
4373 return GetFieldDeclaration (field);
4374 EventDefinition e = member as EventDefinition;
4375 if (e != null)
4376 return GetEventDeclaration (e);
4377 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
4380 protected virtual string GetTypeDeclaration (TypeDefinition type)
4382 if (type == null)
4383 throw new ArgumentNullException ("type");
4384 StringBuilder buf = new StringBuilder (type.Name.Length);
4385 _AppendTypeName (buf, type, null);
4386 AppendGenericTypeConstraints (buf, type);
4387 return buf.ToString ();
4390 protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
4392 return GetConstructorName (constructor);
4395 protected virtual string GetMethodDeclaration (MethodDefinition method)
4397 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
4398 ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
4399 return null;
4401 // Special signature for destructors.
4402 if (method.Name == "Finalize" && method.Parameters.Count == 0)
4403 return GetFinalizerName (method);
4405 StringBuilder buf = new StringBuilder ();
4407 AppendVisibility (buf, method);
4408 if (buf.Length == 0 &&
4409 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
4410 return null;
4412 AppendModifiers (buf, method);
4414 if (buf.Length != 0)
4415 buf.Append (" ");
4416 buf.Append (GetTypeName (method.ReturnType, new DynamicParserContext (method.MethodReturnType))).Append (" ");
4418 AppendMethodName (buf, method);
4419 AppendGenericMethod (buf, method).Append (" ");
4420 AppendParameters (buf, method, method.Parameters);
4421 AppendGenericMethodConstraints (buf, method);
4422 return buf.ToString ();
4425 protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4427 return buf.Append (method.Name);
4430 protected virtual string GetFinalizerName (MethodDefinition method)
4432 return "Finalize";
4435 protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4437 return buf;
4440 protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4442 return buf;
4445 protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4447 return buf;
4450 protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4452 return buf;
4455 protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4457 return buf;
4460 protected virtual string GetPropertyDeclaration (PropertyDefinition property)
4462 return GetPropertyName (property);
4465 protected virtual string GetFieldDeclaration (FieldDefinition field)
4467 return GetFieldName (field);
4470 protected virtual string GetEventDeclaration (EventDefinition e)
4472 return GetEventName (e);
4476 class ILFullMemberFormatter : MemberFormatter {
4478 public override string Language {
4479 get {return "ILAsm";}
4482 protected override char NestedTypeSeparator {
4483 get {
4484 return '/';
4488 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4490 if (GetBuiltinType (type.FullName) != null)
4491 return buf;
4492 string ns = DocUtils.GetNamespace (type);
4493 if (ns != null && ns.Length > 0) {
4494 if (type.IsValueType)
4495 buf.Append ("valuetype ");
4496 else
4497 buf.Append ("class ");
4498 buf.Append (ns).Append ('.');
4500 return buf;
4503 protected static string GetBuiltinType (string t)
4505 switch (t) {
4506 case "System.Byte": return "unsigned int8";
4507 case "System.SByte": return "int8";
4508 case "System.Int16": return "int16";
4509 case "System.Int32": return "int32";
4510 case "System.Int64": return "int64";
4511 case "System.IntPtr": return "native int";
4513 case "System.UInt16": return "unsigned int16";
4514 case "System.UInt32": return "unsigned int32";
4515 case "System.UInt64": return "unsigned int64";
4516 case "System.UIntPtr": return "native unsigned int";
4518 case "System.Single": return "float32";
4519 case "System.Double": return "float64";
4520 case "System.Boolean": return "bool";
4521 case "System.Char": return "char";
4522 case "System.Void": return "void";
4523 case "System.String": return "string";
4524 case "System.Object": return "object";
4526 return null;
4529 protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
4531 return buf.Append (typename);
4534 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
4536 if (type is GenericParameter) {
4537 AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4538 return buf;
4541 string s = GetBuiltinType (type.FullName);
4542 if (s != null) {
4543 return buf.Append (s);
4545 return base.AppendTypeName (buf, type, context);
4548 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4550 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
4551 return buf.Append (type.Owner is TypeReference ? "!" : "!!");
4553 GenericParameterAttributes attrs = type.Attributes;
4554 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
4555 buf.Append ("class ");
4556 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
4557 buf.Append ("struct ");
4558 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
4559 buf.Append (".ctor ");
4560 IList<TypeReference> constraints = type.Constraints;
4561 MemberFormatterState = 0;
4562 if (constraints.Count > 0) {
4563 var full = new ILFullMemberFormatter ();
4564 buf.Append ("(").Append (full.GetName (constraints [0]));
4565 for (int i = 1; i < constraints.Count; ++i) {
4566 buf.Append (", ").Append (full.GetName (constraints [i]));
4568 buf.Append (") ");
4570 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4572 if ((attrs & GenericParameterAttributes.Covariant) != 0)
4573 buf.Append ("+ ");
4574 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
4575 buf.Append ("- ");
4576 return buf;
4579 protected override string GetTypeDeclaration (TypeDefinition type)
4581 string visibility = GetTypeVisibility (type.Attributes);
4582 if (visibility == null)
4583 return null;
4585 StringBuilder buf = new StringBuilder ();
4587 buf.Append (".class ");
4588 if (type.IsNested)
4589 buf.Append ("nested ");
4590 buf.Append (visibility).Append (" ");
4591 if (type.IsInterface)
4592 buf.Append ("interface ");
4593 if (type.IsSequentialLayout)
4594 buf.Append ("sequential ");
4595 if (type.IsAutoLayout)
4596 buf.Append ("auto ");
4597 if (type.IsAnsiClass)
4598 buf.Append ("ansi ");
4599 if (type.IsAbstract)
4600 buf.Append ("abstract ");
4601 if (type.IsSerializable)
4602 buf.Append ("serializable ");
4603 if (type.IsSealed)
4604 buf.Append ("sealed ");
4605 if (type.IsBeforeFieldInit)
4606 buf.Append ("beforefieldinit ");
4607 var state = MemberFormatterState;
4608 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4609 buf.Append (GetName (type));
4610 MemberFormatterState = state;
4611 var full = new ILFullMemberFormatter ();
4612 if (type.BaseType != null) {
4613 buf.Append (" extends ");
4614 if (type.BaseType.FullName == "System.Object")
4615 buf.Append ("System.Object");
4616 else
4617 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
4619 bool first = true;
4620 foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.InterfaceType.Resolve ()))
4621 .Select (i => full.GetName (i.InterfaceType))
4622 .OrderBy (n => n)) {
4623 if (first) {
4624 buf.Append (" implements ");
4625 first = false;
4627 else {
4628 buf.Append (", ");
4630 buf.Append (name);
4633 return buf.ToString ();
4636 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
4638 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
4639 type is GenericInstanceType ? type.GetElementType () : type);
4640 bool first = true;
4641 foreach (var decl in decls) {
4642 TypeReference declDef = decl.Resolve () ?? decl;
4643 if (!first) {
4644 buf.Append (NestedTypeSeparator);
4646 first = false;
4647 AppendTypeName (buf, declDef, context);
4649 buf.Append ('<');
4650 first = true;
4651 foreach (TypeReference arg in GetGenericArguments (type)) {
4652 if (!first)
4653 buf.Append (", ");
4654 first = false;
4655 _AppendTypeName (buf, arg, context);
4657 buf.Append ('>');
4658 return buf;
4661 static string GetTypeVisibility (TypeAttributes ta)
4663 switch (ta & TypeAttributes.VisibilityMask) {
4664 case TypeAttributes.Public:
4665 case TypeAttributes.NestedPublic:
4666 return "public";
4668 case TypeAttributes.NestedFamily:
4669 case TypeAttributes.NestedFamORAssem:
4670 return "protected";
4672 default:
4673 return null;
4677 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4679 return GetMethodDeclaration (constructor);
4682 protected override string GetMethodDeclaration (MethodDefinition method)
4684 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
4685 return null;
4687 var buf = new StringBuilder ();
4688 buf.Append (".method ");
4689 AppendVisibility (buf, method);
4690 if (method.IsStatic)
4691 buf.Append ("static ");
4692 if (method.IsHideBySig)
4693 buf.Append ("hidebysig ");
4694 if (method.IsPInvokeImpl) {
4695 var info = method.PInvokeInfo;
4696 buf.Append ("pinvokeimpl (\"")
4697 .Append (info.Module.Name)
4698 .Append ("\" as \"")
4699 .Append (info.EntryPoint)
4700 .Append ("\"");
4701 if (info.IsCharSetAuto)
4702 buf.Append (" auto");
4703 if (info.IsCharSetUnicode)
4704 buf.Append (" unicode");
4705 if (info.IsCharSetAnsi)
4706 buf.Append (" ansi");
4707 if (info.IsCallConvCdecl)
4708 buf.Append (" cdecl");
4709 if (info.IsCallConvStdCall)
4710 buf.Append (" stdcall");
4711 if (info.IsCallConvWinapi)
4712 buf.Append (" winapi");
4713 if (info.IsCallConvThiscall)
4714 buf.Append (" thiscall");
4715 if (info.SupportsLastError)
4716 buf.Append (" lasterr");
4717 buf.Append (")");
4719 if (method.IsSpecialName)
4720 buf.Append ("specialname ");
4721 if (method.IsRuntimeSpecialName)
4722 buf.Append ("rtspecialname ");
4723 if (method.IsNewSlot)
4724 buf.Append ("newslot ");
4725 if (method.IsVirtual)
4726 buf.Append ("virtual ");
4727 if (!method.IsStatic)
4728 buf.Append ("instance ");
4729 _AppendTypeName (buf, method.ReturnType, new DynamicParserContext (method.MethodReturnType));
4730 buf.Append (' ')
4731 .Append (method.Name);
4732 if (method.IsGenericMethod ()) {
4733 var state = MemberFormatterState;
4734 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
4735 IList<GenericParameter> args = method.GenericParameters;
4736 if (args.Count > 0) {
4737 buf.Append ("<");
4738 _AppendTypeName (buf, args [0], null);
4739 for (int i = 1; i < args.Count; ++i)
4740 _AppendTypeName (buf.Append (", "), args [i], null);
4741 buf.Append (">");
4743 MemberFormatterState = state;
4746 buf.Append ('(');
4747 bool first = true;
4748 for (int i = 0; i < method.Parameters.Count; ++i) {
4749 if (!first)
4750 buf.Append (", ");
4751 first = false;
4752 _AppendTypeName (buf, method.Parameters [i].ParameterType, new DynamicParserContext (method.Parameters [i]));
4753 buf.Append (' ');
4754 buf.Append (method.Parameters [i].Name);
4756 buf.Append (')');
4757 if (method.IsIL)
4758 buf.Append (" cil");
4759 if (method.IsRuntime)
4760 buf.Append (" runtime");
4761 if (method.IsManaged)
4762 buf.Append (" managed");
4764 return buf.ToString ();
4767 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4769 if (DocUtils.IsExplicitlyImplemented (method)) {
4770 TypeReference iface;
4771 MethodReference ifaceMethod;
4772 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4773 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4774 .Append ('.')
4775 .Append (ifaceMethod.Name);
4777 return base.AppendMethodName (buf, method);
4780 protected override string RefTypeModifier {
4781 get {return "";}
4784 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4786 if (method.IsPublic)
4787 return buf.Append ("public ");
4788 if (method.IsFamilyAndAssembly)
4789 return buf.Append ("familyandassembly");
4790 if (method.IsFamilyOrAssembly)
4791 return buf.Append ("familyorassembly");
4792 if (method.IsFamily)
4793 return buf.Append ("family");
4794 return buf;
4797 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4799 string modifiers = String.Empty;
4800 if (method.IsStatic) modifiers += " static";
4801 if (method.IsVirtual && !method.IsAbstract) {
4802 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4803 else modifiers += " override";
4805 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4806 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4807 if (method.IsFinal) modifiers += " sealed";
4808 if (modifiers == " virtual sealed") modifiers = "";
4810 return buf.Append (modifiers);
4813 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4815 if (method.IsGenericMethod ()) {
4816 IList<GenericParameter> args = method.GenericParameters;
4817 if (args.Count > 0) {
4818 buf.Append ("<");
4819 buf.Append (args [0].Name);
4820 for (int i = 1; i < args.Count; ++i)
4821 buf.Append (",").Append (args [i].Name);
4822 buf.Append (">");
4825 return buf;
4828 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4830 return AppendParameters (buf, method, parameters, '(', ')');
4833 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4835 buf.Append (begin);
4837 if (parameters.Count > 0) {
4838 if (DocUtils.IsExtensionMethod (method))
4839 buf.Append ("this ");
4840 AppendParameter (buf, parameters [0]);
4841 for (int i = 1; i < parameters.Count; ++i) {
4842 buf.Append (", ");
4843 AppendParameter (buf, parameters [i]);
4847 return buf.Append (end);
4850 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4852 if (parameter.ParameterType is ByReferenceType) {
4853 if (parameter.IsOut)
4854 buf.Append ("out ");
4855 else
4856 buf.Append ("ref ");
4858 buf.Append (GetName (parameter.ParameterType)).Append (" ");
4859 return buf.Append (parameter.Name);
4862 protected override string GetPropertyDeclaration (PropertyDefinition property)
4864 MethodDefinition gm = null, sm = null;
4866 string get_visible = null;
4867 if ((gm = property.GetMethod) != null &&
4868 (DocUtils.IsExplicitlyImplemented (gm) ||
4869 (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
4870 get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
4871 string set_visible = null;
4872 if ((sm = property.SetMethod) != null &&
4873 (DocUtils.IsExplicitlyImplemented (sm) ||
4874 (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
4875 set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
4877 if ((set_visible == null) && (get_visible == null))
4878 return null;
4880 StringBuilder buf = new StringBuilder ()
4881 .Append (".property ");
4882 if (!(gm ?? sm).IsStatic)
4883 buf.Append ("instance ");
4884 _AppendTypeName (buf, property.PropertyType, new DynamicParserContext (property));
4885 buf.Append (' ').Append (property.Name);
4886 if (!property.HasParameters || property.Parameters.Count == 0)
4887 return buf.ToString ();
4889 buf.Append ('(');
4890 bool first = true;
4891 foreach (ParameterDefinition p in property.Parameters) {
4892 if (!first)
4893 buf.Append (", ");
4894 first = false;
4895 _AppendTypeName (buf, p.ParameterType, new DynamicParserContext (p));
4897 buf.Append (')');
4899 return buf.ToString ();
4902 protected override string GetFieldDeclaration (FieldDefinition field)
4904 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4905 if (declType.IsEnum && field.Name == "value__")
4906 return null; // This member of enums aren't documented.
4908 StringBuilder buf = new StringBuilder ();
4909 AppendFieldVisibility (buf, field);
4910 if (buf.Length == 0)
4911 return null;
4913 buf.Insert (0, ".field ");
4915 if (field.IsStatic)
4916 buf.Append ("static ");
4917 if (field.IsInitOnly)
4918 buf.Append ("initonly ");
4919 if (field.IsLiteral)
4920 buf.Append ("literal ");
4921 _AppendTypeName (buf, field.FieldType, new DynamicParserContext (field));
4922 buf.Append (' ').Append (field.Name);
4923 AppendFieldValue (buf, field);
4925 return buf.ToString ();
4928 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4930 if (field.IsPublic)
4931 return buf.Append ("public ");
4932 if (field.IsFamilyAndAssembly)
4933 return buf.Append ("familyandassembly ");
4934 if (field.IsFamilyOrAssembly)
4935 return buf.Append ("familyorassembly ");
4936 if (field.IsFamily)
4937 return buf.Append ("family ");
4938 return buf;
4941 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4943 // enums have a value__ field, which we ignore
4944 if (field.DeclaringType.IsGenericType ())
4945 return buf;
4946 if (field.HasConstant && field.IsLiteral) {
4947 object val = null;
4948 try {
4949 val = field.Constant;
4950 } catch {
4951 return buf;
4953 if (val == null)
4954 buf.Append (" = ").Append ("null");
4955 else if (val is Enum)
4956 buf.Append (" = ")
4957 .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4958 .Append ('(')
4959 .Append (val.ToString ())
4960 .Append (')');
4961 else if (val is IFormattable) {
4962 string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
4963 buf.Append (" = ");
4964 if (val is string)
4965 buf.Append ("\"" + value + "\"");
4966 else
4967 buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4968 .Append ('(')
4969 .Append (value)
4970 .Append (')');
4973 return buf;
4976 protected override string GetEventDeclaration (EventDefinition e)
4978 StringBuilder buf = new StringBuilder ();
4979 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4980 return null;
4983 buf.Length = 0;
4984 buf.Append (".event ")
4985 .Append (GetName (e.EventType))
4986 .Append (' ')
4987 .Append (e.Name);
4989 return buf.ToString ();
4993 class ILMemberFormatter : ILFullMemberFormatter {
4994 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4996 return buf;
5000 class ILNativeTypeMemberFormatter : ILFullMemberFormatter {
5001 protected static string _GetBuiltinType (string t)
5003 //string moddedType = base.GetBuiltinType (t);
5004 return null;
5005 //return moddedType;
5009 class CSharpNativeTypeMemberFormatter : CSharpFullMemberFormatter {
5010 protected override string GetCSharpType (string t) {
5011 string moddedType = base.GetCSharpType (t);
5013 switch (moddedType) {
5014 case "int": return "nint";
5015 case "uint":
5016 return "nuint";
5017 case "float":
5018 return "nfloat";
5019 case "System.Drawing.SizeF":
5020 return "CoreGraphics.CGSize";
5021 case "System.Drawing.PointF":
5022 return "CoreGraphics.CGPoint";
5023 case "System.Drawing.RectangleF":
5024 return "CoreGraphics.CGPoint";
5026 return null;
5030 class CSharpFullMemberFormatter : MemberFormatter {
5032 public override string Language {
5033 get {return "C#";}
5036 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5039 string ns = DocUtils.GetNamespace (type);
5040 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
5041 buf.Append (ns).Append ('.');
5042 return buf;
5045 protected virtual string GetCSharpType (string t)
5047 switch (t) {
5048 case "System.Byte": return "byte";
5049 case "System.SByte": return "sbyte";
5050 case "System.Int16": return "short";
5051 case "System.Int32": return "int";
5052 case "System.Int64": return "long";
5054 case "System.UInt16": return "ushort";
5055 case "System.UInt32": return "uint";
5056 case "System.UInt64": return "ulong";
5058 case "System.Single": return "float";
5059 case "System.Double": return "double";
5060 case "System.Decimal": return "decimal";
5061 case "System.Boolean": return "bool";
5062 case "System.Char": return "char";
5063 case "System.Void": return "void";
5064 case "System.String": return "string";
5065 case "System.Object": return "object";
5067 return null;
5070 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5072 if (context != null && context.TransformFlags != null &&
5073 (context.TransformFlags.Count == 0 || context.TransformFlags [context.TransformIndex])) {
5074 context.TransformIndex++;
5075 return buf.Append ("dynamic");
5078 if (type is GenericParameter)
5079 return AppendGenericParameterConstraints (buf, (GenericParameter) type, context).Append (type.Name);
5080 string t = type.FullName;
5081 if (!t.StartsWith ("System.")) {
5082 return base.AppendTypeName (buf, type, context);
5085 string s = GetCSharpType (t);
5086 if (s != null) {
5087 if (context != null)
5088 context.TransformIndex++;
5089 return buf.Append (s);
5092 return base.AppendTypeName (buf, type, context);
5095 private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type, DynamicParserContext context)
5097 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
5098 return buf;
5099 GenericParameterAttributes attrs = type.Attributes;
5100 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
5101 bool isin = (attrs & GenericParameterAttributes.Contravariant) != 0;
5102 if (isin)
5103 buf.Append ("in ");
5104 else if (isout)
5105 buf.Append ("out ");
5106 return buf;
5109 protected override string GetTypeDeclaration (TypeDefinition type)
5111 string visibility = GetTypeVisibility (type.Attributes);
5112 if (visibility == null)
5113 return null;
5115 StringBuilder buf = new StringBuilder ();
5117 buf.Append (visibility);
5118 buf.Append (" ");
5120 MemberFormatter full = new CSharpFullMemberFormatter ();
5122 if (DocUtils.IsDelegate (type)) {
5123 buf.Append("delegate ");
5124 MethodDefinition invoke = type.GetMethod ("Invoke");
5125 buf.Append (full.GetName (invoke.ReturnType, new DynamicParserContext (invoke.MethodReturnType))).Append (" ");
5126 buf.Append (GetName (type));
5127 AppendParameters (buf, invoke, invoke.Parameters);
5128 AppendGenericTypeConstraints (buf, type);
5129 buf.Append (";");
5131 return buf.ToString();
5134 if (type.IsAbstract && !type.IsInterface)
5135 buf.Append("abstract ");
5136 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
5137 buf.Append("sealed ");
5138 buf.Replace ("abstract sealed", "static");
5140 buf.Append (GetTypeKind (type));
5141 buf.Append (" ");
5142 buf.Append (GetCSharpType (type.FullName) == null
5143 ? GetName (type)
5144 : type.Name);
5146 if (!type.IsEnum) {
5147 TypeReference basetype = type.BaseType;
5148 if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) // FIXME
5149 basetype = null;
5151 List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
5152 .Select (iface => full.GetName (iface))
5153 .OrderBy (s => s)
5154 .ToList ();
5156 if (basetype != null || interface_names.Count > 0)
5157 buf.Append (" : ");
5159 if (basetype != null) {
5160 buf.Append (full.GetName (basetype));
5161 if (interface_names.Count > 0)
5162 buf.Append (", ");
5165 for (int i = 0; i < interface_names.Count; i++){
5166 if (i != 0)
5167 buf.Append (", ");
5168 buf.Append (interface_names [i]);
5170 AppendGenericTypeConstraints (buf, type);
5173 return buf.ToString ();
5176 static string GetTypeKind (TypeDefinition t)
5178 if (t.IsEnum)
5179 return "enum";
5180 if (t.IsValueType)
5181 return "struct";
5182 if (t.IsClass || t.FullName == "System.Enum")
5183 return "class";
5184 if (t.IsInterface)
5185 return "interface";
5186 throw new ArgumentException(t.FullName);
5189 static string GetTypeVisibility (TypeAttributes ta)
5191 switch (ta & TypeAttributes.VisibilityMask) {
5192 case TypeAttributes.Public:
5193 case TypeAttributes.NestedPublic:
5194 return "public";
5196 case TypeAttributes.NestedFamily:
5197 case TypeAttributes.NestedFamORAssem:
5198 return "protected";
5200 default:
5201 return null;
5205 protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
5207 if (type.GenericParameters.Count == 0)
5208 return buf;
5209 return AppendConstraints (buf, type.GenericParameters);
5212 private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
5214 foreach (GenericParameter genArg in genArgs) {
5215 GenericParameterAttributes attrs = genArg.Attributes;
5216 IList<TypeReference> constraints = genArg.Constraints;
5217 if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
5218 continue;
5220 bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
5221 bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
5222 bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
5223 bool comma = false;
5225 if (!isref && !isvt && !isnew && constraints.Count == 0)
5226 continue;
5227 buf.Append (" where ").Append (genArg.Name).Append (" : ");
5228 if (isref) {
5229 buf.Append ("class");
5230 comma = true;
5232 else if (isvt) {
5233 buf.Append ("struct");
5234 comma = true;
5236 if (constraints.Count > 0 && !isvt) {
5237 if (comma)
5238 buf.Append (", ");
5239 buf.Append (GetTypeName (constraints [0]));
5240 for (int i = 1; i < constraints.Count; ++i)
5241 buf.Append (", ").Append (GetTypeName (constraints [i]));
5243 if (isnew && !isvt) {
5244 if (comma)
5245 buf.Append (", ");
5246 buf.Append ("new()");
5249 return buf;
5252 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5254 StringBuilder buf = new StringBuilder ();
5255 AppendVisibility (buf, constructor);
5256 if (buf.Length == 0)
5257 return null;
5259 buf.Append (' ');
5260 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
5261 AppendParameters (buf, constructor, constructor.Parameters);
5262 buf.Append (';');
5264 return buf.ToString ();
5267 protected override string GetMethodDeclaration (MethodDefinition method)
5269 string decl = base.GetMethodDeclaration (method);
5270 if (decl != null)
5271 return decl + ";";
5272 return null;
5275 protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
5277 if (DocUtils.IsExplicitlyImplemented (method)) {
5278 TypeReference iface;
5279 MethodReference ifaceMethod;
5280 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5281 return buf.Append (new CSharpMemberFormatter ().GetName (iface))
5282 .Append ('.')
5283 .Append (ifaceMethod.Name);
5285 return base.AppendMethodName (buf, method);
5288 protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
5290 if (method.GenericParameters.Count == 0)
5291 return buf;
5292 return AppendConstraints (buf, method.GenericParameters);
5295 protected override string RefTypeModifier {
5296 get {return "";}
5299 protected override string GetFinalizerName (MethodDefinition method)
5301 return "~" + method.DeclaringType.Name + " ()";
5304 protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
5306 if (method == null)
5307 return buf;
5308 if (method.IsPublic)
5309 return buf.Append ("public");
5310 if (method.IsFamily || method.IsFamilyOrAssembly)
5311 return buf.Append ("protected");
5312 return buf;
5315 protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
5317 string modifiers = String.Empty;
5318 if (method.IsStatic) modifiers += " static";
5319 if (method.IsVirtual && !method.IsAbstract) {
5320 if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
5321 else modifiers += " override";
5323 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
5324 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
5325 if (method.IsFinal) modifiers += " sealed";
5326 if (modifiers == " virtual sealed") modifiers = "";
5328 return buf.Append (modifiers);
5331 protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
5333 if (method.IsGenericMethod ()) {
5334 IList<GenericParameter> args = method.GenericParameters;
5335 if (args.Count > 0) {
5336 buf.Append ("<");
5337 buf.Append (args [0].Name);
5338 for (int i = 1; i < args.Count; ++i)
5339 buf.Append (",").Append (args [i].Name);
5340 buf.Append (">");
5343 return buf;
5346 protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
5348 return AppendParameters (buf, method, parameters, '(', ')');
5351 private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
5353 buf.Append (begin);
5355 if (parameters.Count > 0) {
5356 if (DocUtils.IsExtensionMethod (method))
5357 buf.Append ("this ");
5358 AppendParameter (buf, parameters [0]);
5359 for (int i = 1; i < parameters.Count; ++i) {
5360 buf.Append (", ");
5361 AppendParameter (buf, parameters [i]);
5365 return buf.Append (end);
5368 private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
5370 if (parameter.ParameterType is ByReferenceType) {
5371 if (parameter.IsOut)
5372 buf.Append ("out ");
5373 else
5374 buf.Append ("ref ");
5376 buf.Append (GetTypeName (parameter.ParameterType, new DynamicParserContext (parameter))).Append (" ");
5377 buf.Append (parameter.Name);
5378 if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant) {
5379 buf.AppendFormat (" = {0}", MDocUpdater.MakeAttributesValueString (parameter.Constant, parameter.ParameterType));
5381 return buf;
5384 protected override string GetPropertyDeclaration (PropertyDefinition property)
5386 MethodDefinition method;
5388 string get_visible = null;
5389 if ((method = property.GetMethod) != null &&
5390 (DocUtils.IsExplicitlyImplemented (method) ||
5391 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5392 get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5393 string set_visible = null;
5394 if ((method = property.SetMethod) != null &&
5395 (DocUtils.IsExplicitlyImplemented (method) ||
5396 (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
5397 set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
5399 if ((set_visible == null) && (get_visible == null))
5400 return null;
5402 string visibility;
5403 StringBuilder buf = new StringBuilder ();
5404 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
5405 buf.Append (visibility = get_visible);
5406 else if (set_visible != null && get_visible == null)
5407 buf.Append (visibility = set_visible);
5408 else
5409 buf.Append (visibility = "public");
5411 // Pick an accessor to use for static/virtual/override/etc. checks.
5412 method = property.SetMethod;
5413 if (method == null)
5414 method = property.GetMethod;
5416 string modifiers = String.Empty;
5417 if (method.IsStatic) modifiers += " static";
5418 if (method.IsVirtual && !method.IsAbstract) {
5419 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
5420 modifiers += " virtual";
5421 else
5422 modifiers += " override";
5424 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
5425 if (method.IsAbstract && !declDef.IsInterface)
5426 modifiers += " abstract";
5427 if (method.IsFinal)
5428 modifiers += " sealed";
5429 if (modifiers == " virtual sealed")
5430 modifiers = "";
5431 buf.Append (modifiers).Append (' ');
5433 buf.Append (GetTypeName (property.PropertyType, new DynamicParserContext (property))).Append (' ');
5435 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
5436 string name = property.Name;
5437 foreach (MemberReference mi in defs) {
5438 if (mi == property) {
5439 name = "this";
5440 break;
5443 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
5445 if (property.Parameters.Count != 0) {
5446 AppendParameters (buf, method, property.Parameters, '[', ']');
5449 buf.Append (" {");
5450 if (get_visible != null) {
5451 if (get_visible != visibility)
5452 buf.Append (' ').Append (get_visible);
5453 buf.Append (" get;");
5455 if (set_visible != null) {
5456 if (set_visible != visibility)
5457 buf.Append (' ').Append (set_visible);
5458 buf.Append (" set;");
5460 buf.Append (" }");
5462 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
5465 protected override string GetFieldDeclaration (FieldDefinition field)
5467 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
5468 if (declType.IsEnum && field.Name == "value__")
5469 return null; // This member of enums aren't documented.
5471 StringBuilder buf = new StringBuilder ();
5472 AppendFieldVisibility (buf, field);
5473 if (buf.Length == 0)
5474 return null;
5476 if (declType.IsEnum)
5477 return field.Name;
5479 if (field.IsStatic && !field.IsLiteral)
5480 buf.Append (" static");
5481 if (field.IsInitOnly)
5482 buf.Append (" readonly");
5483 if (field.IsLiteral)
5484 buf.Append (" const");
5486 buf.Append (' ').Append (GetTypeName (field.FieldType, new DynamicParserContext (field))).Append (' ');
5487 buf.Append (field.Name);
5488 AppendFieldValue (buf, field);
5489 buf.Append (';');
5491 return buf.ToString ();
5494 static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
5496 if (field.IsPublic)
5497 return buf.Append ("public");
5498 if (field.IsFamily || field.IsFamilyOrAssembly)
5499 return buf.Append ("protected");
5500 return buf;
5503 static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
5505 // enums have a value__ field, which we ignore
5506 if (((TypeDefinition ) field.DeclaringType).IsEnum ||
5507 field.DeclaringType.IsGenericType ())
5508 return buf;
5509 if (field.HasConstant && field.IsLiteral) {
5510 object val = null;
5511 try {
5512 val = field.Constant;
5513 } catch {
5514 return buf;
5516 if (val == null)
5517 buf.Append (" = ").Append ("null");
5518 else if (val is Enum)
5519 buf.Append (" = ").Append (val.ToString ());
5520 else if (val is IFormattable) {
5521 string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
5522 if (val is string)
5523 value = "\"" + value + "\"";
5524 buf.Append (" = ").Append (value);
5527 return buf;
5530 protected override string GetEventDeclaration (EventDefinition e)
5532 StringBuilder buf = new StringBuilder ();
5533 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
5534 return null;
5537 AppendModifiers (buf, e.AddMethod);
5539 buf.Append (" event ");
5540 buf.Append (GetTypeName (e.EventType, new DynamicParserContext (e.AddMethod.Parameters [0]))).Append (' ');
5541 buf.Append (e.Name).Append (';');
5543 return buf.ToString ();
5547 class CSharpMemberFormatter : CSharpFullMemberFormatter {
5548 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5550 return buf;
5554 class DocTypeFullMemberFormatter : MemberFormatter {
5555 public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
5557 protected override char NestedTypeSeparator {
5558 get {return '+';}
5562 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
5563 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5565 return buf;
5569 class SlashDocMemberFormatter : MemberFormatter {
5571 protected override char[] GenericTypeContainer {
5572 get {return new char[]{'{', '}'};}
5575 private bool AddTypeCount = true;
5577 private TypeReference genDeclType;
5578 private MethodReference genDeclMethod;
5580 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type, DynamicParserContext context)
5582 if (type is GenericParameter) {
5583 int l = buf.Length;
5584 if (genDeclType != null) {
5585 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
5586 for (int i = 0; i < genArgs.Count; ++i) {
5587 if (genArgs [i].Name == type.Name) {
5588 buf.Append ('`').Append (i);
5589 break;
5593 if (genDeclMethod != null) {
5594 IList<GenericParameter> genArgs = null;
5595 if (genDeclMethod.IsGenericMethod ()) {
5596 genArgs = genDeclMethod.GenericParameters;
5597 for (int i = 0; i < genArgs.Count; ++i) {
5598 if (genArgs [i].Name == type.Name) {
5599 buf.Append ("``").Append (i);
5600 break;
5605 if (genDeclType == null && genDeclMethod == null) {
5606 // Probably from within an explicitly implemented interface member,
5607 // where CSC uses parameter names instead of indices (why?), e.g.
5608 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
5609 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
5610 buf.Append (type.Name);
5612 if (buf.Length == l) {
5613 throw new Exception (string.Format (
5614 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
5615 type.Name, genDeclType, genDeclMethod));
5618 else {
5619 base.AppendTypeName (buf, type, context);
5620 if (AddTypeCount) {
5621 int numArgs = type.GenericParameters.Count;
5622 if (type.DeclaringType != null)
5623 numArgs -= type.GenericParameters.Count;
5624 if (numArgs > 0) {
5625 buf.Append ('`').Append (numArgs);
5629 return buf;
5632 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5634 buf.Append (ArrayDelimeters [0]);
5635 int rank = array.Rank;
5636 if (rank > 1) {
5637 buf.Append ("0:");
5638 for (int i = 1; i < rank; ++i) {
5639 buf.Append (",0:");
5642 return buf.Append (ArrayDelimeters [1]);
5645 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5647 if (!AddTypeCount)
5648 base.AppendGenericType (buf, type, context);
5649 else
5650 AppendType (buf, type, context);
5651 return buf;
5654 private StringBuilder AppendType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5656 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
5657 bool insertNested = false;
5658 int prevParamCount = 0;
5659 foreach (var decl in decls) {
5660 if (insertNested)
5661 buf.Append (NestedTypeSeparator);
5662 insertNested = true;
5663 base.AppendTypeName (buf, decl, context);
5664 int argCount = DocUtils.GetGenericArgumentCount (decl);
5665 int numArgs = argCount - prevParamCount;
5666 prevParamCount = argCount;
5667 if (numArgs > 0)
5668 buf.Append ('`').Append (numArgs);
5670 return buf;
5673 protected override string GetConstructorName (MethodReference constructor)
5675 return GetMethodDefinitionName (constructor, "#ctor");
5678 protected override string GetMethodName (MethodReference method)
5680 string name = null;
5681 MethodDefinition methodDef = method as MethodDefinition;
5682 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
5683 name = method.Name;
5684 else {
5685 TypeReference iface;
5686 MethodReference ifaceMethod;
5687 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
5688 AddTypeCount = false;
5689 name = GetTypeName (iface) + "." + ifaceMethod.Name;
5690 AddTypeCount = true;
5692 return GetMethodDefinitionName (method, name);
5695 private string GetMethodDefinitionName (MethodReference method, string name)
5697 StringBuilder buf = new StringBuilder ();
5698 buf.Append (GetTypeName (method.DeclaringType));
5699 buf.Append ('.');
5700 buf.Append (name.Replace (".", "#"));
5701 if (method.IsGenericMethod ()) {
5702 IList<GenericParameter> genArgs = method.GenericParameters;
5703 if (genArgs.Count > 0)
5704 buf.Append ("``").Append (genArgs.Count);
5706 IList<ParameterDefinition> parameters = method.Parameters;
5707 try {
5708 genDeclType = method.DeclaringType;
5709 genDeclMethod = method;
5710 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5712 finally {
5713 genDeclType = null;
5714 genDeclMethod = null;
5716 return buf.ToString ();
5719 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5721 if (parameters.Count == 0)
5722 return buf;
5724 buf.Append ('(');
5726 AppendParameter (buf, genArgs, parameters [0]);
5727 for (int i = 1; i < parameters.Count; ++i) {
5728 buf.Append (',');
5729 AppendParameter (buf, genArgs, parameters [i]);
5732 return buf.Append (')');
5735 private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
5737 AddTypeCount = false;
5738 buf.Append (GetTypeName (parameter.ParameterType));
5739 AddTypeCount = true;
5740 return buf;
5743 protected override string GetPropertyName (PropertyReference property)
5745 string name = null;
5747 PropertyDefinition propertyDef = property as PropertyDefinition;
5748 MethodDefinition method = null;
5749 if (propertyDef != null)
5750 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
5751 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
5752 name = property.Name;
5753 else {
5754 TypeReference iface;
5755 MethodReference ifaceMethod;
5756 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
5757 AddTypeCount = false;
5758 name = string.Join ("#", new string[]{
5759 GetTypeName (iface).Replace (".", "#"),
5760 DocUtils.GetMember (property.Name)
5762 AddTypeCount = true;
5765 StringBuilder buf = new StringBuilder ();
5766 buf.Append (GetName (property.DeclaringType));
5767 buf.Append ('.');
5768 buf.Append (name);
5769 IList<ParameterDefinition> parameters = property.Parameters;
5770 if (parameters.Count > 0) {
5771 genDeclType = property.DeclaringType;
5772 buf.Append ('(');
5773 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5774 AppendParameter (buf, genArgs, parameters [0]);
5775 for (int i = 1; i < parameters.Count; ++i) {
5776 buf.Append (',');
5777 AppendParameter (buf, genArgs, parameters [i]);
5779 buf.Append (')');
5780 genDeclType = null;
5782 return buf.ToString ();
5785 protected override string GetFieldName (FieldReference field)
5787 return string.Format ("{0}.{1}",
5788 GetName (field.DeclaringType), field.Name);
5791 protected override string GetEventName (EventReference e)
5793 return string.Format ("{0}.{1}",
5794 GetName (e.DeclaringType), e.Name);
5797 protected override string GetTypeDeclaration (TypeDefinition type)
5799 string name = GetName (type);
5800 if (type == null)
5801 return null;
5802 return "T:" + name;
5805 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5807 string name = GetName (constructor);
5808 if (name == null)
5809 return null;
5810 return "M:" + name;
5813 protected override string GetMethodDeclaration (MethodDefinition method)
5815 string name = GetName (method);
5816 if (name == null)
5817 return null;
5818 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5819 genDeclType = method.DeclaringType;
5820 genDeclMethod = method;
5821 name += "~" + GetName (method.ReturnType);
5822 genDeclType = null;
5823 genDeclMethod = null;
5825 return "M:" + name;
5828 protected override string GetPropertyDeclaration (PropertyDefinition property)
5830 string name = GetName (property);
5831 if (name == null)
5832 return null;
5833 return "P:" + name;
5836 protected override string GetFieldDeclaration (FieldDefinition field)
5838 string name = GetName (field);
5839 if (name == null)
5840 return null;
5841 return "F:" + name;
5844 protected override string GetEventDeclaration (EventDefinition e)
5846 string name = GetName (e);
5847 if (name == null)
5848 return null;
5849 return "E:" + name;
5853 class FileNameMemberFormatter : SlashDocMemberFormatter {
5854 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5856 return buf;
5859 protected override char NestedTypeSeparator {
5860 get {return '+';}
5864 class ResolvedTypeInfo {
5865 TypeDefinition typeDef;
5867 public ResolvedTypeInfo (TypeReference value) {
5868 Reference = value;
5871 public TypeReference Reference { get; private set; }
5873 public TypeDefinition Definition {
5874 get {
5875 if (typeDef == null) {
5876 typeDef = Reference.Resolve ();
5878 return typeDef;
5883 /// <summary>Formats attribute values. Should return true if it is able to format the value.</summary>
5884 class AttributeValueFormatter {
5885 public virtual bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5887 TypeReference valueType = type.Reference;
5888 if (v == null) {
5889 returnvalue = "null";
5890 return true;
5892 if (valueType.FullName == "System.Type") {
5893 var vTypeRef = v as TypeReference;
5894 if (vTypeRef != null)
5895 returnvalue = "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
5896 else
5897 returnvalue = "typeof(" + v.ToString () + ")";
5899 return true;
5901 if (valueType.FullName == "System.String") {
5902 returnvalue = "\"" + v.ToString () + "\"";
5903 return true;
5905 if (valueType.FullName == "System.Char") {
5906 returnvalue = "'" + v.ToString () + "'";
5907 return true;
5909 if (v is Boolean) {
5910 returnvalue = (bool)v ? "true" : "false";
5911 return true;
5914 TypeDefinition valueDef = type.Definition;
5915 if (valueDef == null || !valueDef.IsEnum) {
5916 returnvalue = v.ToString ();
5917 return true;
5920 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5921 var values = MDocUpdater.GetEnumerationValues (valueDef);
5922 long c = MDocUpdater.ToInt64 (v);
5923 if (values.ContainsKey (c)) {
5924 returnvalue = typename + "." + values [c];
5925 return true;
5928 returnvalue = null;
5929 return false;
5933 /// <summary>The final value formatter in the pipeline ... if no other formatter formats the value,
5934 /// then this one will serve as the default implementation.</summary>
5935 class DefaultAttributeValueFormatter : AttributeValueFormatter {
5936 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5938 returnvalue = "(" + MDocUpdater.GetDocTypeFullName (type.Reference) + ") " + v.ToString ();
5939 return true;
5943 /// <summary>Flags enum formatter that assumes powers of two values.</summary>
5944 /// <remarks>As described here: https://msdn.microsoft.com/en-us/library/vstudio/ms229062(v=vs.100).aspx</remarks>
5945 class StandardFlagsEnumFormatter : AttributeValueFormatter {
5946 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5948 TypeReference valueType = type.Reference;
5949 TypeDefinition valueDef = type.Definition;
5950 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5952 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5953 var values = MDocUpdater.GetEnumerationValues (valueDef);
5954 long c = MDocUpdater.ToInt64 (v);
5955 returnvalue = string.Join (" | ",
5956 (from i in values.Keys
5957 where (c & i) == i && i != 0
5958 select typename + "." + values [i])
5959 .DefaultIfEmpty (c.ToString ()).ToArray ());
5961 return true;
5964 returnvalue = null;
5965 return false;
5969 /// <summary>A custom formatter for the ObjCRuntime.Platform enumeration.</summary>
5970 class ApplePlatformEnumFormatter : AttributeValueFormatter {
5971 public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
5973 TypeReference valueType = type.Reference;
5974 string typename = MDocUpdater.GetDocTypeFullName (valueType);
5975 TypeDefinition valueDef = type.Definition;
5976 if (typename.Contains ("ObjCRuntime.Platform") && valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
5978 var values = MDocUpdater.GetEnumerationValues (valueDef);
5979 long c = MDocUpdater.ToInt64 (v);
5981 returnvalue = Format (c, values, typename);
5982 return true;
5985 returnvalue = null;
5986 return false;
5989 string Format (long c, IDictionary<long, string> values, string typename)
5991 int iosarch, iosmajor, iosminor, iossubminor;
5992 int macarch, macmajor, macminor, macsubminor;
5993 GetEncodingiOS (c, out iosarch, out iosmajor, out iosminor, out iossubminor);
5994 GetEncodingMac ((ulong)c, out macarch, out macmajor, out macminor, out macsubminor);
5996 if (iosmajor == 0 & iosminor == 0 && iossubminor == 0) {
5997 return FormatValues ("Mac", macarch, macmajor, macminor, macsubminor);
6000 if (macmajor == 0 & macminor == 0 && macsubminor == 0) {
6001 return FormatValues ("iOS", iosarch, iosmajor, iosminor, iossubminor);
6004 return string.Format ("(Platform){0}", c);
6007 string FormatValues (string plat, int arch, int major, int minor, int subminor)
6009 string archstring = "";
6010 switch (arch) {
6011 case 1:
6012 archstring = "32";
6013 break;
6014 case 2:
6015 archstring = "64";
6016 break;
6018 return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
6019 major,
6020 minor,
6021 subminor == 0 ? "" : "_" + subminor.ToString (),
6022 archstring,
6023 plat
6027 void GetEncodingiOS (long entireLong, out int archindex, out int major, out int minor, out int subminor)
6029 long lowerBits = entireLong & 0xffffffff;
6030 int lowerBitsAsInt = (int) lowerBits;
6031 GetEncoding (lowerBitsAsInt, out archindex, out major, out minor, out subminor);
6034 void GetEncodingMac (ulong entireLong, out int archindex, out int major, out int minor, out int subminor)
6036 ulong higherBits = entireLong & 0xffffffff00000000;
6037 int higherBitsAsInt = (int) ((higherBits) >> 32);
6038 GetEncoding (higherBitsAsInt, out archindex, out major, out minor, out subminor);
6041 void GetEncoding (Int32 encodedBits, out int archindex, out int major, out int minor, out int subminor)
6043 // format is AAJJNNSS
6044 archindex = (int)((encodedBits & 0xFF000000) >> 24);
6045 major = (int)((encodedBits & 0x00FF0000) >> 16);
6046 minor = (int)((encodedBits & 0x0000FF00) >> 8);
6047 subminor = (int)((encodedBits & 0x000000FF) >> 0);