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
.Diagnostics
;
9 using System
.Globalization
;
14 using System
.Xml
.XPath
;
19 using MyXmlNodeList
= System
.Collections
.Generic
.List
<System
.Xml
.XmlNode
>;
20 using StringList
= System
.Collections
.Generic
.List
<string>;
21 using StringToStringMap
= System
.Collections
.Generic
.Dictionary
<string, string>;
22 using StringToXmlNodeMap
= System
.Collections
.Generic
.Dictionary
<string, System
.Xml
.XmlNode
>;
24 namespace Mono
.Documentation
{
26 class MDocUpdater
: MDocCommand
29 List
<AssemblyDefinition
> assemblies
;
30 readonly DefaultAssemblyResolver assemblyResolver
= new DefaultAssemblyResolver();
34 bool no_assembly_versions
;
35 ExceptionLocations
? exceptions
;
37 int additions
= 0, deletions
= 0;
39 static XmlDocument slashdocs
;
44 static readonly MemberFormatter csharpFullFormatter
= new CSharpFullMemberFormatter ();
45 static readonly MemberFormatter csharpFormatter
= new CSharpMemberFormatter ();
46 static readonly MemberFormatter docTypeFormatter
= new DocTypeMemberFormatter ();
47 static readonly MemberFormatter slashdocFormatter
= new SlashDocMemberFormatter ();
48 static readonly MemberFormatter filenameFormatter
= new FileNameMemberFormatter ();
50 MyXmlNodeList extensionMethods
= new MyXmlNodeList ();
52 public override void Run (IEnumerable
<string> args
)
54 show_exceptions
= DebugOutput
;
56 var types
= new List
<string> ();
57 var p
= new OptionSet () {
59 "Delete removed members from the XML files.",
60 v
=> delete
= v
!= null },
62 "Document potential exceptions that members can generate. {SOURCES} " +
63 "is a comma-separated list of:\n" +
64 " asm Method calls in same assembly\n" +
65 " depasm Method calls in dependent assemblies\n" +
66 " all Record all possible exceptions\n" +
67 "If nothing is specified, then only exceptions from the member will " +
69 v
=> exceptions
= ParseExceptionLocations (v
) },
71 "Specify a {FLAG} to alter behavior. See later -f* options for available flags.",
74 case "no-assembly-versions":
75 no_assembly_versions
= true;
78 throw new Exception ("Unsupported flag `" + v
+ "'.");
81 { "fno-assembly-versions",
82 "Do not generate //AssemblyVersion elements.",
83 v
=> no_assembly_versions
= v
!= null },
85 "Import documentation from {FILE}.",
88 "Check for assembly references in {DIRECTORY}.",
89 v
=> assemblyResolver
.AddSearchDirectory (v
) },
91 "Root {DIRECTORY} to generate/update documentation.",
94 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
95 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
96 v
=> assemblyResolver
.AddSearchDirectory (Path
.GetDirectoryName (v
)) },
98 "Manually specify the assembly {VERSION} that new members were added in.",
101 "Only update documentation for {TYPE}.",
102 v
=> types
.Add (v
) },
104 var assemblies
= Parse (p
, args
, "update",
105 "[OPTIONS]+ ASSEMBLIES",
106 "Create or update documentation from ASSEMBLIES.");
107 if (assemblies
== null)
109 if (assemblies
.Count
== 0)
110 Error ("No assemblies specified.");
112 foreach (var dir
in assemblies
113 .Where (a
=> a
.Contains (Path
.DirectorySeparatorChar
))
114 .Select (a
=> Path
.GetDirectoryName (a
)))
115 assemblyResolver
.AddSearchDirectory (dir
);
117 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
120 throw new InvalidOperationException("The --out option is required.");
122 this.assemblies
= assemblies
.Select (a
=> LoadAssembly (a
)).ToList ();
124 if (import
!= null && ecmadocs
== null && slashdocs
== null) {
126 XmlReader r
= new XmlTextReader (import
);
128 while (r
.NodeType
!= XmlNodeType
.Element
) {
130 Error ("Unable to read XML file: {0}.", import
);
132 if (r
.LocalName
== "doc") {
133 var xml
= File
.ReadAllText (import
);
134 // Ensure Unix line endings
135 xml
= xml
.Replace ("\r", "");
136 slashdocs
= new XmlDocument();
137 slashdocs
.LoadXml (xml
);
139 else if (r
.LocalName
== "Libraries") {
140 ecmadocs
= new XmlTextReader (import
);
143 Error ("Unsupported XML format within {0}.", import
);
146 } catch (Exception e
) {
147 Environment
.ExitCode
= 1;
148 Error ("Could not load XML file: {0}.", e
.Message
);
152 // PERFORM THE UPDATES
155 DoUpdateTypes (srcPath
, types
, srcPath
);
157 else if (opts
.@namespace != null)
158 DoUpdateNS (opts
.@namespace, Path
.Combine (opts
.path
, opts
.@namespace),
159 Path
.Combine (dest_dir
, opts
.@namespace));
162 DoUpdateAssemblies (srcPath
, srcPath
);
164 Console
.WriteLine("Members Added: {0}, Members Deleted: {1}", additions
, deletions
);
167 static ExceptionLocations
ParseExceptionLocations (string s
)
169 ExceptionLocations loc
= ExceptionLocations
.Member
;
172 foreach (var type
in s
.Split (',')) {
174 case "added": loc
|= ExceptionLocations
.AddedMembers
; break;
175 case "all": loc
|= ExceptionLocations
.Assembly
| ExceptionLocations
.DependentAssemblies
; break;
176 case "asm": loc
|= ExceptionLocations
.Assembly
; break;
177 case "depasm": loc
|= ExceptionLocations
.DependentAssemblies
; break;
178 default: throw new NotSupportedException ("Unsupported --exceptions value: " + type
);
184 private void Warning (string format
, params object[] args
)
186 Message (TraceLevel
.Warning
, "mdoc: " + format
, args
);
189 private AssemblyDefinition
LoadAssembly (string name
)
191 AssemblyDefinition assembly
= null;
193 assembly
= AssemblyFactory
.GetAssembly (name
);
194 } catch (System
.IO
.FileNotFoundException
) { }
196 if (assembly
== null)
197 throw new InvalidOperationException("Assembly " + name
+ " not found.");
199 assembly
.Resolver
= assemblyResolver
;
203 private static void WriteXml(XmlElement element
, System
.IO
.TextWriter output
) {
204 OrderTypeAttributes (element
);
205 XmlTextWriter writer
= new XmlTextWriter(output
);
206 writer
.Formatting
= Formatting
.Indented
;
207 writer
.Indentation
= 2;
208 writer
.IndentChar
= ' ';
209 element
.WriteTo(writer
);
213 private static void WriteFile (string filename
, FileMode mode
, Action
<TextWriter
> action
)
215 if (!File
.Exists (filename
)) {
216 using (var writer
= OpenWrite (filename
, mode
))
221 string tmpFile
= filename
+ ".tmp";
225 using (var writer
= OpenWrite (tmpFile
, mode
))
228 using (var a
= File
.OpenRead (filename
))
229 using (var b
= File
.OpenRead (tmpFile
)) {
230 if (a
.Length
== b
.Length
)
231 move
= !FileContentsIdentical (a
, b
);
235 File
.Delete (filename
);
236 File
.Move (tmpFile
, filename
);
240 if (!move
&& File
.Exists (tmpFile
))
241 File
.Delete (tmpFile
);
245 static bool FileContentsIdentical (Stream a
, Stream b
)
247 byte[] ba
= new byte[4096];
248 byte[] bb
= new byte[4096];
251 while ((ra
= a
.Read (ba
, 0, ba
.Length
)) > 0 &&
252 (rb
= b
.Read (bb
, 0, bb
.Length
)) > 0) {
255 for (int i
= 0; i
< ra
; ++i
) {
256 if (ba
[i
] != bb
[i
])
263 private static void OrderTypeAttributes (XmlElement e
)
265 foreach (XmlElement type
in e
.SelectNodes ("//Type")) {
266 OrderTypeAttributes (type
.Attributes
);
270 static readonly string[] TypeAttributeOrder
= {
271 "Name", "FullName", "FullNameSP", "Maintainer"
274 private static void OrderTypeAttributes (XmlAttributeCollection c
)
276 XmlAttribute
[] attrs
= new XmlAttribute
[TypeAttributeOrder
.Length
];
277 for (int i
= 0; i
< c
.Count
; ++i
) {
278 XmlAttribute a
= c
[i
];
279 for (int j
= 0; j
< TypeAttributeOrder
.Length
; ++j
) {
280 if (a
.Name
== TypeAttributeOrder
[j
]) {
286 for (int i
= attrs
.Length
-1; i
>= 0; --i
) {
287 XmlAttribute n
= attrs
[i
];
290 XmlAttribute r
= null;
291 for (int j
= i
+1; j
< attrs
.Length
; ++j
) {
292 if (attrs
[j
] != null) {
300 c
.InsertBefore (n
, r
);
304 private XmlDocument
CreateIndexStub()
306 XmlDocument index
= new XmlDocument();
308 XmlElement index_root
= index
.CreateElement("Overview");
309 index
.AppendChild(index_root
);
311 if (assemblies
.Count
== 0)
312 throw new Exception ("No assembly");
314 XmlElement index_assemblies
= index
.CreateElement("Assemblies");
315 index_root
.AppendChild(index_assemblies
);
317 XmlElement index_remarks
= index
.CreateElement("Remarks");
318 index_remarks
.InnerText
= "To be added.";
319 index_root
.AppendChild(index_remarks
);
321 XmlElement index_copyright
= index
.CreateElement("Copyright");
322 index_copyright
.InnerText
= "To be added.";
323 index_root
.AppendChild(index_copyright
);
325 XmlElement index_types
= index
.CreateElement("Types");
326 index_root
.AppendChild(index_types
);
331 private static void WriteNamespaceStub(string ns
, string outdir
) {
332 XmlDocument index
= new XmlDocument();
334 XmlElement index_root
= index
.CreateElement("Namespace");
335 index
.AppendChild(index_root
);
337 index_root
.SetAttribute("Name", ns
);
339 XmlElement index_docs
= index
.CreateElement("Docs");
340 index_root
.AppendChild(index_docs
);
342 XmlElement index_summary
= index
.CreateElement("summary");
343 index_summary
.InnerText
= "To be added.";
344 index_docs
.AppendChild(index_summary
);
346 XmlElement index_remarks
= index
.CreateElement("remarks");
347 index_remarks
.InnerText
= "To be added.";
348 index_docs
.AppendChild(index_remarks
);
350 WriteFile (outdir
+ "/ns-" + ns
+ ".xml", FileMode
.CreateNew
,
351 writer
=> WriteXml (index
.DocumentElement
, writer
));
354 public void DoUpdateTypes (string basepath
, List
<string> typenames
, string dest
)
356 var found
= new HashSet
<string> ();
357 foreach (AssemblyDefinition assembly
in assemblies
) {
358 foreach (DocsTypeInfo docsTypeInfo
in GetTypes (assembly
, typenames
)) {
359 string relpath
= DoUpdateType (docsTypeInfo
.Type
, basepath
, dest
, docsTypeInfo
.EcmaDocs
);
361 found
.Add (docsTypeInfo
.Type
.FullName
);
364 var notFound
= from n
in typenames where
!found
.Contains (n
) select n
;
366 throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound
.ToArray ()));
369 public string DoUpdateType (TypeDefinition type
, string basepath
, string dest
, XmlReader ecmaDocsType
)
371 if (type
.Namespace
== null)
372 Warning ("warning: The type `{0}' is in the root namespace. This may cause problems with display within monodoc.",
374 if (!IsPublic (type
))
377 // Must get the A+B form of the type name.
378 string typename
= GetTypeFileName(type
);
380 string reltypefile
= DocUtils
.PathCombine (DocUtils
.GetNamespace (type
), typename
+ ".xml");
381 string typefile
= Path
.Combine (basepath
, reltypefile
);
382 System
.IO
.FileInfo file
= new System
.IO
.FileInfo(typefile
);
384 string output
= null;
387 } else if (dest
== "-") {
390 output
= Path
.Combine (dest
, reltypefile
);
395 XmlDocument basefile
= new XmlDocument();
397 basefile
.Load(typefile
);
398 } catch (Exception e
) {
399 throw new InvalidOperationException("Error loading " + typefile
+ ": " + e
.Message
, e
);
402 DoUpdateType2("Updating", basefile
, type
, output
, false, ecmaDocsType
);
405 XmlElement td
= StubType(type
, output
, ecmaDocsType
);
409 System
.IO
.DirectoryInfo dir
= new System
.IO
.DirectoryInfo (DocUtils
.PathCombine (dest
, type
.Namespace
));
412 Console
.WriteLine("Namespace Directory Created: " + type
.Namespace
);
418 public void DoUpdateNS (string ns
, string nspath
, string outpath
)
420 Dictionary
<TypeDefinition
, object> seenTypes
= new Dictionary
<TypeDefinition
,object> ();
421 AssemblyDefinition assembly
= assemblies
[0];
423 foreach (System
.IO
.FileInfo file
in new System
.IO
.DirectoryInfo(nspath
).GetFiles("*.xml")) {
424 XmlDocument basefile
= new XmlDocument();
425 string typefile
= Path
.Combine(nspath
, file
.Name
);
427 basefile
.Load(typefile
);
428 } catch (Exception e
) {
429 throw new InvalidOperationException("Error loading " + typefile
+ ": " + e
.Message
, e
);
433 GetTypeFileName (basefile
.SelectSingleNode("Type/@FullName").InnerText
);
434 TypeDefinition type
= assembly
.GetType(typename
);
436 Warning ("Type no longer in assembly: " + typename
);
440 seenTypes
[type
] = seenTypes
;
441 DoUpdateType2("Updating", basefile
, type
, Path
.Combine(outpath
, file
.Name
), false, null);
444 // Stub types not in the directory
445 foreach (DocsTypeInfo docsTypeInfo
in GetTypes (assembly
, null)) {
446 TypeDefinition type
= docsTypeInfo
.Type
;
447 if (type
.Namespace
!= ns
|| seenTypes
.ContainsKey(type
))
450 XmlElement td
= StubType(type
, Path
.Combine(outpath
, GetTypeFileName(type
) + ".xml"), docsTypeInfo
.EcmaDocs
);
451 if (td
== null) continue;
455 private static string GetTypeFileName (TypeReference type
)
457 return filenameFormatter
.GetName (type
);
460 public static string GetTypeFileName (string typename
)
462 StringBuilder filename
= new StringBuilder (typename
.Length
);
466 for (int i
= 0; i
< typename
.Length
; ++i
) {
467 char c
= typename
[i
];
476 filename
.Append ('`').Append ((numArgs
+1).ToString());
491 return filename
.ToString ();
494 private void AddIndexAssembly (AssemblyDefinition assembly
, XmlElement parent
)
496 XmlElement index_assembly
= parent
.OwnerDocument
.CreateElement("Assembly");
497 index_assembly
.SetAttribute ("Name", assembly
.Name
.Name
);
498 index_assembly
.SetAttribute ("Version", assembly
.Name
.Version
.ToString());
499 MakeAttributes (index_assembly
, assembly
.CustomAttributes
, 0);
500 parent
.AppendChild(index_assembly
);
503 private void DoUpdateAssemblies (string source
, string dest
)
505 string indexfile
= dest
+ "/index.xml";
507 if (System
.IO
.File
.Exists(indexfile
)) {
508 index
= new XmlDocument();
509 index
.Load(indexfile
);
512 ClearElement(index
.DocumentElement
, "Assembly");
513 ClearElement(index
.DocumentElement
, "Attributes");
515 index
= CreateIndexStub();
518 string defaultTitle
= "Untitled";
519 if (assemblies
.Count
== 1)
520 defaultTitle
= assemblies
[0].Name
.Name
;
521 WriteElementInitialText(index
.DocumentElement
, "Title", defaultTitle
);
523 XmlElement index_types
= WriteElement(index
.DocumentElement
, "Types");
524 XmlElement index_assemblies
= WriteElement(index
.DocumentElement
, "Assemblies");
525 index_assemblies
.RemoveAll ();
528 HashSet
<string> goodfiles
= new HashSet
<string> ();
530 foreach (AssemblyDefinition assm
in assemblies
) {
531 AddIndexAssembly (assm
, index_assemblies
);
532 DoUpdateAssembly (assm
, index_types
, source
, dest
, goodfiles
);
535 SortIndexEntries (index_types
);
537 CleanupFiles (dest
, goodfiles
);
538 CleanupIndexTypes (index_types
, goodfiles
);
539 CleanupExtensions (index_types
);
541 WriteFile (indexfile
, FileMode
.Create
,
542 writer
=> WriteXml(index
.DocumentElement
, writer
));
545 private static char[] InvalidFilenameChars
= {'\\', '/', ':', '*', '?', '"', '<', '>', '|'}
;
547 private void DoUpdateAssembly (AssemblyDefinition assembly
, XmlElement index_types
, string source
, string dest
, HashSet
<string> goodfiles
)
549 foreach (DocsTypeInfo docTypeInfo
in GetTypes (assembly
, null)) {
550 TypeDefinition type
= docTypeInfo
.Type
;
551 string typename
= GetTypeFileName(type
);
552 if (!IsPublic (type
) || typename
.IndexOfAny (InvalidFilenameChars
) >= 0)
555 string reltypepath
= DoUpdateType (type
, source
, dest
, docTypeInfo
.EcmaDocs
);
556 if (reltypepath
== null)
559 // Add namespace and type nodes into the index file as needed
560 string ns
= DocUtils
.GetNamespace (type
);
561 XmlElement nsnode
= (XmlElement
) index_types
.SelectSingleNode("Namespace[@Name='" + ns
+ "']");
562 if (nsnode
== null) {
563 nsnode
= index_types
.OwnerDocument
.CreateElement("Namespace");
564 nsnode
.SetAttribute ("Name", ns
);
565 index_types
.AppendChild(nsnode
);
567 string doc_typename
= GetDocTypeName (type
);
568 XmlElement typenode
= (XmlElement
)nsnode
.SelectSingleNode("Type[@Name='" + typename
+ "']");
569 if (typenode
== null) {
570 typenode
= index_types
.OwnerDocument
.CreateElement("Type");
571 typenode
.SetAttribute("Name", typename
);
572 nsnode
.AppendChild(typenode
);
574 if (typename
!= doc_typename
)
575 typenode
.SetAttribute("DisplayName", doc_typename
);
577 typenode
.RemoveAttribute("DisplayName");
578 typenode
.SetAttribute ("Kind", GetTypeKind (type
));
580 // Ensure the namespace index file exists
581 string onsdoc
= DocUtils
.PathCombine (dest
, type
.Namespace
+ ".xml");
582 string nsdoc
= DocUtils
.PathCombine (dest
, "ns-" + type
.Namespace
+ ".xml");
583 if (File
.Exists (onsdoc
)) {
584 File
.Move (onsdoc
, nsdoc
);
587 if (!File
.Exists (nsdoc
)) {
588 Console
.WriteLine("New Namespace File: " + type
.Namespace
);
589 WriteNamespaceStub(type
.Namespace
, dest
);
592 goodfiles
.Add (reltypepath
);
597 public TypeDefinition Type
;
598 public XmlReader EcmaDocs
;
600 public DocsTypeInfo (TypeDefinition type
, XmlReader docs
)
603 this.EcmaDocs
= docs
;
607 IEnumerable
<Mono
.Documentation
.MDocUpdater
.DocsTypeInfo
> GetTypes (AssemblyDefinition assembly
, List
<string> forTypes
)
609 HashSet
<string> seen
= null;
610 if (forTypes
!= null)
612 if (ecmadocs
!= null) {
613 seen
= new HashSet
<string> ();
615 while (ecmadocs
.Read ()) {
616 switch (ecmadocs
.Name
) {
619 typeDepth
= ecmadocs
.Depth
;
620 if (ecmadocs
.NodeType
!= XmlNodeType
.Element
)
622 if (typeDepth
!= ecmadocs
.Depth
) // nested <TypeDefinition/> element?
624 string typename
= ecmadocs
.GetAttribute ("FullName");
625 string typename2
= GetTypeFileName (typename
);
626 if (forTypes
!= null &&
627 forTypes
.BinarySearch (typename
) < 0 &&
628 typename
!= typename2
&&
629 forTypes
.BinarySearch (typename2
) < 0)
632 if ((t
= assembly
.GetType (typename
)) == null &&
633 (t
= assembly
.GetType (typename2
)) == null)
636 if (typename
!= typename2
)
637 seen
.Add (typename2
);
638 Console
.WriteLine (" Import: {0}", t
.FullName
);
639 yield return new DocsTypeInfo (t
, ecmadocs
);
647 foreach (TypeDefinition type
in assembly
.GetTypes()) {
648 if (forTypes
!= null && forTypes
.BinarySearch (type
.FullName
) < 0)
650 if (seen
!= null && seen
.Contains (type
.FullName
))
652 yield return new DocsTypeInfo (type
, null);
653 foreach (TypeDefinition nested
in type
.NestedTypes
)
654 yield return new DocsTypeInfo (nested
, null);
658 private static void SortIndexEntries (XmlElement indexTypes
)
660 XmlNodeList namespaces
= indexTypes
.SelectNodes ("Namespace");
661 XmlNodeComparer c
= new AttributeNameComparer ();
662 SortXmlNodes (indexTypes
, namespaces
, c
);
664 for (int i
= 0; i
< namespaces
.Count
; ++i
)
665 SortXmlNodes (namespaces
[i
], namespaces
[i
].SelectNodes ("Type"), c
);
668 private static void SortXmlNodes (XmlNode parent
, XmlNodeList children
, XmlNodeComparer comparer
)
670 MyXmlNodeList l
= new MyXmlNodeList (children
.Count
);
671 for (int i
= 0; i
< children
.Count
; ++i
)
672 l
.Add (children
[i
]);
674 for (int i
= l
.Count
- 1; i
> 0; --i
) {
675 parent
.InsertBefore (parent
.RemoveChild ((XmlNode
) l
[i
-1]), (XmlNode
) l
[i
]);
679 abstract class XmlNodeComparer
: IComparer
, IComparer
<XmlNode
>
681 public abstract int Compare (XmlNode x
, XmlNode y
);
683 public int Compare (object x
, object y
)
685 return Compare ((XmlNode
) x
, (XmlNode
) y
);
689 class AttributeNameComparer
: XmlNodeComparer
{
692 public AttributeNameComparer ()
697 public AttributeNameComparer (string attribute
)
699 this.attribute
= attribute
;
702 public override int Compare (XmlNode x
, XmlNode y
)
704 return x
.Attributes
[attribute
].Value
.CompareTo (y
.Attributes
[attribute
].Value
);
708 class VersionComparer
: XmlNodeComparer
{
709 public override int Compare (XmlNode x
, XmlNode y
)
711 // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
712 string a
= GetVersion (x
.InnerText
);
713 string b
= GetVersion (y
.InnerText
);
714 return new Version (a
).CompareTo (new Version (b
));
717 static string GetVersion (string v
)
719 int n
= v
.IndexOf ("x");
722 return v
.Substring (0, n
-1);
726 private static string GetTypeKind (TypeDefinition type
)
729 return "Enumeration";
730 if (type
.IsValueType
)
732 if (type
.IsInterface
)
734 if (DocUtils
.IsDelegate (type
))
736 if (type
.IsClass
|| type
.FullName
== "System.Enum") // FIXME
738 throw new ArgumentException ("Unknown kind for type: " + type
.FullName
);
741 private static bool IsPublic (TypeDefinition type
)
743 TypeDefinition decl
= type
;
744 while (decl
!= null) {
745 if (!(decl
.IsPublic
|| decl
.IsNestedPublic
)) {
748 decl
= (TypeDefinition
) decl
.DeclaringType
;
753 private void CleanupFiles (string dest
, HashSet
<string> goodfiles
)
755 // Look for files that no longer correspond to types
756 foreach (System
.IO
.DirectoryInfo nsdir
in new System
.IO
.DirectoryInfo(dest
).GetDirectories("*")) {
757 foreach (System
.IO
.FileInfo typefile
in nsdir
.GetFiles("*.xml")) {
758 string relTypeFile
= Path
.Combine(nsdir
.Name
, typefile
.Name
);
759 if (!goodfiles
.Contains (relTypeFile
)) {
760 XmlDocument doc
= new XmlDocument ();
761 doc
.Load (typefile
.FullName
);
762 XmlElement e
= doc
.SelectSingleNode("/Type") as XmlElement
;
763 if (UpdateAssemblyVersions(e
, GetAssemblyVersions(), false)) {
764 using (TextWriter writer
= OpenWrite (typefile
.FullName
, FileMode
.Truncate
))
765 WriteXml(doc
.DocumentElement
, writer
);
766 goodfiles
.Add (relTypeFile
);
769 string newname
= typefile
.FullName
+ ".remove";
770 try { System.IO.File.Delete(newname); }
catch (Exception
) { }
771 try { typefile.MoveTo(newname); }
catch (Exception
) { }
772 Console
.WriteLine("Class no longer present; file renamed: " + Path
.Combine(nsdir
.Name
, typefile
.Name
));
778 private static TextWriter
OpenWrite (string path
, FileMode mode
)
780 var w
= new StreamWriter (
781 new FileStream (path
, mode
),
782 new UTF8Encoding (false)
788 private string[] GetAssemblyVersions ()
790 return (from a
in assemblies
select GetAssemblyVersion (a
)).ToArray ();
793 private static void CleanupIndexTypes (XmlElement index_types
, HashSet
<string> goodfiles
)
795 // Look for type nodes that no longer correspond to types
796 MyXmlNodeList
remove = new MyXmlNodeList ();
797 foreach (XmlElement typenode
in index_types
.SelectNodes("Namespace/Type")) {
798 string fulltypename
= Path
.Combine (((XmlElement
)typenode
.ParentNode
).GetAttribute("Name"), typenode
.GetAttribute("Name") + ".xml");
799 if (!goodfiles
.Contains (fulltypename
)) {
800 remove.Add (typenode
);
803 foreach (XmlNode n
in remove)
804 n
.ParentNode
.RemoveChild (n
);
807 private void CleanupExtensions (XmlElement index_types
)
809 XmlNode e
= index_types
.SelectSingleNode ("/Overview/ExtensionMethods");
810 if (extensionMethods
.Count
== 0) {
813 index_types
.SelectSingleNode ("/Overview").RemoveChild (e
);
817 e
= index_types
.OwnerDocument
.CreateElement ("ExtensionMethods");
818 index_types
.SelectSingleNode ("/Overview").AppendChild (e
);
822 extensionMethods
.Sort (DefaultExtensionMethodComparer
);
823 foreach (XmlNode m
in extensionMethods
) {
824 e
.AppendChild (index_types
.OwnerDocument
.ImportNode (m
, true));
828 class ExtensionMethodComparer
: XmlNodeComparer
{
829 public override int Compare (XmlNode x
, XmlNode y
)
831 XmlNode xLink
= x
.SelectSingleNode ("Member/Link");
832 XmlNode yLink
= y
.SelectSingleNode ("Member/Link");
834 int n
= xLink
.Attributes
["Type"].Value
.CompareTo (
835 yLink
.Attributes
["Type"].Value
);
838 n
= xLink
.Attributes
["Member"].Value
.CompareTo (
839 yLink
.Attributes
["Member"].Value
);
840 if (n
== 0 && !object.ReferenceEquals (x
, y
))
841 throw new InvalidOperationException ("Duplicate extension method found!");
846 static readonly XmlNodeComparer DefaultExtensionMethodComparer
= new ExtensionMethodComparer ();
848 public void DoUpdateType2 (string message
, XmlDocument basefile
, TypeDefinition type
, string output
, bool insertSince
, XmlReader ecmaDocsType
)
850 Console
.WriteLine(message
+ ": " + type
.FullName
);
852 StringToXmlNodeMap seenmembers
= new StringToXmlNodeMap ();
854 // Update type metadata
855 UpdateType(basefile
.DocumentElement
, type
, ecmaDocsType
);
857 if (ecmaDocsType
!= null) {
858 while (ecmaDocsType
.Name
!= "Members" && ecmaDocsType
.Read ()) {
861 if (ecmaDocsType
.IsEmptyElement
)
865 // Update existing members. Delete member nodes that no longer should be there,
866 // and remember what members are already documented so we don't add them again.
868 MyXmlNodeList todelete
= new MyXmlNodeList ();
869 foreach (DocsNodeInfo info
in GetDocumentationMembers (basefile
, type
, ecmaDocsType
)) {
870 XmlElement oldmember
= info
.Node
;
871 IMemberReference oldmember2
= info
.Member
;
872 string sig
= oldmember2
!= null ? MakeMemberSignature(oldmember2
) : null;
874 // Interface implementations and overrides are deleted from the docs
875 // unless the overrides option is given.
876 if (oldmember2
!= null && sig
== null)
879 // Deleted (or signature changed)
880 if (oldmember2
== null) {
881 if (UpdateAssemblyVersions (oldmember
, new string[]{ GetAssemblyVersion (type.Module.Assembly) }
, false))
883 DeleteMember ("Member Removed", output
, oldmember
, todelete
);
888 if (seenmembers
.ContainsKey (sig
)) {
889 if (object.ReferenceEquals (oldmember
, seenmembers
[sig
])) {
890 // ignore, already seen
892 else if (DefaultMemberComparer
.Compare (oldmember
, seenmembers
[sig
]) == 0)
893 DeleteMember ("Duplicate Member Found", output
, oldmember
, todelete
);
895 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig
);
899 // Update signature information
902 seenmembers
.Add (sig
, oldmember
);
904 foreach (XmlElement oldmember
in todelete
)
905 oldmember
.ParentNode
.RemoveChild (oldmember
);
908 if (!DocUtils
.IsDelegate (type
)) {
909 XmlNode members
= WriteElement (basefile
.DocumentElement
, "Members");
910 foreach (IMemberReference m
in type
.GetMembers()) {
911 if (m
is TypeDefinition
) continue;
913 string sig
= MakeMemberSignature(m
);
914 if (sig
== null) continue;
915 if (seenmembers
.ContainsKey(sig
)) continue;
917 XmlElement mm
= MakeMember(basefile
, new DocsNodeInfo (null, m
));
918 if (mm
== null) continue;
919 members
.AppendChild( mm
);
921 Console
.WriteLine("Member Added: " + mm
.SelectSingleNode("MemberSignature/@Value").InnerText
);
926 // Import code snippets from files
927 foreach (XmlNode code
in basefile
.GetElementsByTagName("code")) {
928 if (!(code
is XmlElement
)) continue;
929 string file
= ((XmlElement
)code
).GetAttribute("src");
930 string lang
= ((XmlElement
)code
).GetAttribute("lang");
932 string src
= GetCodeSource (lang
, Path
.Combine (srcPath
, file
));
934 code
.InnerText
= src
;
938 if (insertSince
&& since
!= null) {
939 XmlNode docs
= basefile
.DocumentElement
.SelectSingleNode("Docs");
940 docs
.AppendChild (CreateSinceNode (basefile
));
944 XmlElement d
= basefile
.DocumentElement
["Docs"];
945 XmlElement m
= basefile
.DocumentElement
["Members"];
946 if (d
!= null && m
!= null)
947 basefile
.DocumentElement
.InsertBefore (
948 basefile
.DocumentElement
.RemoveChild (d
), m
);
953 WriteXml(basefile
.DocumentElement
, Console
.Out
);
955 FileInfo file
= new FileInfo (output
);
956 if (!file
.Directory
.Exists
) {
957 Console
.WriteLine("Namespace Directory Created: " + type
.Namespace
);
958 file
.Directory
.Create ();
960 WriteFile (output
, FileMode
.Create
,
961 writer
=> WriteXml(basefile
.DocumentElement
, writer
));
965 private string GetCodeSource (string lang
, string file
)
968 if (lang
== "C#" && (anchorStart
= file
.IndexOf (".cs#")) >= 0) {
969 // Grab the specified region
970 string region
= "#region " + file
.Substring (anchorStart
+ 4);
971 file
= file
.Substring (0, anchorStart
+ 3);
973 using (StreamReader reader
= new StreamReader (file
)) {
975 StringBuilder src
= new StringBuilder ();
977 while ((line
= reader
.ReadLine ()) != null) {
978 if (line
.Trim() == region
) {
979 indent
= line
.IndexOf (region
);
982 if (indent
>= 0 && line
.Trim().StartsWith ("#endregion")) {
987 (line
.Length
> 0 ? line
.Substring (indent
) : string.Empty
) +
990 return src
.ToString ();
992 } catch (Exception e
) {
993 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
994 file
, region
, show_exceptions
? e
.ToString () : e
.Message
);
999 using (StreamReader reader
= new StreamReader (file
))
1000 return reader
.ReadToEnd ();
1001 } catch (Exception e
) {
1002 Warning ("Could not load <code/> file '" + file
+ "': " + e
.Message
);
1007 private IEnumerable
<DocsNodeInfo
> GetDocumentationMembers (XmlDocument basefile
, TypeDefinition type
, XmlReader ecmaDocsMembers
)
1009 if (ecmaDocsMembers
!= null) {
1010 int membersDepth
= ecmaDocsMembers
.Depth
;
1012 while (go
&& ecmaDocsMembers
.Read ()) {
1013 switch (ecmaDocsMembers
.Name
) {
1015 if (membersDepth
!= ecmaDocsMembers
.Depth
- 1 || ecmaDocsMembers
.NodeType
!= XmlNodeType
.Element
)
1017 DocumentationMember dm
= new DocumentationMember (ecmaDocsMembers
);
1018 string xp
= GetXPathForMember (dm
);
1019 XmlElement oldmember
= (XmlElement
) basefile
.SelectSingleNode (xp
);
1021 if (oldmember
== null) {
1022 m
= GetMember (type
, dm
);
1024 Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
1025 type
.FullName
, dm
.MemberSignatures
["C#"]);
1026 // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
1029 // oldmember lookup may have failed due to type parameter renames.
1031 oldmember
= (XmlElement
) basefile
.SelectSingleNode (GetXPathForMember (m
));
1032 if (oldmember
== null) {
1033 XmlElement members
= WriteElement(basefile
.DocumentElement
, "Members");
1034 oldmember
= basefile
.CreateElement ("Member");
1035 oldmember
.SetAttribute ("MemberName", dm
.MemberName
);
1036 members
.AppendChild (oldmember
);
1037 foreach (string key
in Sort (dm
.MemberSignatures
.Keys
)) {
1038 XmlElement ms
= basefile
.CreateElement ("MemberSignature");
1039 ms
.SetAttribute ("Language", key
);
1040 ms
.SetAttribute ("Value", (string) dm
.MemberSignatures
[key
]);
1041 oldmember
.AppendChild (ms
);
1043 oldmember
.SetAttribute ("__monodocer-seen__", "true");
1044 Console
.WriteLine ("Member Added: {0}", MakeMemberSignature (m
));
1049 m
= GetMember (type
, new DocumentationMember (oldmember
));
1051 Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
1052 type
.FullName
, dm
.MemberSignatures
["C#"]);
1055 oldmember
.SetAttribute ("__monodocer-seen__", "true");
1057 DocsNodeInfo node
= new DocsNodeInfo (oldmember
, m
);
1058 if (ecmaDocsMembers
.Name
!= "Docs")
1059 throw new InvalidOperationException ("Found " + ecmaDocsMembers
.Name
+ "; expected <Docs/>!");
1060 node
.EcmaDocs
= ecmaDocsMembers
;
1065 if (membersDepth
== ecmaDocsMembers
.Depth
&& ecmaDocsMembers
.NodeType
== XmlNodeType
.EndElement
) {
1072 foreach (XmlElement oldmember
in basefile
.SelectNodes("Type/Members/Member")) {
1073 if (oldmember
.GetAttribute ("__monodocer-seen__") == "true") {
1074 oldmember
.RemoveAttribute ("__monodocer-seen__");
1077 IMemberReference m
= GetMember (type
, new DocumentationMember (oldmember
));
1079 yield return new DocsNodeInfo (oldmember
);
1082 yield return new DocsNodeInfo (oldmember
, m
);
1087 void DeleteMember (string reason
, string output
, XmlNode member
, MyXmlNodeList todelete
)
1089 string format
= output
!= null
1090 ? "{0}: File='{1}'; Signature='{4}'"
1091 : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1095 member
.OwnerDocument
.DocumentElement
.GetAttribute ("FullName"),
1096 member
.Attributes
["MemberName"].Value
,
1097 member
.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value
);
1098 if (!delete
&& MemberDocsHaveUserContent (member
)) {
1099 Warning ("Member deletions must be enabled with the --delete option.");
1101 todelete
.Add (member
);
1106 class MemberComparer
: XmlNodeComparer
{
1107 public override int Compare (XmlNode x
, XmlNode y
)
1110 string xMemberName
= x
.Attributes
["MemberName"].Value
;
1111 string yMemberName
= y
.Attributes
["MemberName"].Value
;
1113 // generic methods *end* with '>'
1114 // it's possible for explicitly implemented generic interfaces to
1115 // contain <...> without being a generic method
1116 if ((!xMemberName
.EndsWith (">") || !yMemberName
.EndsWith (">")) &&
1117 (r
= xMemberName
.CompareTo (yMemberName
)) != 0)
1121 if ((lt
= xMemberName
.IndexOf ("<")) >= 0)
1122 xMemberName
= xMemberName
.Substring (0, lt
);
1123 if ((lt
= yMemberName
.IndexOf ("<")) >= 0)
1124 yMemberName
= yMemberName
.Substring (0, lt
);
1125 if ((r
= xMemberName
.CompareTo (yMemberName
)) != 0)
1128 // if @MemberName matches, then it's either two different types of
1129 // members sharing the same name, e.g. field & property, or it's an
1130 // overloaded method.
1131 // for different type, sort based on MemberType value.
1132 r
= x
.SelectSingleNode ("MemberType").InnerText
.CompareTo (
1133 y
.SelectSingleNode ("MemberType").InnerText
);
1137 // same type -- must be an overloaded method. Sort based on type
1138 // parameter count, then parameter count, then by the parameter
1140 XmlNodeList xTypeParams
= x
.SelectNodes ("TypeParameters/TypeParameter");
1141 XmlNodeList yTypeParams
= y
.SelectNodes ("TypeParameters/TypeParameter");
1142 if (xTypeParams
.Count
!= yTypeParams
.Count
)
1143 return xTypeParams
.Count
<= yTypeParams
.Count
? -1 : 1;
1144 for (int i
= 0; i
< xTypeParams
.Count
; ++i
) {
1145 r
= xTypeParams
[i
].Attributes
["Name"].Value
.CompareTo (
1146 yTypeParams
[i
].Attributes
["Name"].Value
);
1151 XmlNodeList xParams
= x
.SelectNodes ("Parameters/Parameter");
1152 XmlNodeList yParams
= y
.SelectNodes ("Parameters/Parameter");
1153 if (xParams
.Count
!= yParams
.Count
)
1154 return xParams
.Count
<= yParams
.Count
? -1 : 1;
1155 for (int i
= 0; i
< xParams
.Count
; ++i
) {
1156 r
= xParams
[i
].Attributes
["Type"].Value
.CompareTo (
1157 yParams
[i
].Attributes
["Type"].Value
);
1161 // all parameters match, but return value might not match if it was
1162 // changed between one version and another.
1163 XmlNode xReturn
= x
.SelectSingleNode ("ReturnValue/ReturnType");
1164 XmlNode yReturn
= y
.SelectSingleNode ("ReturnValue/ReturnType");
1165 if (xReturn
!= null && yReturn
!= null) {
1166 r
= xReturn
.InnerText
.CompareTo (yReturn
.InnerText
);
1175 static readonly MemberComparer DefaultMemberComparer
= new MemberComparer ();
1177 private static void SortTypeMembers (XmlNode members
)
1179 if (members
== null)
1181 SortXmlNodes (members
, members
.SelectNodes ("Member"), DefaultMemberComparer
);
1184 private static bool MemberDocsHaveUserContent (XmlNode e
)
1186 e
= (XmlElement
)e
.SelectSingleNode("Docs");
1187 if (e
== null) return false;
1188 foreach (XmlElement d
in e
.SelectNodes("*"))
1189 if (d
.InnerText
!= "" && !d
.InnerText
.StartsWith("To be added"))
1194 // UPDATE HELPER FUNCTIONS
1196 private static IMemberReference
GetMember (TypeDefinition type
, DocumentationMember member
)
1198 string membertype
= member
.MemberType
;
1200 string returntype
= member
.ReturnType
;
1202 string docName
= member
.MemberName
;
1203 string[] docTypeParams
= GetTypeParameters (docName
);
1205 // Loop through all members in this type with the same name
1206 foreach (IMemberReference mi
in GetReflectionMembers (type
, docName
)) {
1207 if (mi
is TypeDefinition
) continue;
1208 if (GetMemberType(mi
) != membertype
) continue;
1210 string sig
= MakeMemberSignature(mi
);
1211 if (sig
== null) continue; // not publicly visible
1213 ParameterDefinitionCollection pis
= null;
1214 string[] typeParams
= null;
1215 if (mi
is MethodDefinition
) {
1216 MethodDefinition mb
= (MethodDefinition
) mi
;
1217 pis
= mb
.Parameters
;
1218 if (docTypeParams
!= null && mb
.IsGenericMethod ()) {
1219 GenericParameterCollection args
= mb
.GenericParameters
;
1220 if (args
.Count
== docTypeParams
.Length
) {
1221 typeParams
= args
.Cast
<GenericParameter
> ().Select (p
=> p
.Name
).ToArray ();
1225 else if (mi
is PropertyDefinition
)
1226 pis
= ((PropertyDefinition
)mi
).Parameters
;
1228 int mcount
= member
.Parameters
== null ? 0 : member
.Parameters
.Count
;
1229 int pcount
= pis
== null ? 0 : pis
.Count
;
1230 if (mcount
!= pcount
)
1233 MethodDefinition mDef
= mi
as MethodDefinition
;
1234 if (mDef
!= null && !mDef
.IsConstructor
) {
1235 // Casting operators can overload based on return type.
1236 if (returntype
!= GetReplacedString (
1237 GetDocTypeFullName (((MethodDefinition
)mi
).ReturnType
.ReturnType
),
1238 typeParams
, docTypeParams
)) {
1246 for (int i
= 0; i
< pis
.Count
; i
++) {
1247 string paramType
= GetReplacedString (
1248 GetDocParameterType (pis
[i
].ParameterType
),
1249 typeParams
, docTypeParams
);
1250 if (paramType
!= (string) member
.Parameters
[i
]) {
1255 if (!good
) continue;
1263 private static IEnumerable
<IMemberReference
> GetReflectionMembers (TypeDefinition type
, string docName
)
1265 // need to worry about 4 forms of //@MemberName values:
1266 // 1. "Normal" (non-generic) member names: GetEnumerator
1268 // 2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
1269 // - try as-is, and try type.member (due to "kludge" for property
1271 // 3. "Normal" Generic member names: Sort<T> (CSC)
1272 // - need to remove generic parameters --> "Sort"
1273 // 4. Explicitly-implemented interface members for generic interfaces:
1274 // -- System.Collections.Generic.IEnumerable<T>.Current
1275 // - Try as-is, and try type.member, *keeping* the generic parameters.
1276 // --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
1277 // 5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
1278 // 'IFoo<A>.Method' for explicitly implemented methods; don't interpret
1279 // this as (1) or (2).
1280 if (docName
.IndexOf ('<') == -1 && docName
.IndexOf ('[') == -1) {
1282 foreach (IMemberReference mi
in type
.GetMembers (docName
))
1284 if (CountChars (docName
, '.') > 0)
1285 // might be a property; try only type.member instead of
1286 // namespace.type.member.
1287 foreach (IMemberReference mi
in
1288 type
.GetMembers (DocUtils
.GetTypeDotMember (docName
)))
1295 int startLt
, startType
, startMethod
;
1296 startLt
= startType
= startMethod
= -1;
1297 for (int i
= 0; i
< docName
.Length
; ++i
) {
1298 switch (docName
[i
]) {
1307 if (numLt
== 0 && (i
+ 1) < docName
.Length
)
1308 // there's another character in docName, so this <...> sequence is
1309 // probably part of a generic type -- case 4.
1313 startType
= startMethod
;
1319 string refName
= startLt
== -1 ? docName
: docName
.Substring (0, startLt
);
1321 foreach (IMemberReference mi
in type
.GetMembers (refName
))
1325 foreach (IMemberReference mi
in type
.GetMembers (refName
.Substring (startType
+ 1)))
1328 // If we _still_ haven't found it, we've hit another generic naming issue:
1329 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
1330 // explicitly-implemented METHOD names (not properties), e.g.
1331 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
1332 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
1333 // which the XML docs will contain.
1335 // Alas, we can't derive the Mono name from docName, so we need to iterate
1336 // over all member names, convert them into CSC format, and compare... :-(
1339 foreach (IMemberReference mi
in type
.GetMembers ()) {
1340 if (GetMemberName (mi
) == docName
)
1345 static string[] GetTypeParameters (string docName
)
1347 if (docName
[docName
.Length
-1] != '>')
1349 StringList types
= new StringList ();
1350 int endToken
= docName
.Length
-2;
1351 int i
= docName
.Length
-2;
1353 if (docName
[i
] == ',' || docName
[i
] == '<') {
1354 types
.Add (docName
.Substring (i
+ 1, endToken
- i
));
1357 if (docName
[i
] == '<')
1362 return types
.ToArray ();
1365 static string GetReplacedString (string typeName
, string[] from, string[] to
)
1369 for (int i
= 0; i
< from.Length
; ++i
)
1370 typeName
= typeName
.Replace (from [i
], to
[i
]);
1374 // CREATE A STUB DOCUMENTATION FILE
1376 public XmlElement
StubType (TypeDefinition type
, string output
, XmlReader ecmaDocsType
)
1378 string typesig
= MakeTypeSignature(type
);
1379 if (typesig
== null) return null; // not publicly visible
1381 XmlDocument doc
= new XmlDocument();
1382 XmlElement root
= doc
.CreateElement("Type");
1383 doc
.AppendChild (root
);
1385 DoUpdateType2 ("New Type", doc
, type
, output
, true, ecmaDocsType
);
1390 private XmlElement
CreateSinceNode (XmlDocument doc
)
1392 XmlElement s
= doc
.CreateElement ("since");
1393 s
.SetAttribute ("version", since
);
1397 // STUBBING/UPDATING FUNCTIONS
1399 public void UpdateType (XmlElement root
, TypeDefinition type
, XmlReader ecmaDocsType
)
1401 root
.SetAttribute("Name", GetDocTypeName (type
));
1402 root
.SetAttribute("FullName", GetDocTypeFullName (type
));
1404 WriteElementAttribute(root
, "TypeSignature[@Language='C#']", "Language", "C#");
1405 WriteElementAttribute(root
, "TypeSignature[@Language='C#']", "Value", MakeTypeSignature(type
));
1407 XmlElement ass
= WriteElement(root
, "AssemblyInfo");
1408 WriteElementText(ass
, "AssemblyName", type
.Module
.Assembly
.Name
.Name
);
1409 if (!no_assembly_versions
) {
1410 UpdateAssemblyVersions (root
, type
, true);
1413 var versions
= ass
.SelectNodes ("AssemblyVersion").Cast
<XmlNode
> ().ToList ();
1414 foreach (var version
in versions
)
1415 ass
.RemoveChild (version
);
1417 if (!string.IsNullOrEmpty (type
.Module
.Assembly
.Name
.Culture
))
1418 WriteElementText(ass
, "AssemblyCulture", type
.Module
.Assembly
.Name
.Culture
);
1420 ClearElement(ass
, "AssemblyCulture");
1422 // Why-oh-why do we put assembly attributes in each type file?
1423 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1424 // since they're outdated in current docs, and a waste of space.
1425 //MakeAttributes(ass, type.Assembly, true);
1426 XmlNode assattrs
= ass
.SelectSingleNode("Attributes");
1427 if (assattrs
!= null)
1428 ass
.RemoveChild(assattrs
);
1430 NormalizeWhitespace(ass
);
1432 if (type
.IsGenericType ()) {
1433 MakeTypeParameters (root
, type
.GenericParameters
);
1435 ClearElement(root
, "TypeParameters");
1438 if (type
.BaseType
!= null) {
1439 XmlElement basenode
= WriteElement(root
, "Base");
1441 string basetypename
= GetDocTypeFullName (type
.BaseType
);
1442 if (basetypename
== "System.MulticastDelegate") basetypename
= "System.Delegate";
1443 WriteElementText(root
, "Base/BaseTypeName", basetypename
);
1445 // Document how this type instantiates the generic parameters of its base type
1446 TypeReference origBase
= type
.BaseType
.GetOriginalType ();
1447 if (origBase
.IsGenericType ()) {
1448 ClearElement(basenode
, "BaseTypeArguments");
1449 GenericInstanceType baseInst
= type
.BaseType
as GenericInstanceType
;
1450 GenericArgumentCollection baseGenArgs
= baseInst
== null ? null : baseInst
.GenericArguments
;
1451 GenericParameterCollection baseGenParams
= origBase
.GenericParameters
;
1452 if (baseGenArgs
.Count
!= baseGenParams
.Count
)
1453 throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1454 for (int i
= 0; baseGenArgs
!= null && i
< baseGenArgs
.Count
; i
++) {
1455 GenericParameter param
= baseGenParams
[i
];
1456 TypeReference
value = baseGenArgs
[i
];
1458 XmlElement bta
= WriteElement(basenode
, "BaseTypeArguments");
1459 XmlElement arg
= bta
.OwnerDocument
.CreateElement("BaseTypeArgument");
1460 bta
.AppendChild(arg
);
1461 arg
.SetAttribute ("TypeParamName", param
.Name
);
1462 arg
.InnerText
= GetDocTypeFullName (value);
1466 ClearElement(root
, "Base");
1469 if (!DocUtils
.IsDelegate (type
) && !type
.IsEnum
) {
1470 IEnumerable
<TypeReference
> userInterfaces
= DocUtils
.GetUserImplementedInterfaces (type
);
1471 List
<string> interface_names
= userInterfaces
1472 .Select (iface
=> GetDocTypeFullName (iface
))
1476 XmlElement interfaces
= WriteElement(root
, "Interfaces");
1477 interfaces
.RemoveAll();
1478 foreach (string iname
in interface_names
) {
1479 XmlElement iface
= root
.OwnerDocument
.CreateElement("Interface");
1480 interfaces
.AppendChild(iface
);
1481 WriteElementText(iface
, "InterfaceName", iname
);
1484 ClearElement(root
, "Interfaces");
1487 MakeAttributes (root
, type
.CustomAttributes
, 0);
1489 if (DocUtils
.IsDelegate (type
)) {
1490 MakeTypeParameters (root
, type
.GenericParameters
);
1491 MakeParameters(root
, type
.GetMethod("Invoke").Parameters
);
1492 MakeReturnValue(root
, type
.GetMethod("Invoke"));
1495 DocsNodeInfo typeInfo
= new DocsNodeInfo (WriteElement(root
, "Docs"), type
);
1496 if (ecmaDocsType
!= null) {
1497 if (ecmaDocsType
.Name
!= "Docs") {
1498 int depth
= ecmaDocsType
.Depth
;
1499 while (ecmaDocsType
.Read ()) {
1500 if (ecmaDocsType
.Name
== "Docs" && ecmaDocsType
.Depth
== depth
+ 1)
1504 if (!ecmaDocsType
.IsStartElement ("Docs"))
1505 throw new InvalidOperationException ("Found " + ecmaDocsType
.Name
+ "; expecting <Docs/>!");
1506 typeInfo
.EcmaDocs
= ecmaDocsType
;
1508 MakeDocNode (typeInfo
);
1510 if (!DocUtils
.IsDelegate (type
))
1511 WriteElement (root
, "Members");
1513 NormalizeWhitespace(root
);
1516 static IEnumerable
<T
> Sort
<T
> (IEnumerable
<T
> list
)
1518 List
<T
> l
= new List
<T
> (list
);
1523 private void UpdateMember (DocsNodeInfo info
)
1525 XmlElement me
= (XmlElement
) info
.Node
;
1526 IMemberReference mi
= info
.Member
;
1527 WriteElementAttribute(me
, "MemberSignature[@Language='C#']", "Language", "C#");
1528 WriteElementAttribute(me
, "MemberSignature[@Language='C#']", "Value", MakeMemberSignature(mi
));
1530 WriteElementText(me
, "MemberType", GetMemberType(mi
));
1532 if (!no_assembly_versions
) {
1533 UpdateAssemblyVersions (me
, mi
, true);
1536 ClearElement (me
, "AssemblyInfo");
1539 ICustomAttributeProvider p
= mi
as ICustomAttributeProvider
;
1541 MakeAttributes (me
, p
.CustomAttributes
, 0);
1543 PropertyReference pr
= mi
as PropertyReference
;
1545 PropertyDefinition pd
= pr
.Resolve ();
1546 if (pd
.GetMethod
!= null)
1547 MakeAttributes (me
, pd
.GetMethod
.CustomAttributes
, AttributeFlags
.KeepExistingAttributes
, "get: ");
1548 if (pd
.SetMethod
!= null)
1549 MakeAttributes (me
, pd
.SetMethod
.CustomAttributes
, AttributeFlags
.KeepExistingAttributes
, "set: ");
1551 EventReference er
= mi
as EventReference
;
1553 EventDefinition ed
= er
.Resolve ();
1554 if (ed
.AddMethod
!= null)
1555 MakeAttributes (me
, ed
.AddMethod
.CustomAttributes
, AttributeFlags
.KeepExistingAttributes
, "add: ");
1556 if (ed
.RemoveMethod
!= null)
1557 MakeAttributes (me
, ed
.RemoveMethod
.CustomAttributes
, AttributeFlags
.KeepExistingAttributes
, "remove: ");
1560 MakeReturnValue(me
, mi
);
1561 if (mi
is MethodReference
) {
1562 MethodReference mb
= (MethodReference
) mi
;
1563 if (mb
.IsGenericMethod ())
1564 MakeTypeParameters (me
, mb
.GenericParameters
);
1566 MakeParameters(me
, mi
);
1569 if (mi
is FieldDefinition
&& GetFieldConstValue ((FieldDefinition
)mi
, out fieldValue
))
1570 WriteElementText(me
, "MemberValue", fieldValue
);
1572 info
.Node
= WriteElement (me
, "Docs");
1574 UpdateExtensionMethods (me
, info
);
1577 static readonly string[] ValidExtensionMembers
= {
1586 static readonly string[] ValidExtensionDocMembers
= {
1592 private void UpdateExtensionMethods (XmlElement e
, DocsNodeInfo info
)
1594 MethodDefinition me
= info
.Member
as MethodDefinition
;
1597 if (info
.Parameters
.Count
< 1)
1599 if (!DocUtils
.IsExtensionMethod (me
))
1602 XmlNode em
= e
.OwnerDocument
.CreateElement ("ExtensionMethod");
1603 XmlNode member
= e
.CloneNode (true);
1604 em
.AppendChild (member
);
1605 RemoveExcept (member
, ValidExtensionMembers
);
1606 RemoveExcept (member
.SelectSingleNode ("Docs"), ValidExtensionDocMembers
);
1607 WriteElementText (member
, "MemberType", "ExtensionMethod");
1608 XmlElement link
= member
.OwnerDocument
.CreateElement ("Link");
1609 link
.SetAttribute ("Type", slashdocFormatter
.GetName (me
.DeclaringType
));
1610 link
.SetAttribute ("Member", slashdocFormatter
.GetDeclaration (me
));
1611 member
.AppendChild (link
);
1612 AddTargets (em
, info
);
1614 extensionMethods
.Add (em
);
1617 private static void RemoveExcept (XmlNode node
, string[] except
)
1621 MyXmlNodeList
remove = null;
1622 foreach (XmlNode n
in node
.ChildNodes
) {
1623 if (Array
.BinarySearch (except
, n
.Name
) < 0) {
1625 remove = new MyXmlNodeList ();
1630 foreach (XmlNode n
in remove)
1631 node
.RemoveChild (n
);
1634 private static void AddTargets (XmlNode member
, DocsNodeInfo info
)
1636 XmlElement targets
= member
.OwnerDocument
.CreateElement ("Targets");
1637 member
.PrependChild (targets
);
1638 if (!(info
.Parameters
[0].ParameterType
is GenericParameter
)) {
1639 AppendElementAttributeText (targets
, "Target", "Type",
1640 slashdocFormatter
.GetDeclaration (info
.Parameters
[0].ParameterType
));
1643 GenericParameter gp
= (GenericParameter
) info
.Parameters
[0].ParameterType
;
1644 ConstraintCollection constraints
= gp
.Constraints
;
1645 if (constraints
.Count
== 0)
1646 AppendElementAttributeText (targets
, "Target", "Type", "System.Object");
1648 foreach (TypeReference c
in constraints
)
1649 AppendElementAttributeText(targets
, "Target", "Type",
1650 slashdocFormatter
.GetDeclaration (c
));
1654 private static bool GetFieldConstValue (FieldDefinition field
, out string value)
1657 TypeDefinition type
= field
.DeclaringType
.Resolve ();
1658 if (type
!= null && type
.IsEnum
) return false;
1660 if (type
!= null && type
.IsGenericType ()) return false;
1661 if (!field
.HasConstant
)
1663 if (field
.IsLiteral
) {
1664 object val
= field
.Constant
;
1665 if (val
== null) value = "null";
1666 else if (val
is Enum
) value = val
.ToString();
1667 else if (val
is IFormattable
) {
1668 value = ((IFormattable
)val
).ToString();
1670 value = "\"" + value + "\"";
1672 if (value != null && value != "")
1678 // XML HELPER FUNCTIONS
1680 private static XmlElement
WriteElement(XmlNode parent
, string element
) {
1681 XmlElement ret
= (XmlElement
)parent
.SelectSingleNode(element
);
1683 string[] path
= element
.Split('/');
1684 foreach (string p
in path
) {
1685 ret
= (XmlElement
)parent
.SelectSingleNode(p
);
1688 if (ename
.IndexOf('[') >= 0) // strip off XPath predicate
1689 ename
= ename
.Substring(0, ename
.IndexOf('['));
1690 ret
= parent
.OwnerDocument
.CreateElement(ename
);
1691 parent
.AppendChild(ret
);
1700 private static void WriteElementText(XmlNode parent
, string element
, string value) {
1701 XmlElement node
= WriteElement(parent
, element
);
1702 node
.InnerText
= value;
1705 static XmlElement
AppendElementText (XmlNode parent
, string element
, string value)
1707 XmlElement n
= parent
.OwnerDocument
.CreateElement (element
);
1708 parent
.AppendChild (n
);
1709 n
.InnerText
= value;
1713 static XmlElement
AppendElementAttributeText (XmlNode parent
, string element
, string attribute
, string value)
1715 XmlElement n
= parent
.OwnerDocument
.CreateElement (element
);
1716 parent
.AppendChild (n
);
1717 n
.SetAttribute (attribute
, value);
1721 private static XmlNode
CopyNode (XmlNode source
, XmlNode dest
)
1723 XmlNode copy
= dest
.OwnerDocument
.ImportNode (source
, true);
1724 dest
.AppendChild (copy
);
1728 private static void WriteElementInitialText(XmlElement parent
, string element
, string value) {
1729 XmlElement node
= (XmlElement
)parent
.SelectSingleNode(element
);
1732 node
= WriteElement(parent
, element
);
1733 node
.InnerText
= value;
1735 private static void WriteElementAttribute(XmlElement parent
, string element
, string attribute
, string value) {
1736 XmlElement node
= WriteElement(parent
, element
);
1737 if (node
.GetAttribute(attribute
) == value) return;
1738 node
.SetAttribute(attribute
, value);
1740 private static void ClearElement(XmlElement parent
, string name
) {
1741 XmlElement node
= (XmlElement
)parent
.SelectSingleNode(name
);
1743 parent
.RemoveChild(node
);
1746 // DOCUMENTATION HELPER FUNCTIONS
1748 private void MakeDocNode (DocsNodeInfo info
)
1750 List
<GenericParameter
> genericParams
= info
.GenericParameters
;
1751 ParameterDefinitionCollection parameters
= info
.Parameters
;
1752 TypeReference returntype
= info
.ReturnType
;
1753 bool returnisreturn
= info
.ReturnIsReturn
;
1754 XmlElement e
= info
.Node
;
1755 bool addremarks
= info
.AddRemarks
;
1757 WriteElementInitialText(e
, "summary", "To be added.");
1759 if (parameters
!= null) {
1760 string[] values
= new string [parameters
.Count
];
1761 for (int i
= 0; i
< values
.Length
; ++i
)
1762 values
[i
] = parameters
[i
].Name
;
1763 UpdateParameters (e
, "param", values
);
1766 if (genericParams
!= null) {
1767 string[] values
= new string [genericParams
.Count
];
1768 for (int i
= 0; i
< values
.Length
; ++i
)
1769 values
[i
] = genericParams
[i
].Name
;
1770 UpdateParameters (e
, "typeparam", values
);
1773 string retnodename
= null;
1774 if (returntype
!= null && returntype
.FullName
!= "System.Void") { // FIXME
1775 retnodename
= returnisreturn
? "returns" : "value";
1776 string retnodename_other
= !returnisreturn
? "returns" : "value";
1778 // If it has a returns node instead of a value node, change its name.
1779 XmlElement retother
= (XmlElement
)e
.SelectSingleNode(retnodename_other
);
1780 if (retother
!= null) {
1781 XmlElement retnode
= e
.OwnerDocument
.CreateElement(retnodename
);
1782 foreach (XmlNode node
in retother
)
1783 retnode
.AppendChild(node
.CloneNode(true));
1784 e
.ReplaceChild(retnode
, retother
);
1786 WriteElementInitialText(e
, retnodename
, "To be added.");
1789 ClearElement(e
, "returns");
1790 ClearElement(e
, "value");
1794 WriteElementInitialText(e
, "remarks", "To be added.");
1796 if (exceptions
.HasValue
&& info
.Member
!= null &&
1797 (exceptions
.Value
& ExceptionLocations
.AddedMembers
) == 0) {
1798 UpdateExceptions (e
, info
.Member
);
1801 if (info
.EcmaDocs
!= null) {
1802 XmlReader r
= info
.EcmaDocs
;
1803 int depth
= r
.Depth
;
1804 r
.ReadStartElement ("Docs");
1806 if (r
.Name
== "Docs") {
1807 if (r
.Depth
== depth
&& r
.NodeType
== XmlNodeType
.EndElement
)
1810 throw new InvalidOperationException ("Skipped past current <Docs/> element!");
1812 if (!r
.IsStartElement ())
1817 string name
= r
.GetAttribute ("name");
1820 XmlNode doc
= e
.SelectSingleNode (
1821 r
.Name
+ "[@name='" + name
+ "']");
1822 string value = r
.ReadInnerXml ();
1824 doc
.InnerXml
= value.Replace ("\r", "");
1831 string name
= r
.Name
;
1832 string cref
= r
.GetAttribute ("cref");
1835 XmlNode doc
= e
.SelectSingleNode (
1836 r
.Name
+ "[@cref='" + cref
+ "']");
1837 string value = r
.ReadInnerXml ().Replace ("\r", "");
1839 doc
.InnerXml
= value;
1841 XmlElement n
= e
.OwnerDocument
.CreateElement (name
);
1842 n
.SetAttribute ("cref", cref
);
1849 string name
= r
.Name
;
1850 string xpath
= r
.Name
;
1851 StringList attributes
= new StringList (r
.AttributeCount
);
1852 if (r
.MoveToFirstAttribute ()) {
1854 attributes
.Add ("@" + r
.Name
+ "=\"" + r
.Value
+ "\"");
1855 } while (r
.MoveToNextAttribute ());
1858 if (attributes
.Count
> 0) {
1859 xpath
+= "[" + string.Join (" and ", attributes
.ToArray ()) + "]";
1861 XmlNode doc
= e
.SelectSingleNode (xpath
);
1862 string value = r
.ReadInnerXml ().Replace ("\r", "");
1864 doc
.InnerXml
= value;
1867 XmlElement n
= e
.OwnerDocument
.CreateElement (name
);
1869 foreach (string a
in attributes
) {
1870 int eq
= a
.IndexOf ('=');
1871 n
.SetAttribute (a
.Substring (1, eq
-1), a
.Substring (eq
+2, a
.Length
-eq
-3));
1880 if (info
.SlashDocs
!= null) {
1881 XmlNode elem
= info
.SlashDocs
;
1883 if (elem
.SelectSingleNode("summary") != null)
1884 ClearElement(e
, "summary");
1885 if (elem
.SelectSingleNode("remarks") != null)
1886 ClearElement(e
, "remarks");
1887 if (elem
.SelectSingleNode("value") != null)
1888 ClearElement(e
, "value");
1889 if (retnodename
!= null && elem
.SelectSingleNode(retnodename
) != null)
1890 ClearElement(e
, retnodename
);
1892 foreach (XmlNode child
in elem
.ChildNodes
) {
1893 switch (child
.Name
) {
1896 XmlAttribute name
= child
.Attributes
["name"];
1899 XmlElement p2
= (XmlElement
) e
.SelectSingleNode (child
.Name
+ "[@name='" + name
.Value
+ "']");
1901 p2
.InnerXml
= child
.InnerXml
;
1906 case "permission": {
1907 XmlAttribute cref
= child
.Attributes
["cref"] ?? child
.Attributes
["name"];
1910 XmlElement a
= (XmlElement
) e
.SelectSingleNode (child
.Name
+ "[@cref='" + cref
.Value
+ "']");
1912 a
= e
.OwnerDocument
.CreateElement (child
.Name
);
1913 a
.SetAttribute ("cref", child
.Attributes
["cref"].Value
);
1916 a
.InnerXml
= child
.InnerXml
;
1920 XmlAttribute cref
= child
.Attributes
["cref"];
1923 XmlElement a
= (XmlElement
) e
.SelectSingleNode ("altmember[@cref='" + cref
.Value
+ "']");
1925 a
= e
.OwnerDocument
.CreateElement ("altmember");
1926 a
.SetAttribute ("cref", child
.Attributes
["cref"].Value
);
1932 CopyNode (child
, e
);
1939 OrderDocsNodes (e
, e
.ChildNodes
);
1940 NormalizeWhitespace(e
);
1943 static readonly string[] DocsNodeOrder
= {
1944 "typeparam", "param", "summary", "returns", "value", "remarks",
1947 private static void OrderDocsNodes (XmlNode docs
, XmlNodeList children
)
1949 MyXmlNodeList newChildren
= new MyXmlNodeList (children
.Count
);
1950 for (int i
= 0; i
< DocsNodeOrder
.Length
; ++i
) {
1951 for (int j
= 0; j
< children
.Count
; ++j
) {
1952 XmlNode c
= children
[j
];
1953 if (c
.Name
== DocsNodeOrder
[i
]) {
1954 newChildren
.Add (c
);
1958 if (newChildren
.Count
>= 0)
1959 docs
.PrependChild ((XmlNode
) newChildren
[0]);
1960 for (int i
= 1; i
< newChildren
.Count
; ++i
) {
1961 XmlNode prev
= (XmlNode
) newChildren
[i
-1];
1962 XmlNode cur
= (XmlNode
) newChildren
[i
];
1963 docs
.RemoveChild (cur
);
1964 docs
.InsertAfter (cur
, prev
);
1969 private void UpdateParameters (XmlElement e
, string element
, string[] values
)
1971 if (values
!= null) {
1972 XmlNode
[] paramnodes
= new XmlNode
[values
.Length
];
1974 // Some documentation had param nodes with leading spaces.
1975 foreach (XmlElement paramnode
in e
.SelectNodes(element
)){
1976 paramnode
.SetAttribute("name", paramnode
.GetAttribute("name").Trim());
1979 // If a member has only one parameter, we can track changes to
1980 // the name of the parameter easily.
1981 if (values
.Length
== 1 && e
.SelectNodes(element
).Count
== 1) {
1982 UpdateParameterName (e
, (XmlElement
) e
.SelectSingleNode(element
), values
[0]);
1985 bool reinsert
= false;
1987 // Pick out existing and still-valid param nodes, and
1988 // create nodes for parameters not in the file.
1989 Hashtable seenParams
= new Hashtable();
1990 for (int pi
= 0; pi
< values
.Length
; pi
++) {
1991 string p
= values
[pi
];
1994 paramnodes
[pi
] = e
.SelectSingleNode(element
+ "[@name='" + p
+ "']");
1995 if (paramnodes
[pi
] != null) continue;
1997 XmlElement pe
= e
.OwnerDocument
.CreateElement(element
);
1998 pe
.SetAttribute("name", p
);
1999 pe
.InnerText
= "To be added.";
2000 paramnodes
[pi
] = pe
;
2004 // Remove parameters that no longer exist and check all params are in the right order.
2006 MyXmlNodeList todelete
= new MyXmlNodeList ();
2007 foreach (XmlElement paramnode
in e
.SelectNodes(element
)) {
2008 string name
= paramnode
.GetAttribute("name");
2009 if (!seenParams
.ContainsKey(name
)) {
2010 if (!delete
&& !paramnode
.InnerText
.StartsWith("To be added")) {
2011 Warning ("The following param node can only be deleted if the --delete option is given: ");
2012 if (e
.ParentNode
== e
.OwnerDocument
.DocumentElement
) {
2014 Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
2015 e
.OwnerDocument
.DocumentElement
.GetAttribute ("FullName"),
2019 Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
2020 e
.OwnerDocument
.DocumentElement
.GetAttribute ("FullName"),
2021 e
.ParentNode
.Attributes
["MemberName"].Value
,
2024 Warning ("\tValue={0}", paramnode
.OuterXml
);
2026 todelete
.Add (paramnode
);
2031 if ((int)seenParams
[name
] != idx
)
2037 foreach (XmlNode n
in todelete
) {
2038 n
.ParentNode
.RemoveChild (n
);
2041 // Re-insert the parameter nodes at the top of the doc section.
2043 for (int pi
= values
.Length
-1; pi
>= 0; pi
--)
2044 e
.PrependChild(paramnodes
[pi
]);
2046 // Clear all existing param nodes
2047 foreach (XmlNode paramnode
in e
.SelectNodes(element
)) {
2048 if (!delete
&& !paramnode
.InnerText
.StartsWith("To be added")) {
2049 Console
.WriteLine("The following param node can only be deleted if the --delete option is given:");
2050 Console
.WriteLine(paramnode
.OuterXml
);
2052 paramnode
.ParentNode
.RemoveChild(paramnode
);
2058 private static void UpdateParameterName (XmlElement docs
, XmlElement pe
, string newName
)
2060 string existingName
= pe
.GetAttribute ("name");
2061 pe
.SetAttribute ("name", newName
);
2062 if (existingName
== newName
)
2064 foreach (XmlElement paramref
in docs
.SelectNodes (".//paramref"))
2065 if (paramref
.GetAttribute ("name").Trim () == existingName
)
2066 paramref
.SetAttribute ("name", newName
);
2069 class CrefComparer
: XmlNodeComparer
{
2071 public CrefComparer ()
2075 public override int Compare (XmlNode x
, XmlNode y
)
2077 string xType
= x
.Attributes
["cref"].Value
;
2078 string yType
= y
.Attributes
["cref"].Value
;
2079 string xNamespace
= GetNamespace (xType
);
2080 string yNamespace
= GetNamespace (yType
);
2082 int c
= xNamespace
.CompareTo (yNamespace
);
2085 return xType
.CompareTo (yType
);
2088 static string GetNamespace (string type
)
2090 int n
= type
.LastIndexOf ('.');
2092 return type
.Substring (0, n
);
2093 return string.Empty
;
2097 private void UpdateExceptions (XmlNode docs
, IMemberReference member
)
2099 foreach (var source
in new ExceptionLookup (exceptions
.Value
)[member
]) {
2100 string cref
= slashdocFormatter
.GetDeclaration (source
.Exception
);
2101 var node
= docs
.SelectSingleNode ("exception[@cref='" + cref
+ "']");
2104 XmlElement e
= docs
.OwnerDocument
.CreateElement ("exception");
2105 e
.SetAttribute ("cref", cref
);
2106 e
.InnerXml
= "To be added; from: <see cref=\"" +
2107 string.Join ("\" />, <see cref=\"",
2108 source
.Sources
.Select (m
=> slashdocFormatter
.GetDeclaration (m
))
2111 docs
.AppendChild (e
);
2113 SortXmlNodes (docs
, docs
.SelectNodes ("exception"),
2114 new CrefComparer ());
2117 private static void NormalizeWhitespace(XmlElement e
) {
2118 // Remove all text and whitespace nodes from the element so it
2119 // is outputted with nice indentation and no blank lines.
2120 ArrayList deleteNodes
= new ArrayList();
2121 foreach (XmlNode n
in e
)
2122 if (n
is XmlText
|| n
is XmlWhitespace
|| n
is XmlSignificantWhitespace
)
2124 foreach (XmlNode n
in deleteNodes
)
2125 n
.ParentNode
.RemoveChild(n
);
2128 private static bool UpdateAssemblyVersions (XmlElement root
, IMemberReference member
, bool add)
2130 TypeDefinition type
= member
as TypeDefinition
;
2132 type
= member
.DeclaringType
as TypeDefinition
;
2133 return UpdateAssemblyVersions(root
, new string[]{ GetAssemblyVersion (type.Module.Assembly) }
, add);
2136 private static string GetAssemblyVersion (AssemblyDefinition assembly
)
2138 return assembly
.Name
.Version
.ToString();
2141 private static bool UpdateAssemblyVersions(XmlElement root
, string[] assemblyVersions
, bool add)
2143 XmlElement e
= (XmlElement
) root
.SelectSingleNode ("AssemblyInfo");
2145 e
= root
.OwnerDocument
.CreateElement("AssemblyInfo");
2146 root
.AppendChild(e
);
2148 List
<XmlNode
> matches
= e
.SelectNodes ("AssemblyVersion").Cast
<XmlNode
>()
2149 .Where(v
=> Array
.IndexOf (assemblyVersions
, v
.InnerText
) >= 0)
2151 // matches.Count > 0 && add: ignore -- already present
2152 if (matches
.Count
> 0 && !add) {
2153 foreach (XmlNode c
in matches
)
2156 else if (matches
.Count
== 0 && add) {
2157 foreach (string sv
in assemblyVersions
) {
2158 XmlElement c
= root
.OwnerDocument
.CreateElement("AssemblyVersion");
2163 // matches.Count == 0 && !add: ignore -- already not present
2165 XmlNodeList avs
= e
.SelectNodes ("AssemblyVersion");
2166 SortXmlNodes (e
, avs
, new VersionComparer ());
2168 return avs
.Count
!= 0;
2171 // FIXME: get TypeReferences instead of string comparison?
2172 private static string[] IgnorableAttributes
= {
2173 // Security related attributes
2174 "System.Reflection.AssemblyKeyFileAttribute",
2175 "System.Reflection.AssemblyDelaySignAttribute",
2176 // Present in @RefType
2177 "System.Runtime.InteropServices.OutAttribute",
2178 // For naming the indexer to use when not using indexers
2179 "System.Reflection.DefaultMemberAttribute",
2180 // for decimal constants
2181 "System.Runtime.CompilerServices.DecimalConstantAttribute",
2182 // compiler generated code
2183 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
2184 // more compiler generated code, e.g. iterator methods
2185 "System.Diagnostics.DebuggerHiddenAttribute",
2186 "System.Runtime.CompilerServices.FixedBufferAttribute",
2187 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
2188 // extension methods
2189 "System.Runtime.CompilerServices.ExtensionAttribute",
2193 enum AttributeFlags
{
2195 KeepExistingAttributes
= 0x1,
2198 private void MakeAttributes (XmlElement root
, CustomAttributeCollection attributes
, AttributeFlags flags
)
2200 MakeAttributes (root
, attributes
, flags
, null);
2203 private void MakeAttributes (XmlElement root
, CustomAttributeCollection attributes
, AttributeFlags flags
, string prefix
)
2205 bool keepExisting
= (flags
& AttributeFlags
.KeepExistingAttributes
) != 0;
2206 if (attributes
.Count
== 0) {
2208 ClearElement(root
, "Attributes");
2213 XmlElement e
= (XmlElement
)root
.SelectSingleNode("Attributes");
2214 if (e
!= null && !keepExisting
)
2217 e
= root
.OwnerDocument
.CreateElement("Attributes");
2219 foreach (CustomAttribute attribute
in attributes
.Cast
<CustomAttribute
> ()
2220 .OrderBy (ca
=> ca
.Constructor
.DeclaringType
.FullName
)) {
2221 if (!attribute
.Resolve ()) {
2223 Warning ("warning: could not resolve type {0}.",
2224 attribute
.Constructor
.DeclaringType
.FullName
);
2226 TypeDefinition attrType
= attribute
.Constructor
.DeclaringType
as TypeDefinition
;
2227 if (attrType
!= null && !IsPublic (attrType
))
2229 if (slashdocFormatter
.GetName (attribute
.Constructor
.DeclaringType
) == null)
2232 if (Array
.IndexOf (IgnorableAttributes
, attribute
.Constructor
.DeclaringType
.FullName
) >= 0)
2237 StringList fields
= new StringList ();
2239 ParameterDefinitionCollection parameters
= attribute
.Constructor
.Parameters
;
2240 for (int i
= 0; i
< attribute
.ConstructorParameters
.Count
; ++i
) {
2241 fields
.Add (MakeAttributesValueString (
2242 attribute
.ConstructorParameters
[i
],
2243 parameters
[i
].ParameterType
));
2246 (from de
in attribute
.Fields
.Cast
<DictionaryEntry
> ()
2247 select new { Type=attribute.GetFieldType (de.Key.ToString ()), Name=de.Key, Value=de.Value }
)
2249 (from de
in attribute
.Properties
.Cast
<DictionaryEntry
> ()
2250 select new { Type=attribute.GetPropertyType (de.Key.ToString ()), Name=de.Key, Value=de.Value }
))
2251 .OrderBy (v
=> v
.Name
);
2252 foreach (var d
in namedArgs
)
2253 fields
.Add (string.Format ("{0}={1}", d
.Name
,
2254 MakeAttributesValueString (d
.Value
, d
.Type
)));
2256 string a2
= String
.Join(", ", fields
.ToArray ());
2257 if (a2
!= "") a2
= "(" + a2
+ ")";
2259 XmlElement ae
= root
.OwnerDocument
.CreateElement("Attribute");
2262 string name
= attribute
.Constructor
.DeclaringType
.FullName
;
2263 if (name
.EndsWith("Attribute")) name
= name
.Substring(0, name
.Length
-"Attribute".Length
);
2264 WriteElementText(ae
, "AttributeName", prefix
+ name
+ a2
);
2267 if (b
&& e
.ParentNode
== null)
2268 root
.AppendChild(e
);
2270 ClearElement(root
, "Attributes");
2272 NormalizeWhitespace(e
);
2275 private static string MakeAttributesValueString (object v
, TypeReference valueType
)
2279 if (valueType
.FullName
== "System.Type")
2280 return "typeof(" + v
.ToString () + ")";
2281 if (valueType
.FullName
== "System.String")
2282 return "\"" + v
.ToString () + "\"";
2284 return (bool)v
? "true" : "false";
2285 TypeDefinition valueDef
= valueType
.Resolve ();
2286 if (valueDef
== null || !valueDef
.IsEnum
)
2287 return v
.ToString ();
2288 string typename
= GetDocTypeFullName (valueType
);
2289 var values
= GetEnumerationValues (valueDef
);
2290 long c
= ToInt64 (v
);
2291 if (values
.ContainsKey (c
))
2292 return typename
+ "." + values
[c
];
2293 if (valueDef
.CustomAttributes
.Cast
<CustomAttribute
> ()
2294 .Any (ca
=> ca
.Constructor
.DeclaringType
.FullName
== "System.FlagsAttribute")) {
2295 return string.Join (" | ",
2296 (from i
in values
.Keys
2298 select typename
+ "." + values
[i
])
2301 return "(" + GetDocTypeFullName (valueType
) + ") " + v
.ToString ();
2304 private static Dictionary
<long, string> GetEnumerationValues (TypeDefinition type
)
2306 var values
= new Dictionary
<long, string> ();
2308 (from f
in type
.Fields
.Cast
<FieldDefinition
> ()
2309 where
!(f
.IsRuntimeSpecialName
|| f
.IsSpecialName
)
2311 values
[ToInt64 (f
.Constant
)] = f
.Name
;
2316 static long ToInt64 (object value)
2319 return (long) (ulong) value;
2320 return Convert
.ToInt64 (value);
2323 private void MakeParameters (XmlElement root
, ParameterDefinitionCollection parameters
)
2325 XmlElement e
= WriteElement(root
, "Parameters");
2327 foreach (ParameterDefinition p
in parameters
) {
2328 XmlElement pe
= root
.OwnerDocument
.CreateElement("Parameter");
2330 pe
.SetAttribute("Name", p
.Name
);
2331 pe
.SetAttribute("Type", GetDocParameterType (p
.ParameterType
));
2332 if (p
.ParameterType
is ReferenceType
) {
2333 if (p
.IsOut
) pe
.SetAttribute("RefType", "out");
2334 else pe
.SetAttribute("RefType", "ref");
2336 MakeAttributes (pe
, p
.CustomAttributes
, 0);
2340 private void MakeTypeParameters (XmlElement root
, GenericParameterCollection typeParams
)
2342 if (typeParams
== null || typeParams
.Count
== 0) {
2343 XmlElement f
= (XmlElement
) root
.SelectSingleNode ("TypeParameters");
2345 root
.RemoveChild (f
);
2348 XmlElement e
= WriteElement(root
, "TypeParameters");
2350 foreach (GenericParameter t
in typeParams
) {
2351 XmlElement pe
= root
.OwnerDocument
.CreateElement("TypeParameter");
2353 pe
.SetAttribute("Name", t
.Name
);
2354 MakeAttributes (pe
, t
.CustomAttributes
, 0);
2355 XmlElement ce
= (XmlElement
) e
.SelectSingleNode ("Constraints");
2356 ConstraintCollection constraints
= t
.Constraints
;
2357 GenericParameterAttributes attrs
= t
.Attributes
;
2358 if (attrs
== GenericParameterAttributes
.NonVariant
&& constraints
.Count
== 0) {
2366 ce
= root
.OwnerDocument
.CreateElement ("Constraints");
2368 pe
.AppendChild (ce
);
2369 if ((attrs
& GenericParameterAttributes
.Contravariant
) != 0)
2370 AppendElementText (ce
, "ParameterAttribute", "Contravariant");
2371 if ((attrs
& GenericParameterAttributes
.Covariant
) != 0)
2372 AppendElementText (ce
, "ParameterAttribute", "Covariant");
2373 if ((attrs
& GenericParameterAttributes
.DefaultConstructorConstraint
) != 0)
2374 AppendElementText (ce
, "ParameterAttribute", "DefaultConstructorConstraint");
2375 if ((attrs
& GenericParameterAttributes
.NotNullableValueTypeConstraint
) != 0)
2376 AppendElementText (ce
, "ParameterAttribute", "NotNullableValueTypeConstraint");
2377 if ((attrs
& GenericParameterAttributes
.ReferenceTypeConstraint
) != 0)
2378 AppendElementText (ce
, "ParameterAttribute", "ReferenceTypeConstraint");
2379 foreach (TypeReference c
in constraints
) {
2380 TypeDefinition cd
= c
.Resolve ();
2381 AppendElementText (ce
,
2382 (cd
!= null && cd
.IsInterface
) ? "InterfaceName" : "BaseTypeName",
2383 GetDocTypeFullName (c
));
2388 private void MakeParameters (XmlElement root
, IMemberReference mi
)
2390 if (mi
is MethodDefinition
&& ((MethodDefinition
) mi
).IsConstructor
)
2391 MakeParameters (root
, ((MethodDefinition
)mi
).Parameters
);
2392 else if (mi
is MethodDefinition
) {
2393 MethodDefinition mb
= (MethodDefinition
) mi
;
2394 ParameterDefinitionCollection parameters
= mb
.Parameters
;
2395 MakeParameters(root
, parameters
);
2396 if (parameters
.Count
> 0 && DocUtils
.IsExtensionMethod (mb
)) {
2397 XmlElement p
= (XmlElement
) root
.SelectSingleNode ("Parameters/Parameter[position()=1]");
2398 p
.SetAttribute ("RefType", "this");
2401 else if (mi
is PropertyDefinition
) {
2402 ParameterDefinitionCollection parameters
= ((PropertyDefinition
)mi
).Parameters
;
2403 if (parameters
.Count
> 0)
2404 MakeParameters(root
, parameters
);
2408 else if (mi
is FieldDefinition
) return;
2409 else if (mi
is EventDefinition
) return;
2410 else throw new ArgumentException();
2413 private static string GetDocParameterType (TypeReference type
)
2415 return GetDocTypeFullName (type
).Replace ("@", "&");
2418 private void MakeReturnValue (XmlElement root
, TypeReference type
, CustomAttributeCollection attributes
)
2420 XmlElement e
= WriteElement(root
, "ReturnValue");
2422 WriteElementText(e
, "ReturnType", GetDocTypeFullName (type
));
2423 if (attributes
!= null)
2424 MakeAttributes(e
, attributes
, 0);
2427 private void MakeReturnValue (XmlElement root
, IMemberReference mi
)
2429 if (mi
is MethodDefinition
&& ((MethodDefinition
) mi
).IsConstructor
)
2431 else if (mi
is MethodDefinition
)
2432 MakeReturnValue (root
, ((MethodDefinition
)mi
).ReturnType
.ReturnType
, ((MethodDefinition
)mi
).ReturnType
.CustomAttributes
);
2433 else if (mi
is PropertyDefinition
)
2434 MakeReturnValue (root
, ((PropertyDefinition
)mi
).PropertyType
, null);
2435 else if (mi
is FieldDefinition
)
2436 MakeReturnValue (root
, ((FieldDefinition
)mi
).FieldType
, null);
2437 else if (mi
is EventDefinition
)
2438 MakeReturnValue (root
, ((EventDefinition
)mi
).EventType
, null);
2440 throw new ArgumentException(mi
+ " is a " + mi
.GetType().FullName
);
2443 private XmlElement
MakeMember(XmlDocument doc
, DocsNodeInfo info
)
2445 IMemberReference mi
= info
.Member
;
2446 if (mi
is TypeDefinition
) return null;
2448 string sigs
= MakeMemberSignature(mi
);
2449 if (sigs
== null) return null; // not publicly visible
2451 // no documentation for property/event accessors. Is there a better way of doing this?
2452 if (mi
.Name
.StartsWith("get_")) return null;
2453 if (mi
.Name
.StartsWith("set_")) return null;
2454 if (mi
.Name
.StartsWith("add_")) return null;
2455 if (mi
.Name
.StartsWith("remove_")) return null;
2456 if (mi
.Name
.StartsWith("raise_")) return null;
2458 XmlElement me
= doc
.CreateElement("Member");
2459 me
.SetAttribute("MemberName", GetMemberName (mi
));
2463 if (exceptions
.HasValue
&&
2464 (exceptions
.Value
& ExceptionLocations
.AddedMembers
) != 0)
2465 UpdateExceptions (info
.Node
, info
.Member
);
2467 if (since
!= null) {
2468 XmlNode docs
= me
.SelectSingleNode("Docs");
2469 docs
.AppendChild (CreateSinceNode (doc
));
2475 private static string GetMemberName (IMemberReference mi
)
2477 MethodDefinition mb
= mi
as MethodDefinition
;
2479 PropertyDefinition pi
= mi
as PropertyDefinition
;
2482 return DocUtils
.GetPropertyName (pi
);
2484 StringBuilder sb
= new StringBuilder (mi
.Name
.Length
);
2485 if (!DocUtils
.IsExplicitlyImplemented (mb
))
2486 sb
.Append (mi
.Name
);
2488 TypeReference iface
;
2489 MethodReference ifaceMethod
;
2490 DocUtils
.GetInfoForExplicitlyImplementedMethod (mb
, out iface
, out ifaceMethod
);
2491 sb
.Append (GetDocTypeFullName (iface
));
2493 sb
.Append (ifaceMethod
.Name
);
2495 if (mb
.IsGenericMethod ()) {
2496 GenericParameterCollection typeParams
= mb
.GenericParameters
;
2497 if (typeParams
.Count
> 0) {
2499 sb
.Append (typeParams
[0].Name
);
2500 for (int i
= 1; i
< typeParams
.Count
; ++i
)
2501 sb
.Append (",").Append (typeParams
[i
].Name
);
2505 return sb
.ToString ();
2508 private static int CountChars (string s
, char c
)
2511 for (int i
= 0; i
< s
.Length
; ++i
) {
2518 /// SIGNATURE GENERATION FUNCTIONS
2520 static string MakeTypeSignature (TypeReference type
)
2522 return csharpFormatter
.GetDeclaration (type
);
2525 static string MakeMemberSignature (IMemberReference mi
)
2527 return csharpFullFormatter
.GetDeclaration (mi
);
2530 static string GetMemberType (IMemberReference mi
)
2532 if (mi
is MethodDefinition
&& ((MethodDefinition
) mi
).IsConstructor
)
2533 return "Constructor";
2534 if (mi
is MethodDefinition
)
2536 if (mi
is PropertyDefinition
)
2538 if (mi
is FieldDefinition
)
2540 if (mi
is EventDefinition
)
2542 throw new ArgumentException();
2545 private static string GetDocTypeName (TypeReference type
)
2547 return docTypeFormatter
.GetName (type
);
2550 private static string GetDocTypeFullName (TypeReference type
)
2552 return DocTypeFullMemberFormatter
.Default
.GetName (type
);
2555 class DocsNodeInfo
{
2556 public DocsNodeInfo (XmlElement node
)
2561 public DocsNodeInfo (XmlElement node
, TypeDefinition type
)
2567 public DocsNodeInfo (XmlElement node
, IMemberReference member
)
2570 SetMemberInfo (member
);
2573 void SetType (TypeDefinition type
)
2576 throw new ArgumentNullException ("type");
2577 GenericParameters
= new List
<GenericParameter
> (type
.GenericParameters
.Cast
<GenericParameter
> ());
2578 List
<TypeReference
> declTypes
= DocUtils
.GetDeclaringTypes (type
);
2579 int maxGenArgs
= DocUtils
.GetGenericArgumentCount (type
);
2580 for (int i
= 0; i
< declTypes
.Count
- 1; ++i
) {
2581 int remove = System
.Math
.Min (maxGenArgs
,
2582 DocUtils
.GetGenericArgumentCount (declTypes
[i
]));
2583 maxGenArgs
-= remove;
2584 while (remove-- > 0)
2585 GenericParameters
.RemoveAt (0);
2587 if (DocUtils
.IsDelegate (type
)) {
2588 Parameters
= type
.GetMethod("Invoke").Parameters
;
2589 ReturnType
= type
.GetMethod("Invoke").ReturnType
.ReturnType
;
2591 SetSlashDocs (type
);
2594 void SetMemberInfo (IMemberReference member
)
2597 throw new ArgumentNullException ("member");
2598 ReturnIsReturn
= true;
2602 if (member
is MethodReference
) {
2603 MethodReference mr
= (MethodReference
) member
;
2604 Parameters
= mr
.Parameters
;
2605 if (mr
.IsGenericMethod ()) {
2606 GenericParameters
= new List
<GenericParameter
> (mr
.GenericParameters
.Cast
<GenericParameter
> ());
2609 else if (member
is PropertyDefinition
) {
2610 Parameters
= ((PropertyDefinition
) member
).Parameters
;
2613 if (member
is MethodDefinition
) {
2614 ReturnType
= ((MethodDefinition
) member
).ReturnType
.ReturnType
;
2615 } else if (member
is PropertyDefinition
) {
2616 ReturnType
= ((PropertyDefinition
) member
).PropertyType
;
2617 ReturnIsReturn
= false;
2620 // no remarks section for enum members
2621 if (member
.DeclaringType
!= null && ((TypeDefinition
) member
.DeclaringType
).IsEnum
)
2623 SetSlashDocs (member
);
2626 private void SetSlashDocs (IMemberReference member
)
2628 if (slashdocs
== null)
2631 string slashdocsig
= slashdocFormatter
.GetDeclaration (member
);
2632 if (slashdocsig
!= null)
2633 SlashDocs
= slashdocs
.SelectSingleNode ("doc/members/member[@name='" + slashdocsig
+ "']");
2636 public TypeReference ReturnType
;
2637 public List
<GenericParameter
> GenericParameters
;
2638 public ParameterDefinitionCollection Parameters
;
2639 public bool ReturnIsReturn
;
2640 public XmlElement Node
;
2641 public bool AddRemarks
= true;
2642 public XmlNode SlashDocs
;
2643 public XmlReader EcmaDocs
;
2644 public IMemberReference Member
;
2647 static string GetXPathForMember (DocumentationMember member
)
2649 StringBuilder xpath
= new StringBuilder ();
2650 xpath
.Append ("//Members/Member[@MemberName=\"")
2651 .Append (member
.MemberName
)
2653 if (member
.Parameters
!= null && member
.Parameters
.Count
> 0) {
2654 xpath
.Append ("/Parameters[count(Parameter) = ")
2655 .Append (member
.Parameters
.Count
);
2656 for (int i
= 0; i
< member
.Parameters
.Count
; ++i
) {
2657 xpath
.Append (" and Parameter [").Append (i
+1).Append ("]/@Type=\"");
2658 xpath
.Append (member
.Parameters
[i
]);
2659 xpath
.Append ("\"");
2661 xpath
.Append ("]/..");
2663 return xpath
.ToString ();
2666 public static string GetXPathForMember (XPathNavigator member
)
2668 StringBuilder xpath
= new StringBuilder ();
2669 xpath
.Append ("//Type[@FullName=\"")
2670 .Append (member
.SelectSingleNode ("../../@FullName").Value
)
2672 xpath
.Append ("Members/Member[@MemberName=\"")
2673 .Append (member
.SelectSingleNode ("@MemberName").Value
)
2675 XPathNodeIterator parameters
= member
.Select ("Parameters/Parameter");
2676 if (parameters
.Count
> 0) {
2677 xpath
.Append ("/Parameters[count(Parameter) = ")
2678 .Append (parameters
.Count
);
2680 while (parameters
.MoveNext ()) {
2682 xpath
.Append (" and Parameter [").Append (i
).Append ("]/@Type=\"");
2683 xpath
.Append (parameters
.Current
.Value
);
2684 xpath
.Append ("\"");
2686 xpath
.Append ("]/..");
2688 return xpath
.ToString ();
2691 public static string GetXPathForMember (IMemberReference member
)
2693 StringBuilder xpath
= new StringBuilder ();
2694 xpath
.Append ("//Type[@FullName=\"")
2695 .Append (member
.DeclaringType
.FullName
)
2697 xpath
.Append ("Members/Member[@MemberName=\"")
2698 .Append (GetMemberName (member
))
2701 ParameterDefinitionCollection parameters
= null;
2702 if (member
is MethodDefinition
)
2703 parameters
= ((MethodDefinition
) member
).Parameters
;
2704 else if (member
is PropertyDefinition
) {
2705 parameters
= ((PropertyDefinition
) member
).Parameters
;
2707 if (parameters
!= null && parameters
.Count
> 0) {
2708 xpath
.Append ("/Parameters[count(Parameter) = ")
2709 .Append (parameters
.Count
);
2710 for (int i
= 0; i
< parameters
.Count
; ++i
) {
2711 xpath
.Append (" and Parameter [").Append (i
+1).Append ("]/@Type=\"");
2712 xpath
.Append (GetDocParameterType (parameters
[i
].ParameterType
));
2713 xpath
.Append ("\"");
2715 xpath
.Append ("]/..");
2717 return xpath
.ToString ();
2721 static class CecilExtensions
{
2722 public static IEnumerable
<IMemberReference
> GetMembers (this TypeDefinition type
)
2724 foreach (var c
in type
.Constructors
)
2725 yield return (IMemberReference
) c
;
2726 foreach (var e
in type
.Events
)
2727 yield return (IMemberReference
) e
;
2728 foreach (var f
in type
.Fields
)
2729 yield return (IMemberReference
) f
;
2730 foreach (var m
in type
.Methods
)
2731 yield return (IMemberReference
) m
;
2732 foreach (var t
in type
.NestedTypes
)
2733 yield return (IMemberReference
) t
;
2734 foreach (var p
in type
.Properties
)
2735 yield return (IMemberReference
) p
;
2738 public static IEnumerable
<IMemberReference
> GetMembers (this TypeDefinition type
, string member
)
2740 return GetMembers (type
).Where (m
=> m
.Name
== member
);
2743 public static IMemberReference
GetMember (this TypeDefinition type
, string member
)
2745 return GetMembers (type
, member
).EnsureZeroOrOne ();
2748 static T EnsureZeroOrOne
<T
> (this IEnumerable
<T
> source
)
2750 if (source
.Count () > 1)
2751 throw new InvalidOperationException ("too many matches");
2752 return source
.FirstOrDefault ();
2755 public static MethodDefinition
GetMethod (this TypeDefinition type
, string method
)
2757 return type
.Methods
.Cast
<MethodDefinition
> ()
2758 .Where (m
=> m
.Name
== method
)
2759 .EnsureZeroOrOne ();
2762 public static IEnumerable
<IMemberReference
> GetDefaultMembers (this TypeReference type
)
2764 TypeDefinition def
= type
as TypeDefinition
;
2766 return new IMemberReference
[0];
2767 CustomAttribute defMemberAttr
= type
.CustomAttributes
.Cast
<CustomAttribute
> ()
2768 .Where (c
=> c
.Constructor
.DeclaringType
.FullName
== "System.Reflection.DefaultMemberAttribute")
2770 if (defMemberAttr
== null)
2771 return new IMemberReference
[0];
2772 string name
= (string) defMemberAttr
.ConstructorParameters
[0];
2773 return def
.Properties
.Cast
<PropertyDefinition
> ()
2774 .Where (p
=> p
.Name
== name
)
2775 .Select (p
=> (IMemberReference
) p
);
2778 public static IEnumerable
<TypeDefinition
> GetTypes (this AssemblyDefinition assembly
)
2780 return assembly
.Modules
.Cast
<ModuleDefinition
> ()
2781 .SelectMany (md
=> md
.Types
.Cast
<TypeDefinition
> ());
2784 public static TypeDefinition
GetType (this AssemblyDefinition assembly
, string type
)
2786 return GetTypes (assembly
)
2787 .Where (td
=> td
.FullName
== type
)
2788 .EnsureZeroOrOne ();
2791 public static bool IsGenericType (this TypeReference type
)
2793 return type
.GenericParameters
.Count
> 0;
2796 public static bool IsGenericMethod (this MethodReference method
)
2798 return method
.GenericParameters
.Count
> 0;
2801 public static IMemberReference
Resolve (this IMemberReference member
)
2803 EventReference er
= member
as EventReference
;
2805 return er
.Resolve ();
2806 FieldReference fr
= member
as FieldReference
;
2808 return fr
.Resolve ();
2809 MethodReference mr
= member
as MethodReference
;
2811 return mr
.Resolve ();
2812 PropertyReference pr
= member
as PropertyReference
;
2814 return pr
.Resolve ();
2815 TypeReference tr
= member
as TypeReference
;
2817 return tr
.Resolve ();
2818 throw new NotSupportedException ("Cannot find definition for " + member
.ToString ());
2822 static class DocUtils
{
2823 public static bool IsExplicitlyImplemented (MethodDefinition method
)
2825 return method
.IsPrivate
&& method
.IsFinal
&& method
.IsVirtual
;
2828 public static string GetTypeDotMember (string name
)
2830 int startType
, startMethod
;
2831 startType
= startMethod
= -1;
2832 for (int i
= 0; i
< name
.Length
; ++i
) {
2833 if (name
[i
] == '.') {
2834 startType
= startMethod
;
2838 return name
.Substring (startType
+1);
2841 public static string GetMember (string name
)
2843 int i
= name
.LastIndexOf ('.');
2846 return name
.Substring (i
+1);
2849 public static void GetInfoForExplicitlyImplementedMethod (
2850 MethodDefinition method
, out TypeReference iface
, out MethodReference ifaceMethod
)
2854 if (method
.Overrides
.Count
!= 1)
2855 throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method
.Name
);
2856 iface
= method
.Overrides
[0].DeclaringType
;
2857 ifaceMethod
= method
.Overrides
[0];
2860 public static string GetPropertyName (PropertyDefinition pi
)
2862 // Issue: (g)mcs-generated assemblies that explicitly implement
2863 // properties don't specify the full namespace, just the
2864 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2865 MethodDefinition method
= pi
.GetMethod
;
2867 method
= pi
.SetMethod
;
2868 if (!IsExplicitlyImplemented (method
))
2871 // Need to determine appropriate namespace for this member.
2872 TypeReference iface
;
2873 MethodReference ifaceMethod
;
2874 GetInfoForExplicitlyImplementedMethod (method
, out iface
, out ifaceMethod
);
2875 return string.Join (".", new string[]{
2876 DocTypeFullMemberFormatter
.Default
.GetName (iface
),
2877 GetMember (pi
.Name
)});
2880 public static string GetNamespace (TypeReference type
)
2882 if (type
.GetOriginalType ().IsNested
)
2883 type
= type
.GetOriginalType ();
2884 while (type
!= null && type
.IsNested
)
2885 type
= type
.DeclaringType
;
2887 return string.Empty
;
2888 return type
.Namespace
;
2891 public static string PathCombine (string dir
, string path
)
2897 return Path
.Combine (dir
, path
);
2900 public static bool IsExtensionMethod (MethodDefinition method
)
2903 method
.CustomAttributes
.Cast
<CustomAttribute
> ()
2904 .Where (m
=> m
.Constructor
.DeclaringType
.FullName
== "System.Runtime.CompilerServices.ExtensionAttribute")
2906 method
.DeclaringType
.CustomAttributes
.Cast
<CustomAttribute
> ()
2907 .Where (m
=> m
.Constructor
.DeclaringType
.FullName
== "System.Runtime.CompilerServices.ExtensionAttribute")
2911 public static bool IsDelegate (TypeDefinition type
)
2913 TypeReference baseRef
= type
.BaseType
;
2914 if (baseRef
== null)
2916 return !type
.IsAbstract
&& baseRef
.FullName
== "System.Delegate" || // FIXME
2917 baseRef
.FullName
== "System.MulticastDelegate";
2920 public static List
<TypeReference
> GetDeclaringTypes (TypeReference type
)
2922 List
<TypeReference
> decls
= new List
<TypeReference
> ();
2924 while (type
.DeclaringType
!= null) {
2925 decls
.Add (type
.DeclaringType
);
2926 type
= type
.DeclaringType
;
2932 public static int GetGenericArgumentCount (TypeReference type
)
2934 GenericInstanceType inst
= type
as GenericInstanceType
;
2936 ? inst
.GenericArguments
.Count
2937 : type
.GenericParameters
.Count
;
2940 public static IEnumerable
<TypeReference
> GetUserImplementedInterfaces (TypeDefinition type
)
2942 HashSet
<string> inheritedInterfaces
= GetInheritedInterfaces (type
);
2943 List
<TypeReference
> userInterfaces
= new List
<TypeReference
> ();
2944 foreach (TypeReference iface
in type
.Interfaces
) {
2945 TypeReference lookup
= iface
.Resolve () ?? iface
;
2946 if (!inheritedInterfaces
.Contains (GetQualifiedTypeName (lookup
)))
2947 userInterfaces
.Add (iface
);
2949 return userInterfaces
;
2952 private static string GetQualifiedTypeName (TypeReference type
)
2954 return "[" + type
.Scope
.Name
+ "]" + type
.FullName
;
2957 private static HashSet
<string> GetInheritedInterfaces (TypeDefinition type
)
2959 HashSet
<string> inheritedInterfaces
= new HashSet
<string> ();
2960 Action
<TypeDefinition
> a
= null;
2962 if (t
== null) return;
2963 foreach (TypeReference r
in t
.Interfaces
) {
2964 inheritedInterfaces
.Add (GetQualifiedTypeName (r
));
2968 TypeReference baseRef
= type
.BaseType
;
2969 while (baseRef
!= null) {
2970 TypeDefinition baseDef
= baseRef
.Resolve ();
2971 if (baseDef
!= null) {
2973 baseRef
= baseDef
.BaseType
;
2978 foreach (TypeReference r
in type
.Interfaces
)
2980 return inheritedInterfaces
;
2984 class DocumentationMember
{
2985 public StringToStringMap MemberSignatures
= new StringToStringMap ();
2986 public string ReturnType
;
2987 public StringList Parameters
;
2988 public string MemberName
;
2989 public string MemberType
;
2991 public DocumentationMember (XmlReader reader
)
2993 MemberName
= reader
.GetAttribute ("MemberName");
2994 int depth
= reader
.Depth
;
2996 StringList p
= new StringList ();
2998 if (reader
.NodeType
!= XmlNodeType
.Element
)
3000 switch (reader
.Name
) {
3001 case "MemberSignature":
3002 MemberSignatures
[reader
.GetAttribute ("Language")] = reader
.GetAttribute ("Value");
3005 MemberType
= reader
.ReadElementString ();
3008 if (reader
.Depth
== depth
+ 2)
3009 ReturnType
= reader
.ReadElementString ();
3012 if (reader
.Depth
== depth
+ 2)
3013 p
.Add (reader
.GetAttribute ("Type"));
3016 if (reader
.Depth
== depth
+ 1)
3020 } while (go
&& reader
.Read () && reader
.Depth
>= depth
);
3026 public DocumentationMember (XmlNode node
)
3028 MemberName
= node
.Attributes
["MemberName"].Value
;
3029 foreach (XmlNode n
in node
.SelectNodes ("MemberSignature")) {
3030 XmlAttribute l
= n
.Attributes
["Language"];
3031 XmlAttribute v
= n
.Attributes
["Value"];
3032 if (l
!= null && v
!= null)
3033 MemberSignatures
[l
.Value
] = v
.Value
;
3035 MemberType
= node
.SelectSingleNode ("MemberType").InnerText
;
3036 XmlNode rt
= node
.SelectSingleNode ("ReturnValue/ReturnType");
3038 ReturnType
= rt
.InnerText
;
3039 XmlNodeList p
= node
.SelectNodes ("Parameters/Parameter");
3041 Parameters
= new StringList (p
.Count
);
3042 for (int i
= 0; i
< p
.Count
; ++i
)
3043 Parameters
.Add (p
[i
].Attributes
["Type"].Value
);
3048 public enum MemberFormatterState
{
3051 WithinGenericTypeContainer
,
3054 public abstract class MemberFormatter
{
3055 public virtual string GetName (IMemberReference member
)
3057 TypeReference type
= member
as TypeReference
;
3059 return GetTypeName (type
);
3060 MethodReference method
= member
as MethodReference
;
3061 if (method
!= null && method
.Name
== ".ctor") // method.IsConstructor
3062 return GetConstructorName (method
);
3064 return GetMethodName (method
);
3065 PropertyReference prop
= member
as PropertyReference
;
3067 return GetPropertyName (prop
);
3068 FieldReference field
= member
as FieldReference
;
3070 return GetFieldName (field
);
3071 EventReference e
= member
as EventReference
;
3073 return GetEventName (e
);
3074 throw new NotSupportedException ("Can't handle: " +
3075 (member
== null ? "null" : member
.GetType().ToString()));
3078 protected virtual string GetTypeName (TypeReference type
)
3081 throw new ArgumentNullException ("type");
3082 return _AppendTypeName (new StringBuilder (type
.Name
.Length
), type
).ToString ();
3085 protected virtual char[] ArrayDelimeters
{
3086 get {return new char[]{'[', ']'}
;}
3089 protected virtual MemberFormatterState MemberFormatterState { get; set; }
3091 protected StringBuilder
_AppendTypeName (StringBuilder buf
, TypeReference type
)
3093 if (type
is ArrayType
) {
3094 TypeSpecification spec
= type
as TypeSpecification
;
3095 _AppendTypeName (buf
, spec
!= null ? spec
.ElementType
: type
.GetOriginalType ())
3096 .Append (ArrayDelimeters
[0]);
3097 var origState
= MemberFormatterState
;
3098 MemberFormatterState
= MemberFormatterState
.WithinArray
;
3099 ArrayType array
= (ArrayType
) type
;
3100 int rank
= array
.Rank
;
3102 buf
.Append (new string (',', rank
-1));
3103 MemberFormatterState
= origState
;
3104 return buf
.Append (ArrayDelimeters
[1]);
3106 if (type
is ReferenceType
) {
3107 return AppendRefTypeName (buf
, type
);
3109 if (type
is PointerType
) {
3110 return AppendPointerTypeName (buf
, type
);
3112 AppendNamespace (buf
, type
);
3113 if (type
is GenericParameter
) {
3114 return AppendTypeName (buf
, type
);
3116 GenericInstanceType genInst
= type
as GenericInstanceType
;
3117 if (type
.GenericParameters
.Count
== 0 &&
3118 (genInst
== null ? true : genInst
.GenericArguments
.Count
== 0)) {
3119 return AppendFullTypeName (buf
, type
);
3121 return AppendGenericType (buf
, type
);
3124 protected virtual StringBuilder
AppendNamespace (StringBuilder buf
, TypeReference type
)
3126 string ns
= DocUtils
.GetNamespace (type
);
3127 if (ns
!= null && ns
.Length
> 0)
3128 buf
.Append (ns
).Append ('.');
3132 private StringBuilder
AppendFullTypeName (StringBuilder buf
, TypeReference type
)
3134 if (type
.DeclaringType
!= null)
3135 AppendFullTypeName (buf
, type
.DeclaringType
).Append (NestedTypeSeparator
);
3136 return AppendTypeName (buf
, type
);
3139 protected virtual StringBuilder
AppendTypeName (StringBuilder buf
, TypeReference type
)
3141 return AppendTypeName (buf
, type
.Name
);
3144 protected virtual StringBuilder
AppendTypeName (StringBuilder buf
, string typename
)
3146 int n
= typename
.IndexOf ("`");
3148 return buf
.Append (typename
.Substring (0, n
));
3149 return buf
.Append (typename
);
3152 protected virtual string RefTypeModifier
{
3156 protected virtual StringBuilder
AppendRefTypeName (StringBuilder buf
, TypeReference type
)
3158 TypeSpecification spec
= type
as TypeSpecification
;
3159 return _AppendTypeName (buf
, spec
!= null ? spec
.ElementType
: type
.GetOriginalType ())
3160 .Append (RefTypeModifier
);
3163 protected virtual string PointerModifier
{
3167 protected virtual StringBuilder
AppendPointerTypeName (StringBuilder buf
, TypeReference type
)
3169 TypeSpecification spec
= type
as TypeSpecification
;
3170 return _AppendTypeName (buf
, spec
!= null ? spec
.ElementType
: type
.GetOriginalType ())
3171 .Append (PointerModifier
);
3174 protected virtual char[] GenericTypeContainer
{
3175 get {return new char[]{'<', '>'}
;}
3178 protected virtual char NestedTypeSeparator
{
3182 protected virtual StringBuilder
AppendGenericType (StringBuilder buf
, TypeReference type
)
3184 List
<TypeReference
> decls
= DocUtils
.GetDeclaringTypes (
3185 type
is GenericInstanceType
? type
.GetOriginalType () : type
);
3186 List
<TypeReference
> genArgs
= GetGenericArguments (type
);
3189 bool insertNested
= false;
3190 foreach (var decl
in decls
) {
3191 TypeReference declDef
= decl
.Resolve () ?? decl
;
3193 buf
.Append (NestedTypeSeparator
);
3195 insertNested
= true;
3196 AppendTypeName (buf
, declDef
);
3197 int ac
= DocUtils
.GetGenericArgumentCount (declDef
);
3201 buf
.Append (GenericTypeContainer
[0]);
3202 var origState
= MemberFormatterState
;
3203 MemberFormatterState
= MemberFormatterState
.WithinGenericTypeContainer
;
3204 _AppendTypeName (buf
, genArgs
[argIdx
++]);
3205 for (int i
= 1; i
< c
; ++i
)
3206 _AppendTypeName (buf
.Append (","), genArgs
[argIdx
++]);
3207 MemberFormatterState
= origState
;
3208 buf
.Append (GenericTypeContainer
[1]);
3214 private List
<TypeReference
> GetGenericArguments (TypeReference type
)
3216 var args
= new List
<TypeReference
> ();
3217 GenericInstanceType inst
= type
as GenericInstanceType
;
3219 args
.AddRange (inst
.GenericArguments
.Cast
<TypeReference
> ());
3221 args
.AddRange (type
.GenericParameters
.Cast
<TypeReference
> ());
3225 protected virtual StringBuilder
AppendGenericTypeConstraints (StringBuilder buf
, TypeReference type
)
3230 protected virtual string GetConstructorName (MethodReference constructor
)
3232 return constructor
.Name
;
3235 protected virtual string GetMethodName (MethodReference method
)
3240 protected virtual string GetPropertyName (PropertyReference property
)
3242 return property
.Name
;
3245 protected virtual string GetFieldName (FieldReference field
)
3250 protected virtual string GetEventName (EventReference e
)
3255 public virtual string GetDeclaration (IMemberReference member
)
3258 throw new ArgumentNullException ("member");
3259 TypeDefinition type
= member
as TypeDefinition
;
3261 return GetTypeDeclaration (type
);
3262 MethodDefinition method
= member
as MethodDefinition
;
3263 if (method
!= null && method
.IsConstructor
)
3264 return GetConstructorDeclaration (method
);
3266 return GetMethodDeclaration (method
);
3267 PropertyDefinition prop
= member
as PropertyDefinition
;
3269 return GetPropertyDeclaration (prop
);
3270 FieldDefinition field
= member
as FieldDefinition
;
3272 return GetFieldDeclaration (field
);
3273 EventDefinition e
= member
as EventDefinition
;
3275 return GetEventDeclaration (e
);
3276 throw new NotSupportedException ("Can't handle: " + member
.GetType().ToString());
3279 protected virtual string GetTypeDeclaration (TypeDefinition type
)
3282 throw new ArgumentNullException ("type");
3283 StringBuilder buf
= new StringBuilder (type
.Name
.Length
);
3284 _AppendTypeName (buf
, type
);
3285 AppendGenericTypeConstraints (buf
, type
);
3286 return buf
.ToString ();
3289 protected virtual string GetConstructorDeclaration (MethodDefinition constructor
)
3291 return GetConstructorName (constructor
);
3294 protected virtual string GetMethodDeclaration (MethodDefinition method
)
3296 // Special signature for destructors.
3297 if (method
.Name
== "Finalize" && method
.Parameters
.Count
== 0)
3298 return GetFinalizerName (method
);
3300 StringBuilder buf
= new StringBuilder ();
3302 AppendVisibility (buf
, method
);
3303 if (buf
.Length
== 0 &&
3304 !(DocUtils
.IsExplicitlyImplemented (method
) && !method
.IsSpecialName
))
3307 AppendModifiers (buf
, method
);
3309 if (buf
.Length
!= 0)
3311 buf
.Append (GetName (method
.ReturnType
.ReturnType
)).Append (" ");
3313 AppendMethodName (buf
, method
);
3314 AppendGenericMethod (buf
, method
).Append (" ");
3315 AppendParameters (buf
, method
, method
.Parameters
);
3316 AppendGenericMethodConstraints (buf
, method
);
3317 return buf
.ToString ();
3320 protected virtual StringBuilder
AppendMethodName (StringBuilder buf
, MethodDefinition method
)
3322 return buf
.Append (method
.Name
);
3325 protected virtual string GetFinalizerName (MethodDefinition method
)
3330 protected virtual StringBuilder
AppendVisibility (StringBuilder buf
, MethodDefinition method
)
3335 protected virtual StringBuilder
AppendModifiers (StringBuilder buf
, MethodDefinition method
)
3340 protected virtual StringBuilder
AppendGenericMethod (StringBuilder buf
, MethodDefinition method
)
3345 protected virtual StringBuilder
AppendParameters (StringBuilder buf
, MethodDefinition method
, ParameterDefinitionCollection parameters
)
3350 protected virtual StringBuilder
AppendGenericMethodConstraints (StringBuilder buf
, MethodDefinition method
)
3355 protected virtual string GetPropertyDeclaration (PropertyDefinition property
)
3357 return GetPropertyName (property
);
3360 protected virtual string GetFieldDeclaration (FieldDefinition field
)
3362 return GetFieldName (field
);
3365 protected virtual string GetEventDeclaration (EventDefinition e
)
3367 return GetEventName (e
);
3371 class CSharpFullMemberFormatter
: MemberFormatter
{
3373 protected override StringBuilder
AppendNamespace (StringBuilder buf
, TypeReference type
)
3375 string ns
= DocUtils
.GetNamespace (type
);
3376 if (GetCSharpType (type
.FullName
) == null && ns
!= null && ns
.Length
> 0 && ns
!= "System")
3377 buf
.Append (ns
).Append ('.');
3381 private string GetCSharpType (string t
)
3384 case "System.Byte": return "byte";
3385 case "System.SByte": return "sbyte";
3386 case "System.Int16": return "short";
3387 case "System.Int32": return "int";
3388 case "System.Int64": return "long";
3390 case "System.UInt16": return "ushort";
3391 case "System.UInt32": return "uint";
3392 case "System.UInt64": return "ulong";
3394 case "System.Single": return "float";
3395 case "System.Double": return "double";
3396 case "System.Decimal": return "decimal";
3397 case "System.Boolean": return "bool";
3398 case "System.Char": return "char";
3399 case "System.Void": return "void";
3400 case "System.String": return "string";
3401 case "System.Object": return "object";
3406 protected override StringBuilder
AppendTypeName (StringBuilder buf
, TypeReference type
)
3408 if (type
is GenericParameter
)
3409 return AppendGenericParameterConstraints (buf
, (GenericParameter
) type
).Append (type
.Name
);
3410 string t
= type
.FullName
;
3411 if (!t
.StartsWith ("System.")) {
3412 return base.AppendTypeName (buf
, type
);
3415 string s
= GetCSharpType (t
);
3417 return buf
.Append (s
);
3419 return base.AppendTypeName (buf
, type
);
3422 private StringBuilder
AppendGenericParameterConstraints (StringBuilder buf
, GenericParameter type
)
3424 if (MemberFormatterState
!= MemberFormatterState
.WithinGenericTypeContainer
)
3426 GenericParameterAttributes attrs
= type
.Attributes
;
3427 bool isout
= (attrs
& GenericParameterAttributes
.Covariant
) != 0;
3428 bool isin
= (attrs
& GenericParameterAttributes
.Contravariant
) != 0;
3432 buf
.Append ("out ");
3436 protected override string GetTypeDeclaration (TypeDefinition type
)
3438 string visibility
= GetTypeVisibility (type
.Attributes
);
3439 if (visibility
== null)
3442 StringBuilder buf
= new StringBuilder ();
3444 buf
.Append (visibility
);
3447 MemberFormatter full
= new CSharpFullMemberFormatter ();
3449 if (DocUtils
.IsDelegate (type
)) {
3450 buf
.Append("delegate ");
3451 MethodDefinition invoke
= type
.GetMethod ("Invoke");
3452 buf
.Append (full
.GetName (invoke
.ReturnType
.ReturnType
)).Append (" ");
3453 buf
.Append (GetName (type
));
3454 AppendParameters (buf
, invoke
, invoke
.Parameters
);
3455 AppendGenericTypeConstraints (buf
, type
);
3458 return buf
.ToString();
3461 if (type
.IsAbstract
&& !type
.IsInterface
)
3462 buf
.Append("abstract ");
3463 if (type
.IsSealed
&& !DocUtils
.IsDelegate (type
) && !type
.IsValueType
)
3464 buf
.Append("sealed ");
3465 buf
.Replace ("abstract sealed", "static");
3467 buf
.Append (GetTypeKind (type
));
3469 buf
.Append (GetCSharpType (type
.FullName
) == null
3474 TypeReference basetype
= type
.BaseType
;
3475 if (basetype
!= null && basetype
.FullName
== "System.Object" || type
.IsValueType
) // FIXME
3478 List
<string> interface_names
= DocUtils
.GetUserImplementedInterfaces (type
)
3479 .Select (iface
=> full
.GetName (iface
))
3483 if (basetype
!= null || interface_names
.Count
> 0)
3486 if (basetype
!= null) {
3487 buf
.Append (full
.GetName (basetype
));
3488 if (interface_names
.Count
> 0)
3492 for (int i
= 0; i
< interface_names
.Count
; i
++){
3495 buf
.Append (interface_names
[i
]);
3497 AppendGenericTypeConstraints (buf
, type
);
3500 return buf
.ToString ();
3503 static string GetTypeKind (TypeDefinition t
)
3509 if (t
.IsClass
|| t
.FullName
== "System.Enum")
3513 throw new ArgumentException(t
.FullName
);
3516 static string GetTypeVisibility (TypeAttributes ta
)
3518 switch (ta
& TypeAttributes
.VisibilityMask
) {
3519 case TypeAttributes
.Public
:
3520 case TypeAttributes
.NestedPublic
:
3523 case TypeAttributes
.NestedFamily
:
3524 case TypeAttributes
.NestedFamORAssem
:
3532 protected override StringBuilder
AppendGenericTypeConstraints (StringBuilder buf
, TypeReference type
)
3534 if (type
.GenericParameters
.Count
== 0)
3536 return AppendConstraints (buf
, type
.GenericParameters
);
3539 private StringBuilder
AppendConstraints (StringBuilder buf
, GenericParameterCollection genArgs
)
3541 foreach (GenericParameter genArg
in genArgs
) {
3542 GenericParameterAttributes attrs
= genArg
.Attributes
;
3543 ConstraintCollection constraints
= genArg
.Constraints
;
3544 if (attrs
== GenericParameterAttributes
.NonVariant
&& constraints
.Count
== 0)
3547 bool isref
= (attrs
& GenericParameterAttributes
.ReferenceTypeConstraint
) != 0;
3548 bool isvt
= (attrs
& GenericParameterAttributes
.NotNullableValueTypeConstraint
) != 0;
3549 bool isnew
= (attrs
& GenericParameterAttributes
.DefaultConstructorConstraint
) != 0;
3552 if (!isref
&& !isvt
&& !isnew
&& constraints
.Count
== 0)
3554 buf
.Append (" where ").Append (genArg
.Name
).Append (" : ");
3556 buf
.Append ("class");
3560 buf
.Append ("struct");
3563 if (constraints
.Count
> 0 && !isvt
) {
3566 buf
.Append (GetTypeName (constraints
[0]));
3567 for (int i
= 1; i
< constraints
.Count
; ++i
)
3568 buf
.Append (", ").Append (GetTypeName (constraints
[i
]));
3570 if (isnew
&& !isvt
) {
3573 buf
.Append ("new()");
3579 protected override string GetConstructorDeclaration (MethodDefinition constructor
)
3581 StringBuilder buf
= new StringBuilder ();
3582 AppendVisibility (buf
, constructor
);
3583 if (buf
.Length
== 0)
3587 base.AppendTypeName (buf
, constructor
.DeclaringType
.Name
).Append (' ');
3588 AppendParameters (buf
, constructor
, constructor
.Parameters
);
3591 return buf
.ToString ();
3594 protected override string GetMethodDeclaration (MethodDefinition method
)
3596 string decl
= base.GetMethodDeclaration (method
);
3602 protected override StringBuilder
AppendMethodName (StringBuilder buf
, MethodDefinition method
)
3604 if (DocUtils
.IsExplicitlyImplemented (method
)) {
3605 TypeReference iface
;
3606 MethodReference ifaceMethod
;
3607 DocUtils
.GetInfoForExplicitlyImplementedMethod (method
, out iface
, out ifaceMethod
);
3608 return buf
.Append (new CSharpMemberFormatter ().GetName (iface
))
3610 .Append (ifaceMethod
.Name
);
3612 return base.AppendMethodName (buf
, method
);
3615 protected override StringBuilder
AppendGenericMethodConstraints (StringBuilder buf
, MethodDefinition method
)
3617 if (method
.GenericParameters
.Count
== 0)
3619 return AppendConstraints (buf
, method
.GenericParameters
);
3622 protected override string RefTypeModifier
{
3626 protected override string GetFinalizerName (MethodDefinition method
)
3628 return "~" + method
.DeclaringType
.Name
+ " ()";
3631 protected override StringBuilder
AppendVisibility (StringBuilder buf
, MethodDefinition method
)
3635 if (method
.IsPublic
)
3636 return buf
.Append ("public");
3637 if (method
.IsFamily
|| method
.IsFamilyOrAssembly
)
3638 return buf
.Append ("protected");
3642 protected override StringBuilder
AppendModifiers (StringBuilder buf
, MethodDefinition method
)
3644 string modifiers
= String
.Empty
;
3645 if (method
.IsStatic
) modifiers
+= " static";
3646 if (method
.IsVirtual
&& !method
.IsAbstract
) {
3647 if ((method
.Attributes
& MethodAttributes
.NewSlot
) != 0) modifiers
+= " virtual";
3648 else modifiers
+= " override";
3650 TypeDefinition declType
= (TypeDefinition
) method
.DeclaringType
;
3651 if (method
.IsAbstract
&& !declType
.IsInterface
) modifiers
+= " abstract";
3652 if (method
.IsFinal
) modifiers
+= " sealed";
3653 if (modifiers
== " virtual sealed") modifiers
= "";
3655 return buf
.Append (modifiers
);
3658 protected override StringBuilder
AppendGenericMethod (StringBuilder buf
, MethodDefinition method
)
3660 if (method
.IsGenericMethod ()) {
3661 GenericParameterCollection args
= method
.GenericParameters
;
3662 if (args
.Count
> 0) {
3664 buf
.Append (args
[0].Name
);
3665 for (int i
= 1; i
< args
.Count
; ++i
)
3666 buf
.Append (",").Append (args
[i
].Name
);
3673 protected override StringBuilder
AppendParameters (StringBuilder buf
, MethodDefinition method
, ParameterDefinitionCollection parameters
)
3675 return AppendParameters (buf
, method
, parameters
, '(', ')');
3678 private StringBuilder
AppendParameters (StringBuilder buf
, MethodDefinition method
, ParameterDefinitionCollection parameters
, char begin
, char end
)
3682 if (parameters
.Count
> 0) {
3683 if (DocUtils
.IsExtensionMethod (method
))
3684 buf
.Append ("this ");
3685 AppendParameter (buf
, parameters
[0]);
3686 for (int i
= 1; i
< parameters
.Count
; ++i
) {
3688 AppendParameter (buf
, parameters
[i
]);
3692 return buf
.Append (end
);
3695 private StringBuilder
AppendParameter (StringBuilder buf
, ParameterDefinition parameter
)
3697 if (parameter
.ParameterType
is ReferenceType
) {
3698 if (parameter
.IsOut
)
3699 buf
.Append ("out ");
3701 buf
.Append ("ref ");
3703 buf
.Append (GetName (parameter
.ParameterType
)).Append (" ");
3704 return buf
.Append (parameter
.Name
);
3707 protected override string GetPropertyDeclaration (PropertyDefinition property
)
3709 MethodDefinition method
;
3711 string get_visible
= null;
3712 if ((method
= property
.GetMethod
) != null &&
3713 (DocUtils
.IsExplicitlyImplemented (method
) ||
3714 (!method
.IsPrivate
&& !method
.IsAssembly
&& !method
.IsFamilyAndAssembly
)))
3715 get_visible
= AppendVisibility (new StringBuilder (), method
).ToString ();
3716 string set_visible
= null;
3717 if ((method
= property
.SetMethod
) != null &&
3718 (DocUtils
.IsExplicitlyImplemented (method
) ||
3719 (!method
.IsPrivate
&& !method
.IsAssembly
&& !method
.IsFamilyAndAssembly
)))
3720 set_visible
= AppendVisibility (new StringBuilder (), method
).ToString ();
3722 if ((set_visible
== null) && (get_visible
== null))
3726 StringBuilder buf
= new StringBuilder ();
3727 if (get_visible
!= null && (set_visible
== null || (set_visible
!= null && get_visible
== set_visible
)))
3728 buf
.Append (visibility
= get_visible
);
3729 else if (set_visible
!= null && get_visible
== null)
3730 buf
.Append (visibility
= set_visible
);
3732 buf
.Append (visibility
= "public");
3734 // Pick an accessor to use for static/virtual/override/etc. checks.
3735 method
= property
.SetMethod
;
3737 method
= property
.GetMethod
;
3739 string modifiers
= String
.Empty
;
3740 if (method
.IsStatic
) modifiers
+= " static";
3741 if (method
.IsVirtual
&& !method
.IsAbstract
) {
3742 if ((method
.Attributes
& MethodAttributes
.NewSlot
) != 0)
3743 modifiers
+= " virtual";
3745 modifiers
+= " override";
3747 TypeDefinition declDef
= (TypeDefinition
) method
.DeclaringType
;
3748 if (method
.IsAbstract
&& !declDef
.IsInterface
)
3749 modifiers
+= " abstract";
3751 modifiers
+= " sealed";
3752 if (modifiers
== " virtual sealed")
3754 buf
.Append (modifiers
).Append (' ');
3756 buf
.Append (GetName (property
.PropertyType
)).Append (' ');
3758 IEnumerable
<IMemberReference
> defs
= property
.DeclaringType
.GetDefaultMembers ();
3759 string name
= property
.Name
;
3760 foreach (IMemberReference mi
in defs
) {
3761 if (mi
== property
) {
3766 buf
.Append (name
== "this" ? name
: DocUtils
.GetPropertyName (property
));
3768 if (property
.Parameters
.Count
!= 0) {
3769 AppendParameters (buf
, method
, property
.Parameters
, '[', ']');
3773 if (set_visible
!= null) {
3774 if (set_visible
!= visibility
)
3775 buf
.Append (' ').Append (set_visible
);
3776 buf
.Append (" set;");
3778 if (get_visible
!= null) {
3779 if (get_visible
!= visibility
)
3780 buf
.Append (' ').Append (get_visible
);
3781 buf
.Append (" get;");
3785 return buf
[0] != ' ' ? buf
.ToString () : buf
.ToString (1, buf
.Length
-1);
3788 protected override string GetFieldDeclaration (FieldDefinition field
)
3790 TypeDefinition declType
= (TypeDefinition
) field
.DeclaringType
;
3791 if (declType
.IsEnum
&& field
.Name
== "value__")
3792 return null; // This member of enums aren't documented.
3794 StringBuilder buf
= new StringBuilder ();
3795 AppendFieldVisibility (buf
, field
);
3796 if (buf
.Length
== 0)
3799 if (declType
.IsEnum
)
3802 if (field
.IsStatic
&& !field
.IsLiteral
)
3803 buf
.Append (" static");
3804 if (field
.IsInitOnly
)
3805 buf
.Append (" readonly");
3806 if (field
.IsLiteral
)
3807 buf
.Append (" const");
3809 buf
.Append (' ').Append (GetName (field
.FieldType
)).Append (' ');
3810 buf
.Append (field
.Name
);
3811 AppendFieldValue (buf
, field
);
3814 return buf
.ToString ();
3817 static StringBuilder
AppendFieldVisibility (StringBuilder buf
, FieldDefinition field
)
3820 return buf
.Append ("public");
3821 if (field
.IsFamily
|| field
.IsFamilyOrAssembly
)
3822 return buf
.Append ("protected");
3826 static StringBuilder
AppendFieldValue (StringBuilder buf
, FieldDefinition field
)
3828 // enums have a value__ field, which we ignore
3829 if (((TypeDefinition
) field
.DeclaringType
).IsEnum
||
3830 field
.DeclaringType
.IsGenericType ())
3832 if (field
.HasConstant
&& field
.IsLiteral
) {
3835 val
= field
.Constant
;
3840 buf
.Append (" = ").Append ("null");
3841 else if (val
is Enum
)
3842 buf
.Append (" = ").Append (val
.ToString ());
3843 else if (val
is IFormattable
) {
3844 string value = ((IFormattable
)val
).ToString();
3846 value = "\"" + value + "\"";
3847 buf
.Append (" = ").Append (value);
3853 protected override string GetEventDeclaration (EventDefinition e
)
3855 StringBuilder buf
= new StringBuilder ();
3856 if (AppendVisibility (buf
, e
.AddMethod
).Length
== 0) {
3860 AppendModifiers (buf
, e
.AddMethod
);
3862 buf
.Append (" event ");
3863 buf
.Append (GetName (e
.EventType
)).Append (' ');
3864 buf
.Append (e
.Name
).Append (';');
3866 return buf
.ToString ();
3870 class CSharpMemberFormatter
: CSharpFullMemberFormatter
{
3871 protected override StringBuilder
AppendNamespace (StringBuilder buf
, TypeReference type
)
3877 class DocTypeFullMemberFormatter
: MemberFormatter
{
3878 public static readonly MemberFormatter Default
= new DocTypeFullMemberFormatter ();
3880 protected override char NestedTypeSeparator
{
3885 class DocTypeMemberFormatter
: DocTypeFullMemberFormatter
{
3886 protected override StringBuilder
AppendNamespace (StringBuilder buf
, TypeReference type
)
3892 class SlashDocMemberFormatter
: MemberFormatter
{
3894 protected override char[] GenericTypeContainer
{
3895 get {return new char[]{'{', '}
'};}
3898 private bool AddTypeCount = true;
3900 private TypeReference genDeclType;
3901 private MethodReference genDeclMethod;
3903 protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3905 if (type is GenericParameter) {
3907 if (genDeclType != null) {
3908 GenericParameterCollection genArgs = genDeclType.GenericParameters;
3909 for (int i = 0; i < genArgs.Count; ++i) {
3910 if (genArgs [i].Name == type.Name) {
3911 buf.Append ('`
').Append (i);
3916 if (genDeclMethod != null) {
3917 GenericParameterCollection genArgs = null;
3918 if (genDeclMethod.IsGenericMethod ()) {
3919 genArgs = genDeclMethod.GenericParameters;
3920 for (int i = 0; i < genArgs.Count; ++i) {
3921 if (genArgs [i].Name == type.Name) {
3922 buf.Append ("``").Append (i);
3928 if (genDeclType == null && genDeclMethod == null) {
3929 // Probably from within an explicitly implemented interface member,
3930 // where CSC uses parameter names instead of indices (why?), e.g.
3931 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
3932 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
3933 buf.Append (type.Name);
3935 if (buf.Length == l) {
3936 throw new Exception (string.Format (
3937 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}",
3938 type.Name, genDeclType, genDeclMethod));
3942 base.AppendTypeName (buf, type);
3944 int numArgs = type.GenericParameters.Count;
3945 if (type.DeclaringType != null)
3946 numArgs -= type.GenericParameters.Count;
3948 buf.Append ('`
').Append (numArgs);
3955 protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3958 base.AppendGenericType (buf, type);
3960 AppendType (buf, type);
3964 private StringBuilder AppendType (StringBuilder buf, TypeReference type)
3966 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
3967 bool insertNested = false;
3968 int prevParamCount = 0;
3969 foreach (var decl in decls) {
3971 buf.Append (NestedTypeSeparator);
3972 insertNested = true;
3973 base.AppendTypeName (buf, decl);
3974 int argCount = DocUtils.GetGenericArgumentCount (decl);
3975 int numArgs = argCount - prevParamCount;
3976 prevParamCount = argCount;
3978 buf.Append ('`
').Append (numArgs);
3983 public override string GetDeclaration (IMemberReference member)
3985 TypeReference r = member as TypeReference;
3987 return "T:" + GetTypeName (r);
3989 return base.GetDeclaration (member);
3992 protected override string GetConstructorName (MethodReference constructor)
3994 return GetMethodDefinitionName (constructor, "#ctor");
3997 protected override string GetMethodName (MethodReference method)
4000 MethodDefinition methodDef = method as MethodDefinition;
4001 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4004 TypeReference iface;
4005 MethodReference ifaceMethod;
4006 DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4007 AddTypeCount = false;
4008 name = GetTypeName (iface) + "." + ifaceMethod.Name;
4009 AddTypeCount = true;
4011 return GetMethodDefinitionName (method, name);
4014 private string GetMethodDefinitionName (MethodReference method, string name)
4016 StringBuilder buf = new StringBuilder ();
4017 buf.Append (GetTypeName (method.DeclaringType));
4019 buf.Append (name.Replace (".", "#"));
4020 if (method.IsGenericMethod ()) {
4021 GenericParameterCollection genArgs = method.GenericParameters;
4022 if (genArgs.Count > 0)
4023 buf.Append ("``").Append (genArgs.Count);
4025 ParameterDefinitionCollection parameters = method.Parameters;
4027 genDeclType = method.DeclaringType;
4028 genDeclMethod = method;
4029 AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4033 genDeclMethod = null;
4035 return buf.ToString ();
4038 private StringBuilder AppendParameters (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinitionCollection parameters)
4040 if (parameters.Count == 0)
4045 AppendParameter (buf, genArgs, parameters [0]);
4046 for (int i = 1; i < parameters.Count; ++i) {
4048 AppendParameter (buf, genArgs, parameters [i]);
4051 return buf.Append (')');
4054 private StringBuilder AppendParameter (StringBuilder buf, GenericParameterCollection genArgs, ParameterDefinition parameter)
4056 AddTypeCount = false;
4057 buf.Append (GetTypeName (parameter.ParameterType));
4058 AddTypeCount = true;
4062 protected override string GetPropertyName (PropertyReference property)
4066 PropertyDefinition propertyDef = property as PropertyDefinition;
4067 MethodDefinition method = null;
4068 if (propertyDef != null)
4069 method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4070 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4071 name = property.Name;
4073 TypeReference iface;
4074 MethodReference ifaceMethod;
4075 DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4076 AddTypeCount = false;
4077 name = string.Join ("#", new string[]{
4078 GetTypeName (iface).Replace (".", "#"),
4079 DocUtils.GetMember (property.Name)
4081 AddTypeCount = true;
4084 StringBuilder buf = new StringBuilder ();
4085 buf.Append (GetName (property.DeclaringType));
4088 ParameterDefinitionCollection parameters = property.Parameters;
4089 if (parameters.Count > 0) {
4090 genDeclType = property.DeclaringType;
4092 GenericParameterCollection genArgs = property.DeclaringType.GenericParameters;
4093 AppendParameter (buf, genArgs, parameters [0]);
4094 for (int i = 1; i < parameters.Count; ++i) {
4096 AppendParameter (buf, genArgs, parameters [i]);
4101 return buf.ToString ();
4104 protected override string GetFieldName (FieldReference field)
4106 return string.Format ("{0}.{1}",
4107 GetName (field.DeclaringType), field.Name);
4110 protected override string GetEventName (EventReference e)
4112 return string.Format ("{0}.{1}",
4113 GetName (e.DeclaringType), e.Name);
4116 protected override string GetTypeDeclaration (TypeDefinition type)
4118 string name = GetName (type);
4124 protected override string GetConstructorDeclaration (MethodDefinition constructor)
4126 string name = GetName (constructor);
4132 protected override string GetMethodDeclaration (MethodDefinition method)
4134 string name = GetName (method);
4137 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4138 genDeclType = method.DeclaringType;
4139 genDeclMethod = method;
4140 name += "~" + GetName (method.ReturnType.ReturnType);
4142 genDeclMethod = null;
4147 protected override string GetPropertyDeclaration (PropertyDefinition property)
4149 string name = GetName (property);
4155 protected override string GetFieldDeclaration (FieldDefinition field)
4157 string name = GetName (field);
4163 protected override string GetEventDeclaration (EventDefinition e)
4165 string name = GetName (e);
4172 class FileNameMemberFormatter : SlashDocMemberFormatter {
4173 protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4178 protected override char NestedTypeSeparator {