1 // Updater program for syncing Mono's ECMA-style documentation files
3 // By Joshua Tauberer <tauberer@for.net>
6 using System
.Collections
;
7 using System
.Collections
.Generic
;
8 using System
.Collections
.ObjectModel
;
9 using System
.Diagnostics
;
10 using System
.Globalization
;
15 using System
.Xml
.XPath
;
20 using MyXmlNodeList
= System
.Collections
.Generic
.List
<System
.Xml
.XmlNode
>;
21 using StringList
= System
.Collections
.Generic
.List
<string>;
22 using StringToStringMap
= System
.Collections
.Generic
.Dictionary
<string, string>;
23 using StringToXmlNodeMap
= System
.Collections
.Generic
.Dictionary
<string, System
.Xml
.XmlNode
>;
25 namespace Mono
.Documentation
{
26 static class NativeTypeManager
{
28 static Dictionary
<string, string> toNativeType
= new Dictionary
<string,string>(){
32 {"System.Int32", "System.nint"}
,
35 {"System.UInt32", "System.nuint"}
,
38 {"System.Single", "System.nfloat"}
,
39 {"SizeF", "CoreGraphics.CGSize"}
,
40 {"System.Drawing.SizeF", "CoreGraphics.CGSize"}
,
41 {"PointF", "CoreGraphics.CGPoint"}
,
42 {"System.Drawing.PointF", "CoreGraphics.CGPoint"}
,
43 {"RectangleF", "CoreGraphics.CGRect" }
,
44 {"System.Drawing.RectangleF", "CoreGraphics.CGRect"}
47 static Dictionary
<string, string> fromNativeType
= new Dictionary
<string,string>(){
50 {"System.nint", "System.Int32"}
,
52 {"System.nuint", "System.UInt32"}
,
54 {"System.nfloat", "System.Single"}
,
55 {"CoreGraphics.CGSize", "System.Drawing.SizeF"}
,
56 {"CoreGraphics.CGPoint", "System.Drawing.PointF"}
,
57 {"CoreGraphics.CGRect", "System.Drawing.RectangleF"}
,
58 {"MonoTouch.CoreGraphics.CGSize", "System.Drawing.SizeF"}
,
59 {"MonoTouch.CoreGraphics.CGPoint", "System.Drawing.PointF"}
,
60 {"MonoTouch.CoreGraphics.CGRect", "System.Drawing.RectangleF"}
63 public static string ConvertToNativeType(string typename
) {
68 string valueToCompare
= StripToComparableType (typename
, ref isOut
, ref isArray
);
70 if (toNativeType
.TryGetValue (valueToCompare
, out nvalue
)) {
82 public static string ConvertFromNativeType(string typename
) {
87 string valueToCompare
= StripToComparableType (typename
, ref isOut
, ref isArray
);
89 if (fromNativeType
.TryGetValue (valueToCompare
, out nvalue
)) {
98 // it wasn't one of the native types ... just return it
102 static string StripToComparableType (string typename
, ref bool isOut
, ref bool isArray
)
104 string valueToCompare
= typename
;
105 if (typename
.EndsWith ("[]")) {
106 valueToCompare
= typename
.Substring (0, typename
.Length
- 2);
109 if (typename
.EndsWith ("&")) {
110 valueToCompare
= typename
.Substring (0, typename
.Length
- 1);
113 if (typename
.Contains ("<")) {
114 // TODO: Need to recursively process generic parameters
116 return valueToCompare
;
119 public static string GetTranslatedName(TypeReference t
) {
120 string typename
= t
.FullName
;
122 bool isInAssembly
= MDocUpdater
.IsInAssemblies (t
.Module
.Name
);
123 if (isInAssembly
&& !typename
.StartsWith ("System") && MDocUpdater
.HasDroppedNamespace (t
)) {
124 string nameWithDropped
= string.Format ("{0}.{1}", MDocUpdater
.droppedNamespace
, typename
);
125 return nameWithDropped
;
130 class MDocUpdater
: MDocCommand
133 List
<AssemblyDefinition
> assemblies
;
134 readonly DefaultAssemblyResolver assemblyResolver
= new DefaultAssemblyResolver();
136 string apistyle
= string.Empty
;
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
;
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
{
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
)
215 show_exceptions
= DebugOutput
;
216 var types
= new List
<string> ();
217 var p
= new OptionSet () {
219 "Delete removed members from the XML files.",
220 v
=> delete
= v
!= null },
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 " +
231 v
=> exceptions
= ParseExceptionLocations (v
) },
233 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
236 case "ignore-missing-types":
237 ignore_missing_types
= true;
239 case "no-assembly-versions":
240 no_assembly_versions
= true;
243 throw new Exception ("Unsupported flag `" + v
+ "'.");
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 },
253 "Import documentation from {FILE}.",
254 v
=> AddImporter (v
) },
256 "Check for assembly references in {DIRECTORY}.",
257 v
=> assemblyResolver
.AddSearchDirectory (v
) },
259 "Ignored for compatibility with update-ecma-xml.",
262 "Root {DIRECTORY} to generate/update documentation.",
265 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
266 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
267 v
=> assemblyResolver
.AddSearchDirectory (Path
.GetDirectoryName (v
)) },
269 "Manually specify the assembly {VERSION} that new members were added in.",
272 "Only update documentation for {TYPE}.",
273 v
=> types
.Add (v
) },
275 "When processing assembly {ASSEMBLY}, strip off leading namespace {PREFIX}:\n" +
276 " e.g. --dropns ASSEMBLY=PREFIX",
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
;
286 "If the new assembly is switching to 'magic types', then this switch should be defined.",
287 v
=> SwitchingToMagicTypes
= true },
289 "Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
290 v
=> PreserveTag
= "true" },
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)
300 if (assemblies
.Count
== 0)
301 Error ("No assemblies specified.");
303 // validation for the api-style parameter
304 if (apistyle
== "classic")
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
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) {
334 DoUpdateTypes (srcPath
, types
, srcPath
);
337 else if (opts
.@namespace != null)
338 DoUpdateNS (opts
.@namespace, Path
.Combine (opts
.path
, opts
.@namespace),
339 Path
.Combine (dest_dir
, opts
.@namespace));
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
)
353 XmlReader r
= new XmlTextReader (path
);
355 while (r
.NodeType
!= XmlNodeType
.Element
) {
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
));
368 Error ("Unsupported XML format within {0}.", path
);
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
;
389 foreach (var type
in s
.Split (',')) {
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
);
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;
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.");
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
);
429 private static void WriteFile (string filename
, FileMode mode
, Action
<TextWriter
> action
)
431 Action
<string> creator
= file
=> {
432 using (var writer
= OpenWrite (file
, mode
))
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
]) {
462 for (int i
= attrs
.Length
-1; i
>= 0; --i
) {
463 XmlAttribute n
= attrs
[i
];
466 XmlAttribute r
= null;
467 for (int j
= i
+1; j
< attrs
.Length
; ++j
) {
468 if (attrs
[j
] != null) {
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
);
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
);
543 found
.Add (type
.FullName
);
548 index
.Add (assembly
);
556 if (ignore_missing_types
)
559 var notFound
= from n
in typenames where
!found
.Contains (n
) select n
;
561 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound
.ToArray ()));
564 class IndexForTypes
{
570 XmlElement index_types
;
571 XmlElement index_assemblies
;
573 public IndexForTypes (MDocUpdater app
, string indexFile
, XmlDocument index
)
576 this.indexFile
= indexFile
;
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)
588 app
.AddIndexAssembly (assembly
, index_assemblies
);
591 public void Add (TypeDefinition type
)
593 app
.AddIndexType (type
, index_types
);
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
))
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
);
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.",
630 if (!IsPublic (type
))
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[] {
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
);
661 reltypefile
= result
.Item1
;
662 typefile
= result
.Item2
;
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
;
680 string output
= null;
683 } else if (dest
== "-") {
686 output
= Path
.Combine (dest
, reltypefile
);
689 if (file
!= null && file
.Exists
) {
691 XmlDocument basefile
= new XmlDocument();
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);
701 XmlElement td
= StubType(type
, output
);
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
);
717 basefile
.Load(typefile
);
718 } catch (Exception e
) {
719 throw new InvalidOperationException("Error loading " + typefile
+ ": " + e
.Message
, e
);
723 GetTypeFileName (basefile
.SelectSingleNode("Type/@FullName").InnerText
);
724 TypeDefinition type
= assembly
.GetType(typename
);
727 if (!string.IsNullOrWhiteSpace (droppedNamespace
)) {
728 string nameWithNs
= string.Format ("{0}.{1}", droppedNamespace
, typename
);
729 type
= assembly
.GetType (nameWithNs
);
731 Warning ("Type no longer in assembly: " + typename
);
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
))
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
);
763 for (int i
= 0; i
< typename
.Length
; ++i
) {
764 char c
= typename
[i
];
773 filename
.Append ('`').Append ((numArgs
+1).ToString());
788 return filename
.ToString ();
791 private void AddIndexAssembly (AssemblyDefinition assembly
, XmlElement parent
)
793 XmlElement index_assembly
= null;
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);
808 foreach (byte b
in name
.PublicKey
)
809 key
.AppendFormat ("{0,2:x2} ", b
);
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
);
847 typenode
.RemoveAttribute("DisplayName");
849 typenode
.SetAttribute ("Kind", GetTypeKind (type
));
852 private void DoUpdateAssemblies (string source
, string dest
)
854 string indexfile
= dest
+ "/index.xml";
856 if (System
.IO
.File
.Exists(indexfile
)) {
857 index
= new XmlDocument();
858 index
.Load(indexfile
);
861 ClearElement(index
.DocumentElement
, "Assembly");
862 ClearElement(index
.DocumentElement
, "Attributes");
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
))
904 string reltypepath
= DoUpdateType (type
, source
, dest
);
905 if (reltypepath
== null)
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
]);
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
{
965 public AttributeNameComparer ()
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");
995 return v
.Substring (0, n
-1);
999 private static string GetTypeKind (TypeDefinition type
)
1002 return "Enumeration";
1003 if (type
.IsValueType
)
1005 if (type
.IsInterface
)
1007 if (DocUtils
.IsDelegate (type
))
1009 if (type
.IsClass
|| type
.FullName
== "System.Enum") // FIXME
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
)) {
1022 decl
= (TypeDefinition
) decl
.DeclaringType
;
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
);
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)) {
1052 goodfiles
.Add (relTypeFile
);
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
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
);
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
);
1089 // we should be safe to delete here because it was not marked as a unified assembly
1094 if (classicAssemblyNode
!= null || classicMembers
.Count
> 0) {
1095 Warning ("*** this type is marked as classic, not deleting {0}", typefile
.FullName
);
1098 // safe to delete because it wasn't marked as a classic assembly, so the type is gone in both.
1108 private static TextWriter
OpenWrite (string path
, FileMode mode
)
1110 var w
= new StreamWriter (
1111 new FileStream (path
, mode
),
1112 new UTF8Encoding (false)
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) {
1145 index_types
.SelectSingleNode ("/Overview").RemoveChild (e
);
1149 e
= index_types
.OwnerDocument
.CreateElement ("ExtensionMethods");
1150 index_types
.SelectSingleNode ("/Overview").AppendChild (e
);
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
);
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!");
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)
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))
1219 DeleteMember ("Member Removed", output
, oldmember
, todelete
, type
);
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
);
1231 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig
);
1235 // Update signature information
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()
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;
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
);
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");
1306 string src
= GetCodeSource (lang
, Path
.Combine (srcPath
, file
));
1308 code
.InnerText
= src
;
1312 if (insertSince
&& since
!= null) {
1313 XmlNode docs
= basefile
.DocumentElement
.SelectSingleNode("Docs");
1314 docs
.AppendChild (CreateSinceNode (basefile
));
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
);
1327 WriteXml(basefile
.DocumentElement
, Console
.Out
);
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
)
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);
1347 using (StreamReader reader
= new StreamReader (file
)) {
1349 StringBuilder src
= new StringBuilder ();
1351 while ((line
= reader
.ReadLine ()) != null) {
1352 if (line
.Trim() == region
) {
1353 indent
= line
.IndexOf (region
);
1356 if (indent
>= 0 && line
.Trim().StartsWith ("#endregion")) {
1361 (line
.Length
> 0 ? line
.Substring (indent
) : string.Empty
) +
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
);
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
);
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
;
1390 member
.OwnerDocument
.DocumentElement
.GetAttribute ("FullName"),
1391 member
.Attributes
["MemberName"].Value
,
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
);
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.
1426 if (!isClassicRun
|| (isClassicRun
&& !nodeIsClassic
&& !nodeIsUnified
)) { // regular codepath (ie. not classic/unified)
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
)
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)
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)
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
);
1469 // same type -- must be an overloaded method. Sort based on type
1470 // parameter count, then parameter count, then by the parameter
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
);
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
);
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
);
1507 static readonly MemberComparer DefaultMemberComparer
= new MemberComparer ();
1509 private static void SortTypeMembers (XmlNode members
)
1511 if (members
== null)
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"))
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);
1544 private XmlElement
CreateSinceNode (XmlDocument doc
)
1546 XmlElement s
= doc
.CreateElement ("since");
1547 s
.SetAttribute ("version", since
);
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
);
1563 root
.SelectNodes (element
).Cast
<XmlElement
> ().ToArray (),
1564 x
=> x
.GetAttribute ("Value") == valueToUse
,
1565 x
=> x
.SetAttribute ("Value", valueToUse
),
1567 var node
= WriteElementAttribute (root
, element
, "Language", f
.Language
, forceNewElement
: true);
1568 var newnode
= WriteElementAttribute (root
, node
, "Value", valueToUse
);
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);
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
);
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
));
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);
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
))
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
);
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
),
1707 XmlElement ass
= WriteElement (root
, "AssemblyInfo", forceNewElement
: true);
1709 if (MDocUpdater
.HasDroppedNamespace (module
))
1710 ass
.AddApiStyle (ApiStyle
.Unified
);
1712 ass
.AddApiStyle (ApiStyle
.Classic
);
1717 static readonly string[] TypeNodeOrder
= {
1721 "ThreadingSafetyStatement",
1722 "ThreadSafetyStatement",
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
);
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
);
1757 me
.SelectNodes (element
).Cast
<XmlElement
> ().ToArray(),
1758 x
=> x
.GetAttribute("Value") == valueToUse
,
1759 x
=> x
.SetAttribute ("Value", valueToUse
),
1761 var node
= WriteElementAttribute (me
, element
, "Language", f
.Language
, forceNewElement
:true);
1762 var newNode
= WriteElementAttribute (me
, node
, "Value", valueToUse
);
1769 WriteElementText(me
, "MemberType", GetMemberType(mi
));
1771 if (!no_assembly_versions
) {
1772 if (!IsMultiAssembly
)
1773 UpdateAssemblyVersions (me
, mi
, true);
1775 var node
= AddAssemblyNameToNode (me
, mi
.Module
);
1777 UpdateAssemblyVersionForAssemblyInfo (node
, me
, new[] { GetAssemblyVersion (mi.Module.Assembly) }
, add: true);
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
));
1795 if (mi
is FieldDefinition
&& GetFieldConstValue ((FieldDefinition
)mi
, out fieldValue
))
1796 WriteElementText(me
, "MemberValue", fieldValue
);
1798 info
.Node
= WriteElement (me
, "Docs");
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
);
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
;
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
)) {
1851 n
.AddApiStyle (ApiStyle
.Classic
);
1852 addedOldApiStyle
= true;
1857 if (!existing
.Any ()) {
1858 var newNode
= makeNewNode ();
1859 if (shouldDuplicate
&& addedOldApiStyle
) {
1860 newNode
.AddApiStyle (ApiStyle
.Unified
);
1864 var itemToReuse
= existing
.First ();
1865 setValue (itemToReuse
);
1867 if (shouldDuplicate
&& addedOldApiStyle
) {
1868 itemToReuse
.AddApiStyle (styleToUse
);
1874 static readonly string[] MemberNodeOrder
= {
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
;
1921 attrs
= attrs
.Concat (GetCustomAttributes (p
.CustomAttributes
, ""));
1923 PropertyDefinition pd
= mi
as PropertyDefinition
;
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
;
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: "));
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
))
1949 if (slashdocFormatter
.GetName (attribute
.AttributeType
) == null)
1952 if (Array
.IndexOf (IgnorableAttributes
, attribute
.AttributeType
.FullName
) >= 0)
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 (
1964 (from namedArg
in attribute
.Fields
1965 select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }
)
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
= {
1992 static readonly string[] ValidExtensionDocMembers
= {
1998 private void UpdateExtensionMethods (XmlElement e
, DocsNodeInfo info
)
2000 MethodDefinition me
= info
.Member
as MethodDefinition
;
2003 if (info
.Parameters
.Count
< 1)
2005 if (!DocUtils
.IsExtensionMethod (me
))
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
)
2030 MyXmlNodeList
remove = null;
2031 foreach (XmlNode n
in node
.ChildNodes
) {
2032 if (Array
.BinarySearch (except
, n
.Name
) < 0) {
2034 remove = new MyXmlNodeList ();
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
));
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");
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)
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
)
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
);
2079 value = "\"" + value + "\"";
2081 if (value != null && value != "")
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
) {
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
);
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;
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;
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);
2131 internal static XmlNode
CopyNode (XmlNode source
, XmlNode dest
)
2133 XmlNode copy
= dest
.OwnerDocument
.ImportNode (source
, true);
2134 dest
.AppendChild (copy
);
2138 private static void WriteElementInitialText(XmlElement parent
, string element
, string value) {
2139 XmlElement node
= (XmlElement
)parent
.SelectSingleNode(element
);
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);
2155 internal static void ClearElement(XmlElement parent
, string name
) {
2156 XmlElement node
= (XmlElement
)parent
.SelectSingleNode(name
);
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
);
2201 WriteElementInitialText(e
, retnodename
, "To be added.");
2204 ClearElement(e
, "returns");
2205 ClearElement(e
, "value");
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
];
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
;
2268 // Remove parameters that no longer exist and check all params are in the right order.
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
) {
2278 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2279 e
.OwnerDocument
.DocumentElement
.GetAttribute ("FullName"),
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
,
2288 Warning ("\tValue={0}", paramnode
.OuterXml
);
2290 todelete
.Add (paramnode
);
2295 if ((int)seenParams
[name
] != 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.
2307 for (int pi
= values
.Length
-1; pi
>= 0; pi
--)
2308 e
.PrependChild(paramnodes
[pi
]);
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
);
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
)
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
);
2349 return xType
.CompareTo (yType
);
2352 static string GetNamespace (string type
)
2354 int n
= type
.LastIndexOf ('.');
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
+ "']");
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
)) +
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
)
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
;
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);
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
)
2417 XmlElement av
= (XmlElement
) root
.SelectSingleNode ("AssemblyVersions");
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
);
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
)
2457 else if (matches
.Count
== 0 && add) {
2458 foreach (string sv
in assemblyVersions
) {
2459 XmlElement c
= root
.OwnerDocument
.CreateElement("AssemblyVersion");
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");
2506 XmlElement e
= (XmlElement
)root
.SelectSingleNode("Attributes");
2510 e
= root
.OwnerDocument
.CreateElement("Attributes");
2512 foreach (string attribute
in attributes
) {
2513 XmlElement ae
= root
.OwnerDocument
.CreateElement("Attribute");
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> ();
2551 (from f
in type
.Fields
2552 where
!(f
.IsRuntimeSpecialName
|| f
.IsSpecialName
)
2554 values
[ToInt64 (f
.Constant
)] = f
.Name
;
2559 internal static long ToInt64 (object value)
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");
2571 foreach (ParameterDefinition p
in parameters
) {
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
)
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.
2598 x
=> x
.GetAttribute ("Type") == ptype
,
2599 x
=> x
.SetAttribute ("Type", ptype
),
2601 pe
= root
.OwnerDocument
.CreateElement ("Parameter");
2604 pe
.SetAttribute ("Name", p
.Name
);
2605 pe
.SetAttribute ("Type", ptype
);
2606 if (p
.ParameterType
is ByReferenceType
) {
2608 pe
.SetAttribute ("RefType", "out");
2610 pe
.SetAttribute ("RefType", "ref");
2613 MakeAttributes (pe
, GetCustomAttributes (p
.CustomAttributes
, ""));
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");
2627 root
.RemoveChild (f
);
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
;
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
2650 XmlElement pe
= root
.OwnerDocument
.CreateElement("TypeParameter");
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) {
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
));
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
);
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
,
2728 var newNode
= WriteElementText(e
, "ReturnType", valueToUse
, forceNewElement
: true);
2729 if (attributes
!= null)
2730 MakeAttributes(e
, GetCustomAttributes (attributes
, ""), type
);
2737 private void MakeReturnValue (XmlElement root
, MemberReference mi
, bool shouldDuplicateWithNew
=false)
2739 if (mi
is MethodDefinition
&& ((MethodDefinition
) mi
).IsConstructor
)
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
);
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
));
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
));
2785 internal static string GetMemberName (MemberReference mi
)
2787 MethodDefinition mb
= mi
as MethodDefinition
;
2789 PropertyDefinition pi
= mi
as PropertyDefinition
;
2792 return DocUtils
.GetPropertyName (pi
);
2794 StringBuilder sb
= new StringBuilder (mi
.Name
.Length
);
2795 if (!DocUtils
.IsExplicitlyImplemented (mb
))
2796 sb
.Append (mi
.Name
);
2798 TypeReference iface
;
2799 MethodReference ifaceMethod
;
2800 DocUtils
.GetInfoForExplicitlyImplementedMethod (mb
, out iface
, out ifaceMethod
);
2801 sb
.Append (GetDocTypeFullName (iface
));
2803 sb
.Append (ifaceMethod
.Name
);
2805 if (mb
.IsGenericMethod ()) {
2806 IList
<GenericParameter
> typeParams
= mb
.GenericParameters
;
2807 if (typeParams
.Count
> 0) {
2809 sb
.Append (typeParams
[0].Name
);
2810 for (int i
= 1; i
< typeParams
.Count
; ++i
)
2811 sb
.Append (",").Append (typeParams
[i
].Name
);
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
)
2830 if (mi
is PropertyDefinition
)
2832 if (mi
is FieldDefinition
)
2834 if (mi
is EventDefinition
)
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
)
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
)
2874 xpath
.Append ("Members/Member[@MemberName=\"")
2875 .Append (member
.SelectSingleNode ("@MemberName").Value
)
2877 XPathNodeIterator parameters
= member
.Select ("Parameters/Parameter");
2878 if (parameters
.Count
> 0) {
2879 xpath
.Append ("/Parameters[count(Parameter) = ")
2880 .Append (parameters
.Count
);
2882 while (parameters
.MoveNext ()) {
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
)
2899 xpath
.Append ("Members/Member[@MemberName=\"")
2900 .Append (GetMemberName (member
))
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
)
2969 .Where (m
=> m
.Name
== method
)
2970 .EnsureZeroOrOne ();
2973 public static IEnumerable
<MemberReference
> GetDefaultMembers (this TypeReference type
)
2975 TypeDefinition def
= type
as TypeDefinition
;
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
)
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
)
3026 if (!self
.HasNestedTypes
)
3029 foreach (var type
in self
.NestedTypes
.SelectMany (t
=> t
.GetAllTypes ()))
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))
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
;
3126 return name
.Substring (startType
+1);
3129 public static string GetMember (string name
)
3131 int i
= name
.LastIndexOf ('.');
3134 return name
.Substring (i
+1);
3137 public static void GetInfoForExplicitlyImplementedMethod (
3138 MethodDefinition method
, out TypeReference iface
, out MethodReference ifaceMethod
)
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
;
3155 method
= pi
.SetMethod
;
3156 if (!IsExplicitlyImplemented (method
))
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
;
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
);
3188 public static string PathCombine (string dir
, string path
)
3194 return Path
.Combine (dir
, path
);
3197 public static bool IsExtensionMethod (MethodDefinition method
)
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)
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
> ();
3219 while (type
.DeclaringType
!= null) {
3220 decls
.Add (type
.DeclaringType
);
3221 type
= type
.DeclaringType
;
3227 public static int GetGenericArgumentCount (TypeReference type
)
3229 GenericInstanceType inst
= type
as GenericInstanceType
;
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;
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) {
3269 baseRef
= baseDef
.BaseType
;
3274 foreach (var r
in type
.Interfaces
)
3275 a (r
.InterfaceType
.Resolve ());
3276 return inheritedInterfaces
;
3280 class DocsNodeInfo
{
3281 public DocsNodeInfo (XmlElement node
)
3286 public DocsNodeInfo (XmlElement node
, TypeDefinition type
)
3292 public DocsNodeInfo (XmlElement node
, MemberReference member
)
3295 SetMemberInfo (member
);
3298 void SetType (TypeDefinition type
)
3301 throw new ArgumentNullException ("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
)
3323 throw new ArgumentNullException ("member");
3324 ReturnIsReturn
= true;
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
)
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)
3378 if (seen
!= null && seen
.Contains (type
.FullName
))
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__");
3393 MemberReference m
= GetMember (type
, new DocumentationMember (oldmember
));
3395 yield return new DocsNodeInfo (oldmember
);
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
))
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
)
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
)
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
)) {
3467 if (originalRType
!= rtype
)
3468 matchedMagicType
= 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
) {
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
;
3518 return likelyCandidate
;
3521 static string[] GetTypeParameters (string docName
, IEnumerable
<string> knownParameters
)
3523 if (docName
[docName
.Length
-1] != '>')
3525 StringList types
= new StringList ();
3526 int endToken
= docName
.Length
-2;
3527 int i
= docName
.Length
-2;
3529 if (docName
[i
] == ',' || docName
[i
] == '<') {
3530 types
.Add (docName
.Substring (i
+ 1, endToken
- i
));
3533 if (docName
[i
] == '<')
3538 var arrayTypes
= types
.ToArray ();
3540 if (knownParameters
!= null && knownParameters
.Any () && arrayTypes
.Length
!= knownParameters
.Count ())
3541 return knownParameters
.ToArray ();
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
3558 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
3559 // - try as-is, and try type.member (due to "kludge" for property
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) {
3572 foreach (MemberReference mi
in type
.GetMembers (docName
))
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
)))
3585 int startLt
, startType
, startMethod
;
3586 startLt
= startType
= startMethod
= -1;
3587 for (int i
= 0; i
< docName
.Length
; ++i
) {
3588 switch (docName
[i
]) {
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.
3603 startType
= startMethod
;
3609 string refName
= startLt
== -1 ? docName
: docName
.Substring (0, startLt
);
3611 foreach (MemberReference mi
in type
.GetMembers (refName
))
3615 foreach (MemberReference mi
in type
.GetMembers (refName
.Substring (startType
+ 1)))
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... :-(
3629 foreach (MemberReference mi
in type
.GetMembers ()) {
3630 if (MDocUpdater
.GetMemberName (mi
) == docName
)
3635 static string GetReplacedString (string typeName
, string[] from, string[] to
)
3639 for (int i
= 0; i
< from.Length
; ++i
)
3640 typeName
= typeName
.Replace (from [i
], to
[i
]);
3644 private static int CountChars (string s
, char c
)
3647 for (int i
= 0; i
< s
.Length
; ++i
) {
3655 class EcmaDocumentationEnumerator
: DocumentationEnumerator
{
3660 public EcmaDocumentationEnumerator (MDocUpdater app
, XmlReader ecmaDocs
)
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
)
3676 while (ecmadocs
.Read ()) {
3677 switch (ecmadocs
.Name
) {
3679 if (typeDepth
== -1)
3680 typeDepth
= ecmadocs
.Depth
;
3681 if (ecmadocs
.NodeType
!= XmlNodeType
.Element
)
3683 if (typeDepth
!= ecmadocs
.Depth
) // nested <TypeDefinition/> element?
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)
3693 if ((t
= assembly
.GetType (typename
)) == null &&
3694 (t
= assembly
.GetType (typename2
)) == null)
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)
3707 if (!ecmadocs
.IsStartElement ("Docs"))
3708 throw new InvalidOperationException ("Found " + ecmadocs
.Name
+ "; expecting <Docs/>!");
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 ()) {
3729 if (ecmadocs
.IsEmptyElement
)
3732 int membersDepth
= ecmadocs
.Depth
;
3734 while (go
&& ecmadocs
.Read ()) {
3735 switch (ecmadocs
.Name
) {
3737 if (membersDepth
!= ecmadocs
.Depth
- 1 || ecmadocs
.NodeType
!= XmlNodeType
.Element
)
3739 DocumentationMember dm
= new DocumentationMember (ecmadocs
);
3741 string xp
= MDocUpdater
.GetXPathForMember (dm
);
3742 XmlElement oldmember
= (XmlElement
) basefile
.SelectSingleNode (xp
);
3744 if (oldmember
== null) {
3745 m
= GetMember (type
, dm
);
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);
3752 // oldmember lookup may have failed due to type parameter renames.
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
);
3772 m
= GetMember (type
, new DocumentationMember (oldmember
));
3774 app
.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
3775 type
.FullName
, dm
.MemberSignatures
["C#"]);
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/>!");
3787 if (membersDepth
== ecmadocs
.Depth
&& ecmadocs
.NodeType
== XmlNodeType
.EndElement
) {
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
);
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
) {
3838 XmlAttribute name
= child
.Attributes
["name"];
3841 XmlElement p2
= (XmlElement
) e
.SelectSingleNode (child
.Name
+ "[@name='" + name
.Value
+ "']");
3843 p2
.InnerXml
= child
.InnerXml
;
3846 // Occasionally XML documentation will use <returns/> on
3847 // properties, so let's try to normalize things.
3850 XmlElement v
= e
.OwnerDocument
.CreateElement (info
.ReturnIsReturn
? "returns" : "value");
3851 v
.InnerXml
= child
.InnerXml
;
3857 case "permission": {
3858 XmlAttribute cref
= child
.Attributes
["cref"] ?? child
.Attributes
["name"];
3861 XmlElement a
= (XmlElement
) e
.SelectSingleNode (child
.Name
+ "[@cref='" + cref
.Value
+ "']");
3863 a
= e
.OwnerDocument
.CreateElement (child
.Name
);
3864 a
.SetAttribute ("cref", cref
.Value
);
3867 a
.InnerXml
= child
.InnerXml
;
3871 XmlAttribute cref
= child
.Attributes
["cref"];
3874 XmlElement a
= (XmlElement
) e
.SelectSingleNode ("altmember[@cref='" + cref
.Value
+ "']");
3876 a
= e
.OwnerDocument
.CreateElement ("altmember");
3877 a
.SetAttribute ("cref", cref
.Value
);
3884 if (child
.NodeType
== XmlNodeType
.Element
&&
3885 e
.SelectNodes (child
.Name
).Cast
<XmlElement
>().Any (n
=> n
.OuterXml
== child
.OuterXml
))
3888 MDocUpdater
.CopyNode (child
, e
);
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
+ "']");
3904 class EcmaDocumentationImporter
: DocumentationImporter
{
3908 public EcmaDocumentationImporter (XmlReader ecmaDocs
)
3910 this.ecmadocs
= ecmaDocs
;
3913 public override void ImportDocumentation (DocsNodeInfo info
)
3915 if (!ecmadocs
.IsStartElement ("Docs")) {
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
)
3928 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3930 if (!ecmadocs
.IsStartElement ())
3932 switch (ecmadocs
.Name
) {
3935 string name
= ecmadocs
.GetAttribute ("name");
3938 XmlNode doc
= e
.SelectSingleNode (
3939 ecmadocs
.Name
+ "[@name='" + name
+ "']");
3940 string value = ecmadocs
.ReadInnerXml ();
3942 doc
.InnerXml
= value.Replace ("\r", "");
3949 string name
= ecmadocs
.Name
;
3950 string cref
= ecmadocs
.GetAttribute ("cref");
3953 XmlNode doc
= e
.SelectSingleNode (
3954 ecmadocs
.Name
+ "[@cref='" + cref
+ "']");
3955 string value = ecmadocs
.ReadInnerXml ().Replace ("\r", "");
3957 doc
.InnerXml
= value;
3959 XmlElement n
= e
.OwnerDocument
.CreateElement (name
);
3960 n
.SetAttribute ("cref", cref
);
3967 string name
= ecmadocs
.Name
;
3968 string xpath
= ecmadocs
.Name
;
3969 StringList attributes
= new StringList (ecmadocs
.AttributeCount
);
3970 if (ecmadocs
.MoveToFirstAttribute ()) {
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", "");
3982 doc
.InnerXml
= value;
3985 XmlElement n
= e
.OwnerDocument
.CreateElement (name
);
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));
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
;
4013 StringList p
= new StringList ();
4014 StringList tp
= new StringList ();
4016 if (reader
.NodeType
!= XmlNodeType
.Element
)
4019 bool shouldUse
= true;
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":
4028 MemberSignatures
[reader
.GetAttribute ("Language")] = reader
.GetAttribute ("Value");
4032 MemberType
= reader
.ReadElementString ();
4035 if (reader
.Depth
== depth
+ 2 && shouldUse
)
4036 ReturnType
= reader
.ReadElementString ();
4039 if (reader
.Depth
== depth
+ 2 && shouldUse
)
4040 p
.Add (reader
.GetAttribute ("Type"));
4042 case "TypeParameter":
4043 if (reader
.Depth
== depth
+ 2 && shouldUse
)
4044 tp
.Add (reader
.GetAttribute ("Name"));
4047 if (reader
.Depth
== depth
+ 1)
4051 } while (go
&& reader
.Read () && reader
.Depth
>= depth
);
4056 TypeParameters
= tp
;
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']");
4076 ReturnType
= rt
.InnerText
;
4077 XmlNodeList p
= node
.SelectNodes ("Parameters/Parameter[not(@apistyle) or @apistyle='classic']");
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']");
4085 TypeParameters
= new StringList (tp
.Count
);
4086 for (int i
= 0; i
< tp
.Count
; ++i
)
4087 TypeParameters
.Add (tp
[i
].Attributes
["Name"].Value
);
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
)
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
{
4128 WithinGenericTypeParameters
,
4131 public abstract class MemberFormatter
{
4133 public virtual string Language
{
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
;
4146 return GetTypeName (type
, context
);
4147 MethodReference method
= member
as MethodReference
;
4148 if (method
!= null && method
.Name
== ".ctor") // method.IsConstructor
4149 return GetConstructorName (method
);
4151 return GetMethodName (method
);
4152 PropertyReference prop
= member
as PropertyReference
;
4154 return GetPropertyName (prop
);
4155 FieldReference field
= member
as FieldReference
;
4157 return GetFieldName (field
);
4158 EventReference e
= member
as EventReference
;
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
)
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 ('.');
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 ("`");
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
;
4243 buf
.Append (new string (',', rank
-1));
4244 return buf
.Append (ArrayDelimeters
[1]);
4247 protected virtual string RefTypeModifier
{
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
{
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
{
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
);
4284 bool insertNested
= false;
4285 foreach (var decl
in decls
) {
4286 TypeReference declDef
= decl
.Resolve () ?? decl
;
4288 buf
.Append (NestedTypeSeparator
);
4290 insertNested
= true;
4291 AppendTypeName (buf
, declDef
, context
);
4292 int ac
= DocUtils
.GetGenericArgumentCount (declDef
);
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]);
4310 protected List
<TypeReference
> GetGenericArguments (TypeReference type
)
4312 var args
= new List
<TypeReference
> ();
4313 GenericInstanceType inst
= type
as GenericInstanceType
;
4315 args
.AddRange (inst
.GenericArguments
.Cast
<TypeReference
> ());
4317 args
.AddRange (type
.GenericParameters
.Cast
<TypeReference
> ());
4321 protected virtual StringBuilder
AppendGenericTypeConstraints (StringBuilder buf
, TypeReference type
)
4326 protected virtual string GetConstructorName (MethodReference constructor
)
4328 return constructor
.Name
;
4331 protected virtual string GetMethodName (MethodReference method
)
4336 protected virtual string GetPropertyName (PropertyReference property
)
4338 return property
.Name
;
4341 protected virtual string GetFieldName (FieldReference field
)
4346 protected virtual string GetEventName (EventReference e
)
4351 public string GetDeclaration (MemberReference mreference
)
4353 return GetDeclaration (mreference
.Resolve ());
4356 string GetDeclaration (IMemberDefinition member
)
4359 throw new ArgumentNullException ("member");
4360 TypeDefinition type
= member
as TypeDefinition
;
4362 return GetTypeDeclaration (type
);
4363 MethodDefinition method
= member
as MethodDefinition
;
4364 if (method
!= null && method
.IsConstructor
)
4365 return GetConstructorDeclaration (method
);
4367 return GetMethodDeclaration (method
);
4368 PropertyDefinition prop
= member
as PropertyDefinition
;
4370 return GetPropertyDeclaration (prop
);
4371 FieldDefinition field
= member
as FieldDefinition
;
4373 return GetFieldDeclaration (field
);
4374 EventDefinition e
= member
as EventDefinition
;
4376 return GetEventDeclaration (e
);
4377 throw new NotSupportedException ("Can't handle: " + member
.GetType().ToString());
4380 protected virtual string GetTypeDeclaration (TypeDefinition type
)
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"))
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
))
4412 AppendModifiers (buf
, method
);
4414 if (buf
.Length
!= 0)
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
)
4435 protected virtual StringBuilder
AppendVisibility (StringBuilder buf
, MethodDefinition method
)
4440 protected virtual StringBuilder
AppendModifiers (StringBuilder buf
, MethodDefinition method
)
4445 protected virtual StringBuilder
AppendGenericMethod (StringBuilder buf
, MethodDefinition method
)
4450 protected virtual StringBuilder
AppendParameters (StringBuilder buf
, MethodDefinition method
, IList
<ParameterDefinition
> parameters
)
4455 protected virtual StringBuilder
AppendGenericMethodConstraints (StringBuilder buf
, MethodDefinition method
)
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
{
4488 protected override StringBuilder
AppendNamespace (StringBuilder buf
, TypeReference type
)
4490 if (GetBuiltinType (type
.FullName
) != null)
4492 string ns
= DocUtils
.GetNamespace (type
);
4493 if (ns
!= null && ns
.Length
> 0) {
4494 if (type
.IsValueType
)
4495 buf
.Append ("valuetype ");
4497 buf
.Append ("class ");
4498 buf
.Append (ns
).Append ('.');
4503 protected static string GetBuiltinType (string 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";
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
);
4541 string s
= GetBuiltinType (type
.FullName
);
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
]));
4570 MemberFormatterState
= MemberFormatterState
.WithinGenericTypeParameters
;
4572 if ((attrs
& GenericParameterAttributes
.Covariant
) != 0)
4574 if ((attrs
& GenericParameterAttributes
.Contravariant
) != 0)
4579 protected override string GetTypeDeclaration (TypeDefinition type
)
4581 string visibility
= GetTypeVisibility (type
.Attributes
);
4582 if (visibility
== null)
4585 StringBuilder buf
= new StringBuilder ();
4587 buf
.Append (".class ");
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 ");
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");
4617 buf
.Append (full
.GetName (type
.BaseType
).Substring ("class ".Length
));
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
)) {
4624 buf
.Append (" implements ");
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
);
4641 foreach (var decl
in decls
) {
4642 TypeReference declDef
= decl
.Resolve () ?? decl
;
4644 buf
.Append (NestedTypeSeparator
);
4647 AppendTypeName (buf
, declDef
, context
);
4651 foreach (TypeReference arg
in GetGenericArguments (type
)) {
4655 _AppendTypeName (buf
, arg
, context
);
4661 static string GetTypeVisibility (TypeAttributes ta
)
4663 switch (ta
& TypeAttributes
.VisibilityMask
) {
4664 case TypeAttributes
.Public
:
4665 case TypeAttributes
.NestedPublic
:
4668 case TypeAttributes
.NestedFamily
:
4669 case TypeAttributes
.NestedFamORAssem
:
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
))
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
)
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");
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
));
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) {
4738 _AppendTypeName (buf
, args
[0], null);
4739 for (int i
= 1; i
< args
.Count
; ++i
)
4740 _AppendTypeName (buf
.Append (", "), args
[i
], null);
4743 MemberFormatterState
= state
;
4748 for (int i
= 0; i
< method
.Parameters
.Count
; ++i
) {
4752 _AppendTypeName (buf
, method
.Parameters
[i
].ParameterType
, new DynamicParserContext (method
.Parameters
[i
]));
4754 buf
.Append (method
.Parameters
[i
].Name
);
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
))
4775 .Append (ifaceMethod
.Name
);
4777 return base.AppendMethodName (buf
, method
);
4780 protected override string RefTypeModifier
{
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");
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) {
4819 buf
.Append (args
[0].Name
);
4820 for (int i
= 1; i
< args
.Count
; ++i
)
4821 buf
.Append (",").Append (args
[i
].Name
);
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
)
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
) {
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 ");
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))
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 ();
4891 foreach (ParameterDefinition p
in property
.Parameters
) {
4895 _AppendTypeName (buf
, p
.ParameterType
, new DynamicParserContext (p
));
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)
4913 buf
.Insert (0, ".field ");
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
)
4931 return buf
.Append ("public ");
4932 if (field
.IsFamilyAndAssembly
)
4933 return buf
.Append ("familyandassembly ");
4934 if (field
.IsFamilyOrAssembly
)
4935 return buf
.Append ("familyorassembly ");
4937 return buf
.Append ("family ");
4941 static StringBuilder
AppendFieldValue (StringBuilder buf
, FieldDefinition field
)
4943 // enums have a value__ field, which we ignore
4944 if (field
.DeclaringType
.IsGenericType ())
4946 if (field
.HasConstant
&& field
.IsLiteral
) {
4949 val
= field
.Constant
;
4954 buf
.Append (" = ").Append ("null");
4955 else if (val
is Enum
)
4957 .Append (GetBuiltinType (field
.DeclaringType
.GetUnderlyingType ().FullName
))
4959 .Append (val
.ToString ())
4961 else if (val
is IFormattable
) {
4962 string value = ((IFormattable
)val
).ToString(null, CultureInfo
.InvariantCulture
);
4965 buf
.Append ("\"" + value + "\"");
4967 buf
.Append (GetBuiltinType (field
.DeclaringType
.GetUnderlyingType ().FullName
))
4976 protected override string GetEventDeclaration (EventDefinition e
)
4978 StringBuilder buf
= new StringBuilder ();
4979 if (AppendVisibility (buf
, e
.AddMethod
).Length
== 0) {
4984 buf
.Append (".event ")
4985 .Append (GetName (e
.EventType
))
4989 return buf
.ToString ();
4993 class ILMemberFormatter
: ILFullMemberFormatter
{
4994 protected override StringBuilder
AppendNamespace (StringBuilder buf
, TypeReference type
)
5000 class ILNativeTypeMemberFormatter
: ILFullMemberFormatter
{
5001 protected static string _GetBuiltinType (string t
)
5003 //string moddedType = base.GetBuiltinType (t);
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";
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";
5030 class CSharpFullMemberFormatter
: MemberFormatter
{
5032 public override string Language
{
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 ('.');
5045 protected virtual string GetCSharpType (string 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";
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
);
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
)
5099 GenericParameterAttributes attrs
= type
.Attributes
;
5100 bool isout
= (attrs
& GenericParameterAttributes
.Covariant
) != 0;
5101 bool isin
= (attrs
& GenericParameterAttributes
.Contravariant
) != 0;
5105 buf
.Append ("out ");
5109 protected override string GetTypeDeclaration (TypeDefinition type
)
5111 string visibility
= GetTypeVisibility (type
.Attributes
);
5112 if (visibility
== null)
5115 StringBuilder buf
= new StringBuilder ();
5117 buf
.Append (visibility
);
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
);
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
));
5142 buf
.Append (GetCSharpType (type
.FullName
) == null
5147 TypeReference basetype
= type
.BaseType
;
5148 if (basetype
!= null && basetype
.FullName
== "System.Object" || type
.IsValueType
) // FIXME
5151 List
<string> interface_names
= DocUtils
.GetUserImplementedInterfaces (type
)
5152 .Select (iface
=> full
.GetName (iface
))
5156 if (basetype
!= null || interface_names
.Count
> 0)
5159 if (basetype
!= null) {
5160 buf
.Append (full
.GetName (basetype
));
5161 if (interface_names
.Count
> 0)
5165 for (int i
= 0; i
< interface_names
.Count
; i
++){
5168 buf
.Append (interface_names
[i
]);
5170 AppendGenericTypeConstraints (buf
, type
);
5173 return buf
.ToString ();
5176 static string GetTypeKind (TypeDefinition t
)
5182 if (t
.IsClass
|| t
.FullName
== "System.Enum")
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
:
5196 case TypeAttributes
.NestedFamily
:
5197 case TypeAttributes
.NestedFamORAssem
:
5205 protected override StringBuilder
AppendGenericTypeConstraints (StringBuilder buf
, TypeReference type
)
5207 if (type
.GenericParameters
.Count
== 0)
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)
5220 bool isref
= (attrs
& GenericParameterAttributes
.ReferenceTypeConstraint
) != 0;
5221 bool isvt
= (attrs
& GenericParameterAttributes
.NotNullableValueTypeConstraint
) != 0;
5222 bool isnew
= (attrs
& GenericParameterAttributes
.DefaultConstructorConstraint
) != 0;
5225 if (!isref
&& !isvt
&& !isnew
&& constraints
.Count
== 0)
5227 buf
.Append (" where ").Append (genArg
.Name
).Append (" : ");
5229 buf
.Append ("class");
5233 buf
.Append ("struct");
5236 if (constraints
.Count
> 0 && !isvt
) {
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
) {
5246 buf
.Append ("new()");
5252 protected override string GetConstructorDeclaration (MethodDefinition constructor
)
5254 StringBuilder buf
= new StringBuilder ();
5255 AppendVisibility (buf
, constructor
);
5256 if (buf
.Length
== 0)
5260 base.AppendTypeName (buf
, constructor
.DeclaringType
.Name
).Append (' ');
5261 AppendParameters (buf
, constructor
, constructor
.Parameters
);
5264 return buf
.ToString ();
5267 protected override string GetMethodDeclaration (MethodDefinition method
)
5269 string decl
= base.GetMethodDeclaration (method
);
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
))
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)
5292 return AppendConstraints (buf
, method
.GenericParameters
);
5295 protected override string RefTypeModifier
{
5299 protected override string GetFinalizerName (MethodDefinition method
)
5301 return "~" + method
.DeclaringType
.Name
+ " ()";
5304 protected override StringBuilder
AppendVisibility (StringBuilder buf
, MethodDefinition method
)
5308 if (method
.IsPublic
)
5309 return buf
.Append ("public");
5310 if (method
.IsFamily
|| method
.IsFamilyOrAssembly
)
5311 return buf
.Append ("protected");
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) {
5337 buf
.Append (args
[0].Name
);
5338 for (int i
= 1; i
< args
.Count
; ++i
)
5339 buf
.Append (",").Append (args
[i
].Name
);
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
)
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
) {
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 ");
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
));
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))
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
);
5409 buf
.Append (visibility
= "public");
5411 // Pick an accessor to use for static/virtual/override/etc. checks.
5412 method
= property
.SetMethod
;
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";
5422 modifiers
+= " override";
5424 TypeDefinition declDef
= (TypeDefinition
) method
.DeclaringType
;
5425 if (method
.IsAbstract
&& !declDef
.IsInterface
)
5426 modifiers
+= " abstract";
5428 modifiers
+= " sealed";
5429 if (modifiers
== " virtual sealed")
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
) {
5443 buf
.Append (name
== "this" ? name
: DocUtils
.GetPropertyName (property
));
5445 if (property
.Parameters
.Count
!= 0) {
5446 AppendParameters (buf
, method
, property
.Parameters
, '[', ']');
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;");
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)
5476 if (declType
.IsEnum
)
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
);
5491 return buf
.ToString ();
5494 static StringBuilder
AppendFieldVisibility (StringBuilder buf
, FieldDefinition field
)
5497 return buf
.Append ("public");
5498 if (field
.IsFamily
|| field
.IsFamilyOrAssembly
)
5499 return buf
.Append ("protected");
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 ())
5509 if (field
.HasConstant
&& field
.IsLiteral
) {
5512 val
= field
.Constant
;
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
);
5523 value = "\"" + value + "\"";
5524 buf
.Append (" = ").Append (value);
5530 protected override string GetEventDeclaration (EventDefinition e
)
5532 StringBuilder buf
= new StringBuilder ();
5533 if (AppendVisibility (buf
, e
.AddMethod
).Length
== 0) {
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
)
5554 class DocTypeFullMemberFormatter
: MemberFormatter
{
5555 public static readonly MemberFormatter Default
= new DocTypeFullMemberFormatter ();
5557 protected override char NestedTypeSeparator
{
5562 class DocTypeMemberFormatter
: DocTypeFullMemberFormatter
{
5563 protected override StringBuilder
AppendNamespace (StringBuilder buf
, TypeReference type
)
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) {
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);
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);
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));
5619 base.AppendTypeName (buf, type, context);
5621 int numArgs = type.GenericParameters.Count;
5622 if (type.DeclaringType != null)
5623 numArgs -= type.GenericParameters.Count;
5625 buf.Append ('`
').Append (numArgs);
5632 protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
5634 buf.Append (ArrayDelimeters [0]);
5635 int rank = array.Rank;
5638 for (int i = 1; i < rank; ++i) {
5642 return buf.Append (ArrayDelimeters [1]);
5645 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type, DynamicParserContext context)
5648 base.AppendGenericType (buf, type, context);
5650 AppendType (buf, type, context);
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) {
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;
5668 buf.Append ('`
').Append (numArgs);
5673 protected override string GetConstructorName (MethodReference constructor)
5675 return GetMethodDefinitionName (constructor, "#ctor");
5678 protected override string GetMethodName (MethodReference method)
5681 MethodDefinition methodDef = method as MethodDefinition;
5682 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
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));
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;
5708 genDeclType = method.DeclaringType;
5709 genDeclMethod = method;
5710 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
5714 genDeclMethod = null;
5716 return buf.ToString ();
5719 private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
5721 if (parameters.Count == 0)
5726 AppendParameter (buf, genArgs, parameters [0]);
5727 for (int i = 1; i < parameters.Count; ++i) {
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;
5743 protected override string GetPropertyName (PropertyReference property)
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;
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));
5769 IList<ParameterDefinition> parameters = property.Parameters;
5770 if (parameters.Count > 0) {
5771 genDeclType = property.DeclaringType;
5773 IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
5774 AppendParameter (buf, genArgs, parameters [0]);
5775 for (int i = 1; i < parameters.Count; ++i) {
5777 AppendParameter (buf, genArgs, parameters [i]);
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);
5805 protected override string GetConstructorDeclaration (MethodDefinition constructor)
5807 string name = GetName (constructor);
5813 protected override string GetMethodDeclaration (MethodDefinition method)
5815 string name = GetName (method);
5818 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
5819 genDeclType = method.DeclaringType;
5820 genDeclMethod = method;
5821 name += "~" + GetName (method.ReturnType);
5823 genDeclMethod = null;
5828 protected override string GetPropertyDeclaration (PropertyDefinition property)
5830 string name = GetName (property);
5836 protected override string GetFieldDeclaration (FieldDefinition field)
5838 string name = GetName (field);
5844 protected override string GetEventDeclaration (EventDefinition e)
5846 string name = GetName (e);
5853 class FileNameMemberFormatter : SlashDocMemberFormatter {
5854 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
5859 protected override char NestedTypeSeparator {
5864 class ResolvedTypeInfo {
5865 TypeDefinition typeDef;
5867 public ResolvedTypeInfo (TypeReference value) {
5871 public TypeReference Reference { get; private set; }
5873 public TypeDefinition Definition {
5875 if (typeDef == null) {
5876 typeDef = Reference.Resolve ();
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;
5889 returnvalue = "null";
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
5897 returnvalue = "typeof(" + v.ToString () + ")";
5901 if (valueType.FullName == "System.String") {
5902 returnvalue = "\"" + v.ToString () + "\"";
5905 if (valueType.FullName == "System.Char") {
5906 returnvalue = "'" + v.ToString () + "'";
5910 returnvalue = (bool)v ? "true" : "false";
5914 TypeDefinition valueDef = type.Definition;
5915 if (valueDef == null || !valueDef.IsEnum) {
5916 returnvalue = v.ToString ();
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];
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 ();
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 ());
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);
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 = "";
6018 return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
6021 subminor == 0 ? "" : "_" + subminor.ToString (),
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);