Merge pull request #121 from LogosBible/processfixes
[mono.git] / mcs / tools / mdoc / Mono.Documentation / monodocer.cs
1 // Updater program for syncing Mono's ECMA-style documentation files
2 // with an assembly.
3 // By Joshua Tauberer <tauberer@for.net>
4
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using System.Globalization;
10 using System.IO;
11 using System.Linq;
12 using System.Text;
13 using System.Xml;
14 using System.Xml.XPath;
15
16 using Mono.Cecil;
17 using Mono.Options;
18
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>;
23
24 namespace Mono.Documentation {
25
26 class MDocUpdater : MDocCommand
27 {
28         string srcPath;
29         List<AssemblyDefinition> assemblies;
30         readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
31         
32         bool delete;
33         bool show_exceptions;
34         bool no_assembly_versions, ignore_missing_types;
35         ExceptionLocations? exceptions;
36         
37         internal int additions = 0, deletions = 0;
38
39         List<DocumentationImporter> importers = new List<DocumentationImporter> ();
40
41         DocumentationEnumerator docEnum;
42
43         string since;
44
45         static readonly MemberFormatter docTypeFormatter     = new DocTypeMemberFormatter ();
46         static readonly MemberFormatter filenameFormatter    = new FileNameMemberFormatter ();
47
48         static MemberFormatter[] typeFormatters = new MemberFormatter[]{
49                 new CSharpMemberFormatter (),
50                 new ILMemberFormatter (),
51         };
52
53         static MemberFormatter[] memberFormatters = new MemberFormatter[]{
54                 new CSharpFullMemberFormatter (),
55                 new ILFullMemberFormatter (),
56         };
57
58         internal static readonly MemberFormatter slashdocFormatter    = new SlashDocMemberFormatter ();
59
60         MyXmlNodeList extensionMethods = new MyXmlNodeList ();
61
62         public override void Run (IEnumerable<string> args)
63         {
64                 show_exceptions = DebugOutput;
65                 var types = new List<string> ();
66                 var p = new OptionSet () {
67                         { "delete",
68                                 "Delete removed members from the XML files.",
69                                 v => delete = v != null },
70                         { "exceptions:",
71                           "Document potential exceptions that members can generate.  {SOURCES} " +
72                                 "is a comma-separated list of:\n" +
73                                 "  asm      Method calls in same assembly\n" +
74                                 "  depasm   Method calls in dependent assemblies\n" +
75                                 "  all      Record all possible exceptions\n" +
76                                 "  added    Modifier; only create <exception/>s\n" +
77                                 "             for NEW types/members\n" +
78                                 "If nothing is specified, then only exceptions from the member will " +
79                                 "be listed.",
80                                 v => exceptions = ParseExceptionLocations (v) },
81                         { "f=",
82                                 "Specify a {FLAG} to alter behavior.  See later -f* options for available flags.",
83                                 v => {
84                                         switch (v) {
85                                                 case "ignore-missing-types":
86                                                         ignore_missing_types = true;
87                                                         break;
88                                                 case "no-assembly-versions":
89                                                         no_assembly_versions = true;
90                                                         break;
91                                                 default:
92                                                         throw new Exception ("Unsupported flag `" + v + "'.");
93                                         }
94                                 } },
95                         { "fignore-missing-types",
96                                 "Do not report an error if a --type=TYPE type\nwas not found.",
97                                 v => ignore_missing_types = v != null },
98                         { "fno-assembly-versions",
99                                 "Do not generate //AssemblyVersion elements.",
100                                 v => no_assembly_versions = v != null },
101                         { "i|import=", 
102                                 "Import documentation from {FILE}.",
103                                 v => AddImporter (v) },
104                         { "L|lib=",
105                                 "Check for assembly references in {DIRECTORY}.",
106                                 v => assemblyResolver.AddSearchDirectory (v) },
107                         { "library=",
108                                 "Ignored for compatibility with update-ecma-xml.",
109                                 v => {} },
110                         { "o|out=",
111                                 "Root {DIRECTORY} to generate/update documentation.",
112                                 v => srcPath = v },
113                         { "r=",
114                                 "Search for dependent assemblies in the directory containing {ASSEMBLY}.\n" +
115                                 "(Equivalent to '-L `dirname ASSEMBLY`'.)",
116                                 v => assemblyResolver.AddSearchDirectory (Path.GetDirectoryName (v)) },
117                         { "since=",
118                                 "Manually specify the assembly {VERSION} that new members were added in.",
119                                 v => since = v },
120                         { "type=",
121                           "Only update documentation for {TYPE}.",
122                                 v => types.Add (v) },
123                 };
124                 var assemblies = Parse (p, args, "update", 
125                                 "[OPTIONS]+ ASSEMBLIES",
126                                 "Create or update documentation from ASSEMBLIES.");
127                 if (assemblies == null)
128                         return;
129                 if (assemblies.Count == 0)
130                         Error ("No assemblies specified.");
131
132                 foreach (var dir in assemblies
133                                 .Where (a => a.Contains (Path.DirectorySeparatorChar))
134                                 .Select (a => Path.GetDirectoryName (a)))
135                         assemblyResolver.AddSearchDirectory (dir);
136
137                 // PARSE BASIC OPTIONS AND LOAD THE ASSEMBLY TO DOCUMENT
138                 
139                 if (srcPath == null)
140                         throw new InvalidOperationException("The --out option is required.");
141                 
142                 this.assemblies = assemblies.Select (a => LoadAssembly (a)).ToList ();
143
144                 docEnum = docEnum ?? new DocumentationEnumerator ();
145                 
146                 // PERFORM THE UPDATES
147                 
148                 if (types.Count > 0) {
149                         types.Sort ();
150                         DoUpdateTypes (srcPath, types, srcPath);
151                 }
152 #if false
153                 else if (opts.@namespace != null)
154                         DoUpdateNS (opts.@namespace, Path.Combine (opts.path, opts.@namespace),
155                                         Path.Combine (dest_dir, opts.@namespace));
156 #endif
157                 else
158                         DoUpdateAssemblies (srcPath, srcPath);
159
160                 Console.WriteLine("Members Added: {0}, Members Deleted: {1}", additions, deletions);
161         }
162
163         void AddImporter (string path)
164         {
165                 try {
166                         XmlReader r = new XmlTextReader (path);
167                         if (r.Read ()) {
168                                 while (r.NodeType != XmlNodeType.Element) {
169                                         if (!r.Read ())
170                                                 Error ("Unable to read XML file: {0}.", path);
171                                 }
172                                 if (r.LocalName == "doc") {
173                                         importers.Add (new MsxdocDocumentationImporter (path));
174                                 }
175                                 else if (r.LocalName == "Libraries") {
176                                         var ecmadocs = new XmlTextReader (path);
177                                         docEnum = new EcmaDocumentationEnumerator (this, ecmadocs);
178                                         importers.Add (new EcmaDocumentationImporter (ecmadocs));
179                                 }
180                                 else
181                                         Error ("Unsupported XML format within {0}.", path);
182                         }
183                         r.Close ();
184                 } catch (Exception e) {
185                         Environment.ExitCode = 1;
186                         Error ("Could not load XML file: {0}.", e.Message);
187                 }
188         }
189
190         static ExceptionLocations ParseExceptionLocations (string s)
191         {
192                 ExceptionLocations loc = ExceptionLocations.Member;
193                 if (s == null)
194                         return loc;
195                 foreach (var type in s.Split (',')) {
196                         switch (type) {
197                                 case "added":   loc |= ExceptionLocations.AddedMembers; break;
198                                 case "all":     loc |= ExceptionLocations.Assembly | ExceptionLocations.DependentAssemblies; break;
199                                 case "asm":     loc |= ExceptionLocations.Assembly; break;
200                                 case "depasm":  loc |= ExceptionLocations.DependentAssemblies; break;
201                                 default:        throw new NotSupportedException ("Unsupported --exceptions value: " + type);
202                         }
203                 }
204                 return loc;
205         }
206
207         internal void Warning (string format, params object[] args)
208         {
209                 Message (TraceLevel.Warning, "mdoc: " + format, args);
210         }
211         
212         private AssemblyDefinition LoadAssembly (string name)
213         {
214                 AssemblyDefinition assembly = null;
215                 try {
216                         assembly = AssemblyDefinition.ReadAssembly (name, new ReaderParameters { AssemblyResolver = assemblyResolver });
217                 } catch (System.IO.FileNotFoundException) { }
218
219                 if (assembly == null)
220                         throw new InvalidOperationException("Assembly " + name + " not found.");
221
222                 return assembly;
223         }
224
225         private static void WriteXml(XmlElement element, System.IO.TextWriter output) {
226                 OrderTypeAttributes (element);
227                 XmlTextWriter writer = new XmlTextWriter(output);
228                 writer.Formatting = Formatting.Indented;
229                 writer.Indentation = 2;
230                 writer.IndentChar = ' ';
231                 element.WriteTo(writer);
232                 output.WriteLine();     
233         }
234
235         private static void WriteFile (string filename, FileMode mode, Action<TextWriter> action)
236         {
237                 Action<string> creator = file => {
238                         using (var writer = OpenWrite (file, mode))
239                                 action (writer);
240                 };
241
242                 MdocFile.UpdateFile (filename, creator);
243         }
244
245         private static void OrderTypeAttributes (XmlElement e)
246         {
247                 foreach (XmlElement type in e.SelectNodes ("//Type")) {
248                         OrderTypeAttributes (type.Attributes);
249                 }
250         }
251
252         static readonly string[] TypeAttributeOrder = {
253                 "Name", "FullName", "FullNameSP", "Maintainer"
254         };
255
256         private static void OrderTypeAttributes (XmlAttributeCollection c)
257         {
258                 XmlAttribute[] attrs = new XmlAttribute [TypeAttributeOrder.Length];
259                 for (int i = 0; i < c.Count; ++i) {
260                         XmlAttribute a = c [i];
261                         for (int j = 0; j < TypeAttributeOrder.Length; ++j) {
262                                 if (a.Name == TypeAttributeOrder [j]) {
263                                         attrs [j] = a;
264                                         break;
265                                 }
266                         }
267                 }
268                 for (int i = attrs.Length-1; i >= 0; --i) {
269                         XmlAttribute n = attrs [i];
270                         if (n == null)
271                                 continue;
272                         XmlAttribute r = null;
273                         for (int j = i+1; j < attrs.Length; ++j) {
274                                 if (attrs [j] != null) {
275                                         r = attrs [j];
276                                         break;
277                                 }
278                         }
279                         if (r == null)
280                                 continue;
281                         c.Remove (n);
282                         c.InsertBefore (n, r);
283                 }
284         }
285         
286         private XmlDocument CreateIndexStub()
287         {
288                 XmlDocument index = new XmlDocument();
289
290                 XmlElement index_root = index.CreateElement("Overview");
291                 index.AppendChild(index_root);
292
293                 if (assemblies.Count == 0)
294                         throw new Exception ("No assembly");
295
296                 XmlElement index_assemblies = index.CreateElement("Assemblies");
297                 index_root.AppendChild(index_assemblies);
298
299                 XmlElement index_remarks = index.CreateElement("Remarks");
300                 index_remarks.InnerText = "To be added.";
301                 index_root.AppendChild(index_remarks);
302
303                 XmlElement index_copyright = index.CreateElement("Copyright");
304                 index_copyright.InnerText = "To be added.";
305                 index_root.AppendChild(index_copyright);
306
307                 XmlElement index_types = index.CreateElement("Types");
308                 index_root.AppendChild(index_types);
309                 
310                 return index;
311         }
312         
313         private static void WriteNamespaceStub(string ns, string outdir) {
314                 XmlDocument index = new XmlDocument();
315
316                 XmlElement index_root = index.CreateElement("Namespace");
317                 index.AppendChild(index_root);
318                 
319                 index_root.SetAttribute("Name", ns);
320
321                 XmlElement index_docs = index.CreateElement("Docs");
322                 index_root.AppendChild(index_docs);
323
324                 XmlElement index_summary = index.CreateElement("summary");
325                 index_summary.InnerText = "To be added.";
326                 index_docs.AppendChild(index_summary);
327
328                 XmlElement index_remarks = index.CreateElement("remarks");
329                 index_remarks.InnerText = "To be added.";
330                 index_docs.AppendChild(index_remarks);
331
332                 WriteFile (outdir + "/ns-" + ns + ".xml", FileMode.CreateNew, 
333                                 writer => WriteXml (index.DocumentElement, writer));
334         }
335
336         public void DoUpdateTypes (string basepath, List<string> typenames, string dest)
337         {
338                 var index = CreateIndexForTypes (dest);
339
340                 var found = new HashSet<string> ();
341                 foreach (AssemblyDefinition assembly in assemblies) {
342                         foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, typenames)) {
343                                 string relpath = DoUpdateType (type, basepath, dest);
344                                 if (relpath == null)
345                                         continue;
346
347                                 found.Add (type.FullName);
348
349                                 if (index == null)
350                                         continue;
351
352                                 index.Add (assembly);
353                                 index.Add (type);
354                         }
355                 }
356
357                 if (index != null)
358                         index.Write ();
359                 
360                 if (ignore_missing_types)
361                         return;
362
363                 var notFound = from n in typenames where !found.Contains (n) select n;
364                 if (notFound.Any ())
365                         throw new InvalidOperationException("Type(s) not found: " + string.Join (", ", notFound.ToArray ()));
366         }
367
368         class IndexForTypes {
369
370                 MDocUpdater app;
371                 string indexFile;
372
373                 XmlDocument index;
374                 XmlElement index_types;
375                 XmlElement index_assemblies;
376
377                 public IndexForTypes (MDocUpdater app, string indexFile, XmlDocument index)
378                 {
379                         this.app        = app;
380                         this.indexFile  = indexFile;
381                         this.index      = index;
382
383                         index_types = WriteElement (index.DocumentElement, "Types");
384                         index_assemblies = WriteElement (index.DocumentElement, "Assemblies");
385                 }
386
387                 public void Add (AssemblyDefinition assembly)
388                 {
389                         if (index_assemblies.SelectSingleNode ("Assembly[@Name='" + assembly.Name.Name + "']") != null)
390                                 return;
391
392                         app.AddIndexAssembly (assembly, index_assemblies);
393                 }
394
395                 public void Add (TypeDefinition type)
396                 {
397                         app.AddIndexType (type, index_types);
398                 }
399
400                 public void Write ()
401                 {
402                         SortIndexEntries (index_types);
403                         WriteFile (indexFile, FileMode.Create, 
404                                         writer => WriteXml (index.DocumentElement, writer));
405                 }
406         }
407
408         IndexForTypes CreateIndexForTypes (string dest)
409         {
410                 string indexFile = Path.Combine (dest, "index.xml");
411                 if (File.Exists (indexFile))
412                         return null;
413                 return new IndexForTypes (this, indexFile, CreateIndexStub ());
414         }
415
416         public string DoUpdateType (TypeDefinition type, string basepath, string dest)
417         {
418                 if (type.Namespace == null)
419                         Warning ("warning: The type `{0}' is in the root namespace.  This may cause problems with display within monodoc.",
420                                         type.FullName);
421                 if (!IsPublic (type))
422                         return null;
423                 
424                 // Must get the A+B form of the type name.
425                 string typename = GetTypeFileName(type);
426                 
427                 string reltypefile = DocUtils.PathCombine (DocUtils.GetNamespace (type), typename + ".xml");
428                 string typefile = Path.Combine (basepath, reltypefile);
429                 System.IO.FileInfo file = new System.IO.FileInfo(typefile);
430
431                 string output = null;
432                 if (dest == null) {
433                         output = typefile;
434                 } else if (dest == "-") {
435                         output = null;
436                 } else {
437                         output = Path.Combine (dest, reltypefile);
438                 }       
439
440                 if (file.Exists) {
441                         // Update
442                         XmlDocument basefile = new XmlDocument();
443                         try {
444                                 basefile.Load(typefile);
445                         } catch (Exception e) {
446                                 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
447                         }
448                         
449                         DoUpdateType2("Updating", basefile, type, output, false);
450                 } else {
451                         // Stub
452                         XmlElement td = StubType(type, output);
453                         if (td == null)
454                                 return null;
455                         
456                         System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
457                         if (!dir.Exists) {
458                                 dir.Create();
459                                 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
460                         }
461                 }
462                 return reltypefile;
463         }
464
465         public void DoUpdateNS (string ns, string nspath, string outpath)
466         {
467                 Dictionary<TypeDefinition, object> seenTypes = new Dictionary<TypeDefinition,object> ();
468                 AssemblyDefinition                  assembly = assemblies [0];
469
470                 foreach (System.IO.FileInfo file in new System.IO.DirectoryInfo(nspath).GetFiles("*.xml")) {
471                         XmlDocument basefile = new XmlDocument();
472                         string typefile = Path.Combine(nspath, file.Name);
473                         try {
474                                 basefile.Load(typefile);
475                         } catch (Exception e) {
476                                 throw new InvalidOperationException("Error loading " + typefile + ": " + e.Message, e);
477                         }
478
479                         string typename = 
480                                 GetTypeFileName (basefile.SelectSingleNode("Type/@FullName").InnerText);
481                         TypeDefinition type = assembly.GetType(typename);
482                         if (type == null) {
483                                 Warning ("Type no longer in assembly: " + typename);
484                                 continue;
485                         }                       
486
487                         seenTypes[type] = seenTypes;
488                         DoUpdateType2("Updating", basefile, type, Path.Combine(outpath, file.Name), false);
489                 }
490                 
491                 // Stub types not in the directory
492                 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
493                         if (type.Namespace != ns || seenTypes.ContainsKey(type))
494                                 continue;
495
496                         XmlElement td = StubType(type, Path.Combine(outpath, GetTypeFileName(type) + ".xml"));
497                         if (td == null) continue;
498                 }
499         }
500         
501         private static string GetTypeFileName (TypeReference type)
502         {
503                 return filenameFormatter.GetName (type);
504         }
505
506         public static string GetTypeFileName (string typename)
507         {
508                 StringBuilder filename = new StringBuilder (typename.Length);
509                 int numArgs = 0;
510                 int numLt = 0;
511                 bool copy = true;
512                 for (int i = 0; i < typename.Length; ++i) {
513                         char c = typename [i];
514                         switch (c) {
515                                 case '<':
516                                         copy = false;
517                                         ++numLt;
518                                         break;
519                                 case '>':
520                                         --numLt;
521                                         if (numLt == 0) {
522                                                 filename.Append ('`').Append ((numArgs+1).ToString());
523                                                 numArgs = 0;
524                                                 copy = true;
525                                         }
526                                         break;
527                                 case ',':
528                                         if (numLt == 1)
529                                                 ++numArgs;
530                                         break;
531                                 default:
532                                         if (copy)
533                                                 filename.Append (c);
534                                         break;
535                         }
536                 }
537                 return filename.ToString ();
538         }
539
540         private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
541         {
542                 XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
543                 index_assembly.SetAttribute ("Name", assembly.Name.Name);
544                 index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
545
546                 AssemblyNameDefinition name = assembly.Name;
547                 if (name.HasPublicKey) {
548                         XmlElement pubkey = parent.OwnerDocument.CreateElement ("AssemblyPublicKey");
549                         var key = new StringBuilder (name.PublicKey.Length*3 + 2);
550                         key.Append ("[");
551                         foreach (byte b in name.PublicKey)
552                                 key.AppendFormat ("{0,2:x2} ", b);
553                         key.Append ("]");
554                         pubkey.InnerText = key.ToString ();
555                         index_assembly.AppendChild (pubkey);
556                 }
557
558                 if (!string.IsNullOrEmpty (name.Culture)) {
559                         XmlElement culture = parent.OwnerDocument.CreateElement ("AssemblyCulture");
560                         culture.InnerText = name.Culture;
561                         index_assembly.AppendChild (culture);
562                 }
563
564                 MakeAttributes (index_assembly, GetCustomAttributes (assembly.CustomAttributes, ""));
565                 parent.AppendChild(index_assembly);
566         }
567
568         private void AddIndexType (TypeDefinition type, XmlElement index_types)
569         {
570                 string typename = GetTypeFileName(type);
571
572                 // Add namespace and type nodes into the index file as needed
573                 string ns = DocUtils.GetNamespace (type);
574                 XmlElement nsnode = (XmlElement) index_types.SelectSingleNode ("Namespace[@Name='" + ns + "']");
575                 if (nsnode == null) {
576                         nsnode = index_types.OwnerDocument.CreateElement("Namespace");
577                         nsnode.SetAttribute ("Name", ns);
578                         index_types.AppendChild (nsnode);
579                 }
580                 string doc_typename = GetDocTypeName (type);
581                 XmlElement typenode = (XmlElement) nsnode.SelectSingleNode ("Type[@Name='" + typename + "']");
582                 if (typenode == null) {
583                         typenode = index_types.OwnerDocument.CreateElement ("Type");
584                         typenode.SetAttribute ("Name", typename);
585                         nsnode.AppendChild (typenode);
586                 }
587                 if (typename != doc_typename)
588                         typenode.SetAttribute("DisplayName", doc_typename);
589                 else
590                         typenode.RemoveAttribute("DisplayName");
591
592                 typenode.SetAttribute ("Kind", GetTypeKind (type));
593         }
594
595         private void DoUpdateAssemblies (string source, string dest) 
596         {
597                 string indexfile = dest + "/index.xml";
598                 XmlDocument index;
599                 if (System.IO.File.Exists(indexfile)) {
600                         index = new XmlDocument();
601                         index.Load(indexfile);
602
603                         // Format change
604                         ClearElement(index.DocumentElement, "Assembly");
605                         ClearElement(index.DocumentElement, "Attributes");
606                 } else {
607                         index = CreateIndexStub();
608                 }
609                 
610                 string defaultTitle = "Untitled";
611                 if (assemblies.Count == 1)
612                         defaultTitle = assemblies[0].Name.Name;
613                 WriteElementInitialText(index.DocumentElement, "Title", defaultTitle);
614                 
615                 XmlElement index_types = WriteElement(index.DocumentElement, "Types");
616                 XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
617                 index_assemblies.RemoveAll ();
618
619
620                 HashSet<string> goodfiles = new HashSet<string> ();
621
622                 foreach (AssemblyDefinition assm in assemblies) {
623                         AddIndexAssembly (assm, index_assemblies);
624                         DoUpdateAssembly (assm, index_types, source, dest, goodfiles);
625                 }
626
627                 SortIndexEntries (index_types);
628                 
629                 CleanupFiles (dest, goodfiles);
630                 CleanupIndexTypes (index_types, goodfiles);
631                 CleanupExtensions (index_types);
632
633                 WriteFile (indexfile, FileMode.Create, 
634                                 writer => WriteXml(index.DocumentElement, writer));
635         }
636                 
637         private static char[] InvalidFilenameChars = {'\\', '/', ':', '*', '?', '"', '<', '>', '|'};
638
639         private void DoUpdateAssembly (AssemblyDefinition assembly, XmlElement index_types, string source, string dest, HashSet<string> goodfiles) 
640         {
641                 foreach (TypeDefinition type in docEnum.GetDocumentationTypes (assembly, null)) {
642                         string typename = GetTypeFileName(type);
643                         if (!IsPublic (type) || typename.IndexOfAny (InvalidFilenameChars) >= 0)
644                                 continue;
645
646                         string reltypepath = DoUpdateType (type, source, dest);
647                         if (reltypepath == null)
648                                 continue;
649                         
650                         // Add namespace and type nodes into the index file as needed
651                         AddIndexType (type, index_types);
652                                 
653                         // Ensure the namespace index file exists
654                         string onsdoc = DocUtils.PathCombine (dest, type.Namespace + ".xml");
655                         string nsdoc  = DocUtils.PathCombine (dest, "ns-" + type.Namespace + ".xml");
656                         if (File.Exists (onsdoc)) {
657                                 File.Move (onsdoc, nsdoc);
658                         }
659
660                         if (!File.Exists (nsdoc)) {
661                                 Console.WriteLine("New Namespace File: " + type.Namespace);
662                                 WriteNamespaceStub(type.Namespace, dest);
663                         }
664
665                         goodfiles.Add (reltypepath);
666                 }
667         }
668
669         private static void SortIndexEntries (XmlElement indexTypes)
670         {
671                 XmlNodeList namespaces = indexTypes.SelectNodes ("Namespace");
672                 XmlNodeComparer c = new AttributeNameComparer ();
673                 SortXmlNodes (indexTypes, namespaces, c);
674
675                 for (int i = 0; i < namespaces.Count; ++i)
676                         SortXmlNodes (namespaces [i], namespaces [i].SelectNodes ("Type"), c);
677         }
678
679         private static void SortXmlNodes (XmlNode parent, XmlNodeList children, XmlNodeComparer comparer)
680         {
681                 MyXmlNodeList l = new MyXmlNodeList (children.Count);
682                 for (int i = 0; i < children.Count; ++i)
683                         l.Add (children [i]);
684                 l.Sort (comparer);
685                 for (int i = l.Count - 1; i > 0; --i) {
686                         parent.InsertBefore (parent.RemoveChild ((XmlNode) l [i-1]), (XmlNode) l [i]);
687                 }
688         }
689
690         abstract class XmlNodeComparer : IComparer, IComparer<XmlNode>
691         {
692                 public abstract int Compare (XmlNode x, XmlNode y);
693
694                 public int Compare (object x, object y)
695                 {
696                         return Compare ((XmlNode) x, (XmlNode) y);
697                 }
698         }
699
700         class AttributeNameComparer : XmlNodeComparer {
701                 string attribute;
702
703                 public AttributeNameComparer ()
704                         : this ("Name")
705                 {
706                 }
707
708                 public AttributeNameComparer (string attribute)
709                 {
710                         this.attribute = attribute;
711                 }
712
713                 public override int Compare (XmlNode x, XmlNode y)
714                 {
715                         return x.Attributes [attribute].Value.CompareTo (y.Attributes [attribute].Value);
716                 }
717         }
718         
719         class VersionComparer : XmlNodeComparer {
720                 public override int Compare (XmlNode x, XmlNode y)
721                 {
722                         // Some of the existing docs use e.g. 1.0.x.x, which Version doesn't like.
723                         string a = GetVersion (x.InnerText);
724                         string b = GetVersion (y.InnerText);
725                         return new Version (a).CompareTo (new Version (b));
726                 }
727
728                 static string GetVersion (string v)
729                 {
730                         int n = v.IndexOf ("x");
731                         if (n < 0)
732                                 return v;
733                         return v.Substring (0, n-1);
734                 }
735         }
736
737         private static string GetTypeKind (TypeDefinition type)
738         {
739                 if (type.IsEnum)
740                         return "Enumeration";
741                 if (type.IsValueType)
742                         return "Structure";
743                 if (type.IsInterface)
744                         return "Interface";
745                 if (DocUtils.IsDelegate (type))
746                         return "Delegate";
747                 if (type.IsClass || type.FullName == "System.Enum") // FIXME
748                         return "Class";
749                 throw new ArgumentException ("Unknown kind for type: " + type.FullName);
750         }
751
752         private static bool IsPublic (TypeDefinition type)
753         {
754                 TypeDefinition decl = type;
755                 while (decl != null) {
756                         if (!(decl.IsPublic || decl.IsNestedPublic)) {
757                                 return false;
758                         }
759                         decl = (TypeDefinition) decl.DeclaringType;
760                 }
761                 return true;
762         }
763
764         private void CleanupFiles (string dest, HashSet<string> goodfiles)
765         {
766                 // Look for files that no longer correspond to types
767                 foreach (System.IO.DirectoryInfo nsdir in new System.IO.DirectoryInfo(dest).GetDirectories("*")) {
768                         foreach (System.IO.FileInfo typefile in nsdir.GetFiles("*.xml")) {
769                                 string relTypeFile = Path.Combine(nsdir.Name, typefile.Name);
770                                 if (!goodfiles.Contains (relTypeFile)) {
771                                         XmlDocument doc = new XmlDocument ();
772                                         doc.Load (typefile.FullName);
773                                         XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
774                                         if (UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
775                                                 using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
776                                                         WriteXml(doc.DocumentElement, writer);
777                                                 goodfiles.Add (relTypeFile);
778                                                 continue;
779                                         }
780                                         string newname = typefile.FullName + ".remove";
781                                         try { System.IO.File.Delete(newname); } catch (Exception) { }
782                                         try { typefile.MoveTo(newname); } catch (Exception) { }
783                                         Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
784                                 }
785                         }
786                 }
787         }
788
789         private static TextWriter OpenWrite (string path, FileMode mode)
790         {
791                 var w = new StreamWriter (
792                         new FileStream (path, mode),
793                         new UTF8Encoding (false)
794                 );
795                 w.NewLine = "\n";
796                 return w;
797         }
798
799         private string[] GetAssemblyVersions ()
800         {
801                 return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
802         }
803
804         private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
805         {
806                 // Look for type nodes that no longer correspond to types
807                 MyXmlNodeList remove = new MyXmlNodeList ();
808                 foreach (XmlElement typenode in index_types.SelectNodes("Namespace/Type")) {
809                         string fulltypename = Path.Combine (((XmlElement)typenode.ParentNode).GetAttribute("Name"), typenode.GetAttribute("Name") + ".xml");
810                         if (!goodfiles.Contains (fulltypename)) {
811                                 remove.Add (typenode);
812                         }
813                 }
814                 foreach (XmlNode n in remove)
815                         n.ParentNode.RemoveChild (n);
816         }
817
818         private void CleanupExtensions (XmlElement index_types)
819         {
820                 XmlNode e = index_types.SelectSingleNode ("/Overview/ExtensionMethods");
821                 if (extensionMethods.Count == 0) {
822                         if (e == null)
823                                 return;
824                         index_types.SelectSingleNode ("/Overview").RemoveChild (e);
825                         return;
826                 }
827                 if (e == null) {
828                         e = index_types.OwnerDocument.CreateElement ("ExtensionMethods");
829                         index_types.SelectSingleNode ("/Overview").AppendChild (e);
830                 }
831                 else
832                         e.RemoveAll ();
833                 extensionMethods.Sort (DefaultExtensionMethodComparer);
834                 foreach (XmlNode m in extensionMethods) {
835                         e.AppendChild (index_types.OwnerDocument.ImportNode (m, true));
836                 }
837         }
838
839         class ExtensionMethodComparer : XmlNodeComparer {
840                 public override int Compare (XmlNode x, XmlNode y)
841                 {
842                         XmlNode xLink = x.SelectSingleNode ("Member/Link");
843                         XmlNode yLink = y.SelectSingleNode ("Member/Link");
844
845                         int n = xLink.Attributes ["Type"].Value.CompareTo (
846                                         yLink.Attributes ["Type"].Value);
847                         if (n != 0)
848                                 return n;
849                         n = xLink.Attributes ["Member"].Value.CompareTo (
850                                         yLink.Attributes ["Member"].Value);
851                         if (n == 0 && !object.ReferenceEquals (x, y))
852                                 throw new InvalidOperationException ("Duplicate extension method found!");
853                         return n;
854                 }
855         }
856
857         static readonly XmlNodeComparer DefaultExtensionMethodComparer = new ExtensionMethodComparer ();
858                 
859         public void DoUpdateType2 (string message, XmlDocument basefile, TypeDefinition type, string output, bool insertSince)
860         {
861                 Console.WriteLine(message + ": " + type.FullName);
862                 
863                 StringToXmlNodeMap seenmembers = new StringToXmlNodeMap ();
864
865                 // Update type metadata
866                 UpdateType(basefile.DocumentElement, type);
867
868                 // Update existing members.  Delete member nodes that no longer should be there,
869                 // and remember what members are already documented so we don't add them again.
870                 if (true) {
871                         MyXmlNodeList todelete = new MyXmlNodeList ();
872                         foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
873                                 XmlElement oldmember  = info.Node;
874                                 MemberReference oldmember2 = info.Member;
875                                 string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
876
877                                 // Interface implementations and overrides are deleted from the docs
878                                 // unless the overrides option is given.
879                                 if (oldmember2 != null && sig == null)
880                                         oldmember2 = null;
881                                 
882                                 // Deleted (or signature changed)
883                                 if (oldmember2 == null) {
884                                         if (UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
885                                                 continue;
886                                         DeleteMember ("Member Removed", output, oldmember, todelete);
887                                         continue;
888                                 }
889                                 
890                                 // Duplicated
891                                 if (seenmembers.ContainsKey (sig)) {
892                                         if (object.ReferenceEquals (oldmember, seenmembers [sig])) {
893                                                 // ignore, already seen
894                                         }
895                                         else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
896                                                 DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
897                                         else
898                                                 Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
899                                         continue;
900                                 }
901                                 
902                                 // Update signature information
903                                 UpdateMember(info);
904                                 
905                                 seenmembers.Add (sig, oldmember);
906                         }
907                         foreach (XmlElement oldmember in todelete)
908                                 oldmember.ParentNode.RemoveChild (oldmember);
909                 }
910                 
911                 if (!DocUtils.IsDelegate (type)) {
912                         XmlNode members = WriteElement (basefile.DocumentElement, "Members");
913                         foreach (MemberReference m in type.GetMembers()) {
914                                 if (m is TypeDefinition) continue;
915                                 
916                                 string sig = memberFormatters [0].GetDeclaration (m);
917                                 if (sig == null) continue;
918                                 if (seenmembers.ContainsKey(sig)) continue;
919                                 
920                                 XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
921                                 if (mm == null) continue;
922                                 members.AppendChild( mm );
923         
924                                 Console.WriteLine("Member Added: " + mm.SelectSingleNode("MemberSignature/@Value").InnerText);
925                                 additions++;
926                         }
927                 }
928                 
929                 // Import code snippets from files
930                 foreach (XmlNode code in basefile.GetElementsByTagName("code")) {
931                         if (!(code is XmlElement)) continue;
932                         string file = ((XmlElement)code).GetAttribute("src");
933                         string lang = ((XmlElement)code).GetAttribute("lang");
934                         if (file != "") {
935                                 string src = GetCodeSource (lang, Path.Combine (srcPath, file));
936                                 if (src != null)
937                                         code.InnerText = src;
938                         }
939                 }
940
941                 if (insertSince && since != null) {
942                         XmlNode docs = basefile.DocumentElement.SelectSingleNode("Docs");
943                         docs.AppendChild (CreateSinceNode (basefile));
944                 }
945
946                 do {
947                         XmlElement d = basefile.DocumentElement ["Docs"];
948                         XmlElement m = basefile.DocumentElement ["Members"];
949                         if (d != null && m != null)
950                                 basefile.DocumentElement.InsertBefore (
951                                                 basefile.DocumentElement.RemoveChild (d), m);
952                         SortTypeMembers (m);
953                 } while (false);
954
955                 if (output == null)
956                         WriteXml(basefile.DocumentElement, Console.Out);
957                 else {
958                         FileInfo file = new FileInfo (output);
959                         if (!file.Directory.Exists) {
960                                 Console.WriteLine("Namespace Directory Created: " + type.Namespace);
961                                 file.Directory.Create ();
962                         }
963                         WriteFile (output, FileMode.Create,
964                                         writer => WriteXml(basefile.DocumentElement, writer));
965                 }
966         }
967
968         private string GetCodeSource (string lang, string file)
969         {
970                 int anchorStart;
971                 if (lang == "C#" && (anchorStart = file.IndexOf (".cs#")) >= 0) {
972                         // Grab the specified region
973                         string region = "#region " + file.Substring (anchorStart + 4);
974                         file          = file.Substring (0, anchorStart + 3);
975                         try {
976                                 using (StreamReader reader = new StreamReader (file)) {
977                                         string line;
978                                         StringBuilder src = new StringBuilder ();
979                                         int indent = -1;
980                                         while ((line = reader.ReadLine ()) != null) {
981                                                 if (line.Trim() == region) {
982                                                         indent = line.IndexOf (region);
983                                                         continue;
984                                                 }
985                                                 if (indent >= 0 && line.Trim().StartsWith ("#endregion")) {
986                                                         break;
987                                                 }
988                                                 if (indent >= 0)
989                                                         src.Append (
990                                                                         (line.Length > 0 ? line.Substring (indent) : string.Empty) +
991                                                                         "\n");
992                                         }
993                                         return src.ToString ();
994                                 }
995                         } catch (Exception e) {
996                                 Warning ("Could not load <code/> file '{0}' region '{1}': {2}",
997                                                 file, region, show_exceptions ? e.ToString () : e.Message);
998                                 return null;
999                         }
1000                 }
1001                 try {
1002                         using (StreamReader reader = new StreamReader (file))
1003                                 return reader.ReadToEnd ();
1004                 } catch (Exception e) {
1005                         Warning ("Could not load <code/> file '" + file + "': " + e.Message);
1006                 }
1007                 return null;
1008         }
1009
1010         void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
1011         {
1012                 string format = output != null
1013                         ? "{0}: File='{1}'; Signature='{4}'"
1014                         : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
1015                 Warning (format,
1016                                 reason, 
1017                                 output,
1018                                 member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1019                                 member.Attributes ["MemberName"].Value, 
1020                                 member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
1021                 if (!delete && MemberDocsHaveUserContent (member)) {
1022                         Warning ("Member deletions must be enabled with the --delete option.");
1023                 } else {
1024                         todelete.Add (member);
1025                         deletions++;
1026                 }
1027         }
1028
1029         class MemberComparer : XmlNodeComparer {
1030                 public override int Compare (XmlNode x, XmlNode y)
1031                 {
1032                         int r;
1033                         string xMemberName = x.Attributes ["MemberName"].Value;
1034                         string yMemberName = y.Attributes ["MemberName"].Value;
1035
1036                         // generic methods *end* with '>'
1037                         // it's possible for explicitly implemented generic interfaces to
1038                         // contain <...> without being a generic method
1039                         if ((!xMemberName.EndsWith (">") || !yMemberName.EndsWith (">")) &&
1040                                         (r = xMemberName.CompareTo (yMemberName)) != 0)
1041                                 return r;
1042
1043                         int lt;
1044                         if ((lt = xMemberName.IndexOf ("<")) >= 0)
1045                                 xMemberName = xMemberName.Substring (0, lt);
1046                         if ((lt = yMemberName.IndexOf ("<")) >= 0)
1047                                 yMemberName = yMemberName.Substring (0, lt);
1048                         if ((r = xMemberName.CompareTo (yMemberName)) != 0)
1049                                 return r;
1050
1051                         // if @MemberName matches, then it's either two different types of
1052                         // members sharing the same name, e.g. field & property, or it's an
1053                         // overloaded method.
1054                         // for different type, sort based on MemberType value.
1055                         r = x.SelectSingleNode ("MemberType").InnerText.CompareTo (
1056                                         y.SelectSingleNode ("MemberType").InnerText);
1057                         if (r != 0)
1058                                 return r;
1059
1060                         // same type -- must be an overloaded method.  Sort based on type 
1061                         // parameter count, then parameter count, then by the parameter 
1062                         // type names.
1063                         XmlNodeList xTypeParams = x.SelectNodes ("TypeParameters/TypeParameter");
1064                         XmlNodeList yTypeParams = y.SelectNodes ("TypeParameters/TypeParameter");
1065                         if (xTypeParams.Count != yTypeParams.Count)
1066                                 return xTypeParams.Count <= yTypeParams.Count ? -1 : 1;
1067                         for (int i = 0; i < xTypeParams.Count; ++i) {
1068                                 r = xTypeParams [i].Attributes ["Name"].Value.CompareTo (
1069                                                 yTypeParams [i].Attributes ["Name"].Value);
1070                                 if (r != 0)
1071                                         return r;
1072                         }
1073
1074                         XmlNodeList xParams = x.SelectNodes ("Parameters/Parameter");
1075                         XmlNodeList yParams = y.SelectNodes ("Parameters/Parameter");
1076                         if (xParams.Count != yParams.Count)
1077                                 return xParams.Count <= yParams.Count ? -1 : 1;
1078                         for (int i = 0; i < xParams.Count; ++i) {
1079                                 r = xParams [i].Attributes ["Type"].Value.CompareTo (
1080                                                 yParams [i].Attributes ["Type"].Value);
1081                                 if (r != 0)
1082                                         return r;
1083                         }
1084                         // all parameters match, but return value might not match if it was
1085                         // changed between one version and another.
1086                         XmlNode xReturn = x.SelectSingleNode ("ReturnValue/ReturnType");
1087                         XmlNode yReturn = y.SelectSingleNode ("ReturnValue/ReturnType");
1088                         if (xReturn != null && yReturn != null) {
1089                                 r = xReturn.InnerText.CompareTo (yReturn.InnerText);
1090                                 if (r != 0)
1091                                         return r;
1092                         }
1093
1094                         return 0;
1095                 }
1096         }
1097
1098         static readonly MemberComparer DefaultMemberComparer = new MemberComparer ();
1099
1100         private static void SortTypeMembers (XmlNode members)
1101         {
1102                 if (members == null)
1103                         return;
1104                 SortXmlNodes (members, members.SelectNodes ("Member"), DefaultMemberComparer);
1105         }
1106         
1107         private static bool MemberDocsHaveUserContent (XmlNode e)
1108         {
1109                 e = (XmlElement)e.SelectSingleNode("Docs");
1110                 if (e == null) return false;
1111                 foreach (XmlElement d in e.SelectNodes("*"))
1112                         if (d.InnerText != "" && !d.InnerText.StartsWith("To be added"))
1113                                 return true;
1114                 return false;
1115         }
1116         
1117         // UPDATE HELPER FUNCTIONS
1118         
1119         // CREATE A STUB DOCUMENTATION FILE     
1120
1121         public XmlElement StubType (TypeDefinition type, string output)
1122         {
1123                 string typesig = typeFormatters [0].GetDeclaration (type);
1124                 if (typesig == null) return null; // not publicly visible
1125                 
1126                 XmlDocument doc = new XmlDocument();
1127                 XmlElement root = doc.CreateElement("Type");
1128                 doc.AppendChild (root);
1129
1130                 DoUpdateType2 ("New Type", doc, type, output, true);
1131                 
1132                 return root;
1133         }
1134
1135         private XmlElement CreateSinceNode (XmlDocument doc)
1136         {
1137                 XmlElement s = doc.CreateElement ("since");
1138                 s.SetAttribute ("version", since);
1139                 return s;
1140         }
1141         
1142         // STUBBING/UPDATING FUNCTIONS
1143         
1144         public void UpdateType (XmlElement root, TypeDefinition type)
1145         {
1146                 root.SetAttribute("Name", GetDocTypeName (type));
1147                 root.SetAttribute("FullName", GetDocTypeFullName (type));
1148
1149                 foreach (MemberFormatter f in typeFormatters) {
1150                         string element = "TypeSignature[@Language='" + f.Language + "']";
1151                         WriteElementAttribute (root, element, "Language", f.Language);
1152                         WriteElementAttribute (root, element, "Value", f.GetDeclaration (type));
1153                 }
1154                 
1155                 XmlElement ass = WriteElement(root, "AssemblyInfo");
1156                 WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
1157                 if (!no_assembly_versions) {
1158                         UpdateAssemblyVersions (root, type, true);
1159                 }
1160                 else {
1161                         var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
1162                         foreach (var version in versions)
1163                                 ass.RemoveChild (version);
1164                 }
1165                 if (!string.IsNullOrEmpty (type.Module.Assembly.Name.Culture))
1166                         WriteElementText(ass, "AssemblyCulture", type.Module.Assembly.Name.Culture);
1167                 else
1168                         ClearElement(ass, "AssemblyCulture");
1169                 
1170                 // Why-oh-why do we put assembly attributes in each type file?
1171                 // Neither monodoc nor monodocs2html use them, so I'm deleting them
1172                 // since they're outdated in current docs, and a waste of space.
1173                 //MakeAttributes(ass, type.Assembly, true);
1174                 XmlNode assattrs = ass.SelectSingleNode("Attributes");
1175                 if (assattrs != null)
1176                         ass.RemoveChild(assattrs);
1177                 
1178                 NormalizeWhitespace(ass);
1179                 
1180                 if (type.IsGenericType ()) {
1181                         MakeTypeParameters (root, type.GenericParameters);
1182                 } else {
1183                         ClearElement(root, "TypeParameters");
1184                 }
1185                 
1186                 if (type.BaseType != null) {
1187                         XmlElement basenode = WriteElement(root, "Base");
1188                         
1189                         string basetypename = GetDocTypeFullName (type.BaseType);
1190                         if (basetypename == "System.MulticastDelegate") basetypename = "System.Delegate";
1191                         WriteElementText(root, "Base/BaseTypeName", basetypename);
1192                         
1193                         // Document how this type instantiates the generic parameters of its base type
1194                         TypeReference origBase = type.BaseType.GetElementType ();
1195                         if (origBase.IsGenericType ()) {
1196                                 ClearElement(basenode, "BaseTypeArguments");
1197                                 GenericInstanceType baseInst             = type.BaseType as GenericInstanceType;
1198                                 IList<TypeReference> baseGenArgs    = baseInst == null ? null : baseInst.GenericArguments;
1199                                 IList<GenericParameter> baseGenParams = origBase.GenericParameters;
1200                                 if (baseGenArgs.Count != baseGenParams.Count)
1201                                         throw new InvalidOperationException ("internal error: number of generic arguments doesn't match number of generic parameters.");
1202                                 for (int i = 0; baseGenArgs != null && i < baseGenArgs.Count; i++) {
1203                                         GenericParameter param = baseGenParams [i];
1204                                         TypeReference    value = baseGenArgs [i];
1205
1206                                         XmlElement bta = WriteElement(basenode, "BaseTypeArguments");
1207                                         XmlElement arg = bta.OwnerDocument.CreateElement("BaseTypeArgument");
1208                                         bta.AppendChild(arg);
1209                                         arg.SetAttribute ("TypeParamName", param.Name);
1210                                         arg.InnerText = GetDocTypeFullName (value);
1211                                 }
1212                         }
1213                 } else {
1214                         ClearElement(root, "Base");
1215                 }
1216
1217                 if (!DocUtils.IsDelegate (type) && !type.IsEnum) {
1218                         IEnumerable<TypeReference> userInterfaces = DocUtils.GetUserImplementedInterfaces (type);
1219                         List<string> interface_names = userInterfaces
1220                                         .Select (iface => GetDocTypeFullName (iface))
1221                                         .OrderBy (s => s)
1222                                         .ToList ();
1223
1224                         XmlElement interfaces = WriteElement(root, "Interfaces");
1225                         interfaces.RemoveAll();
1226                         foreach (string iname in interface_names) {
1227                                 XmlElement iface = root.OwnerDocument.CreateElement("Interface");
1228                                 interfaces.AppendChild(iface);
1229                                 WriteElementText(iface, "InterfaceName", iname);
1230                         }
1231                 } else {
1232                         ClearElement(root, "Interfaces");
1233                 }
1234
1235                 MakeAttributes (root, GetCustomAttributes (type));
1236                 
1237                 if (DocUtils.IsDelegate (type)) {
1238                         MakeTypeParameters (root, type.GenericParameters);
1239                         MakeParameters(root, type.GetMethod("Invoke").Parameters);
1240                         MakeReturnValue(root, type.GetMethod("Invoke"));
1241                 }
1242                 
1243                 DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
1244                 MakeDocNode (typeInfo);
1245                 
1246                 if (!DocUtils.IsDelegate (type))
1247                         WriteElement (root, "Members");
1248
1249                 OrderTypeNodes (root, root.ChildNodes);
1250                 NormalizeWhitespace(root);
1251         }
1252
1253         static readonly string[] TypeNodeOrder = {
1254                 "TypeSignature",
1255                 "MemberOfLibrary",
1256                 "AssemblyInfo",
1257                 "ThreadingSafetyStatement",
1258                 "ThreadSafetyStatement",
1259                 "TypeParameters",
1260                 "Base",
1261                 "Interfaces",
1262                 "Attributes",
1263                 "Parameters",
1264                 "ReturnValue",
1265                 "Docs",
1266                 "Members",
1267                 "TypeExcluded",
1268         };
1269
1270         static void OrderTypeNodes (XmlNode member, XmlNodeList children)
1271         {
1272                 ReorderNodes (member, children, TypeNodeOrder);
1273         }
1274
1275         internal static IEnumerable<T> Sort<T> (IEnumerable<T> list)
1276         {
1277                 List<T> l = new List<T> (list);
1278                 l.Sort ();
1279                 return l;
1280         }
1281
1282         private void UpdateMember (DocsNodeInfo info)
1283         {
1284                 XmlElement me = (XmlElement) info.Node;
1285                 MemberReference mi = info.Member;
1286
1287                 foreach (MemberFormatter f in memberFormatters) {
1288                         string element = "MemberSignature[@Language='" + f.Language + "']";
1289                         WriteElementAttribute (me, element, "Language", f.Language);
1290                         WriteElementAttribute (me, element, "Value", f.GetDeclaration (mi));
1291                 }
1292
1293                 WriteElementText(me, "MemberType", GetMemberType(mi));
1294                 
1295                 if (!no_assembly_versions) {
1296                         UpdateAssemblyVersions (me, mi, true);
1297                 }
1298                 else {
1299                         ClearElement (me, "AssemblyInfo");
1300                 }
1301
1302                 MakeAttributes (me, GetCustomAttributes (mi));
1303
1304                 MakeReturnValue(me, mi);
1305                 if (mi is MethodReference) {
1306                         MethodReference mb = (MethodReference) mi;
1307                         if (mb.IsGenericMethod ())
1308                                 MakeTypeParameters (me, mb.GenericParameters);
1309                 }
1310                 MakeParameters(me, mi);
1311                 
1312                 string fieldValue;
1313                 if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
1314                         WriteElementText(me, "MemberValue", fieldValue);
1315                 
1316                 info.Node = WriteElement (me, "Docs");
1317                 MakeDocNode (info);
1318                 OrderMemberNodes (me, me.ChildNodes);
1319                 UpdateExtensionMethods (me, info);
1320         }
1321
1322         static readonly string[] MemberNodeOrder = {
1323                 "MemberSignature",
1324                 "MemberType",
1325                 "AssemblyInfo",
1326                 "Attributes",
1327                 "ReturnValue",
1328                 "TypeParameters",
1329                 "Parameters",
1330                 "MemberValue",
1331                 "Docs",
1332                 "Excluded",
1333                 "ExcludedLibrary",
1334                 "Link",
1335         };
1336
1337         static void OrderMemberNodes (XmlNode member, XmlNodeList children)
1338         {
1339                 ReorderNodes (member, children, MemberNodeOrder);
1340         }
1341
1342         static void ReorderNodes (XmlNode node, XmlNodeList children, string[] ordering)
1343         {
1344                 MyXmlNodeList newChildren = new MyXmlNodeList (children.Count);
1345                 for (int i = 0; i < ordering.Length; ++i) {
1346                         for (int j = 0; j < children.Count; ++j) {
1347                                 XmlNode c = children [j];
1348                                 if (c.Name == ordering [i]) {
1349                                         newChildren.Add (c);
1350                                 }
1351                         }
1352                 }
1353                 if (newChildren.Count >= 0)
1354                         node.PrependChild ((XmlNode) newChildren [0]);
1355                 for (int i = 1; i < newChildren.Count; ++i) {
1356                         XmlNode prev = (XmlNode) newChildren [i-1];
1357                         XmlNode cur  = (XmlNode) newChildren [i];
1358                         node.RemoveChild (cur);
1359                         node.InsertAfter (cur, prev);
1360                 }
1361         }
1362
1363         IEnumerable<string> GetCustomAttributes (MemberReference mi)
1364         {
1365                 IEnumerable<string> attrs = Enumerable.Empty<string>();
1366
1367                 ICustomAttributeProvider p = mi as ICustomAttributeProvider;
1368                 if (p != null)
1369                         attrs = attrs.Concat (GetCustomAttributes (p.CustomAttributes, ""));
1370
1371                 PropertyDefinition pd = mi as PropertyDefinition;
1372                 if (pd != null) {
1373                         if (pd.GetMethod != null)
1374                                 attrs = attrs.Concat (GetCustomAttributes (pd.GetMethod.CustomAttributes, "get: "));
1375                         if (pd.SetMethod != null)
1376                                 attrs = attrs.Concat (GetCustomAttributes (pd.SetMethod.CustomAttributes, "set: "));
1377                 }
1378
1379                 EventDefinition ed = mi as EventDefinition;
1380                 if (ed != null) {
1381                         if (ed.AddMethod != null)
1382                                 attrs = attrs.Concat (GetCustomAttributes (ed.AddMethod.CustomAttributes, "add: "));
1383                         if (ed.RemoveMethod != null)
1384                                 attrs = attrs.Concat (GetCustomAttributes (ed.RemoveMethod.CustomAttributes, "remove: "));
1385                 }
1386
1387                 return attrs;
1388         }
1389
1390         IEnumerable<string> GetCustomAttributes (IList<CustomAttribute> attributes, string prefix)
1391         {
1392                 foreach (CustomAttribute attribute in attributes.OrderBy (ca => ca.AttributeType.FullName)) {
1393
1394                         TypeDefinition attrType = attribute.AttributeType as TypeDefinition;
1395                         if (attrType != null && !IsPublic (attrType))
1396                                 continue;
1397                         if (slashdocFormatter.GetName (attribute.AttributeType) == null)
1398                                 continue;
1399                         
1400                         if (Array.IndexOf (IgnorableAttributes, attribute.AttributeType.FullName) >= 0)
1401                                 continue;
1402                         
1403                         StringList fields = new StringList ();
1404
1405                         for (int i = 0; i < attribute.ConstructorArguments.Count; ++i) {
1406                                 CustomAttributeArgument argument = attribute.ConstructorArguments [i];
1407                                 fields.Add (MakeAttributesValueString (
1408                                                 argument.Value,
1409                                                 argument.Type));
1410                         }
1411                         var namedArgs =
1412                                 (from namedArg in attribute.Fields
1413                                  select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value })
1414                                 .Concat (
1415                                                 (from namedArg in attribute.Properties
1416                                                  select new { Type=namedArg.Argument.Type, Name=namedArg.Name, Value=namedArg.Argument.Value }))
1417                                 .OrderBy (v => v.Name);
1418                         foreach (var d in namedArgs)
1419                                 fields.Add (string.Format ("{0}={1}", d.Name, 
1420                                                 MakeAttributesValueString (d.Value, d.Type)));
1421
1422                         string a2 = String.Join(", ", fields.ToArray ());
1423                         if (a2 != "") a2 = "(" + a2 + ")";
1424
1425                         string name = attribute.GetDeclaringType();
1426                         if (name.EndsWith("Attribute")) name = name.Substring(0, name.Length-"Attribute".Length);
1427                         yield return prefix + name + a2;
1428                 }
1429         }
1430
1431         static readonly string[] ValidExtensionMembers = {
1432                 "Docs",
1433                 "MemberSignature",
1434                 "MemberType",
1435                 "Parameters",
1436                 "ReturnValue",
1437                 "TypeParameters",
1438         };
1439
1440         static readonly string[] ValidExtensionDocMembers = {
1441                 "param",
1442                 "summary",
1443                 "typeparam",
1444         };
1445
1446         private void UpdateExtensionMethods (XmlElement e, DocsNodeInfo info)
1447         {
1448                 MethodDefinition me = info.Member as MethodDefinition;
1449                 if (me == null)
1450                         return;
1451                 if (info.Parameters.Count < 1)
1452                         return;
1453                 if (!DocUtils.IsExtensionMethod (me))
1454                         return;
1455
1456                 XmlNode em = e.OwnerDocument.CreateElement ("ExtensionMethod");
1457                 XmlNode member = e.CloneNode (true);
1458                 em.AppendChild (member);
1459                 RemoveExcept (member, ValidExtensionMembers);
1460                 RemoveExcept (member.SelectSingleNode ("Docs"), ValidExtensionDocMembers);
1461                 WriteElementText (member, "MemberType", "ExtensionMethod");
1462                 XmlElement link = member.OwnerDocument.CreateElement ("Link");
1463                 link.SetAttribute ("Type", slashdocFormatter.GetName (me.DeclaringType));
1464                 link.SetAttribute ("Member", slashdocFormatter.GetDeclaration (me));
1465                 member.AppendChild (link);
1466                 AddTargets (em, info);
1467
1468                 extensionMethods.Add (em);
1469         }
1470
1471         private static void RemoveExcept (XmlNode node, string[] except)
1472         {
1473                 if (node == null)
1474                         return;
1475                 MyXmlNodeList remove = null;
1476                 foreach (XmlNode n in node.ChildNodes) {
1477                         if (Array.BinarySearch (except, n.Name) < 0) {
1478                                 if (remove == null)
1479                                         remove = new MyXmlNodeList ();
1480                                 remove.Add (n);
1481                         }
1482                 }
1483                 if (remove != null)
1484                         foreach (XmlNode n in remove)
1485                                 node.RemoveChild (n);
1486         }
1487
1488         private static void AddTargets (XmlNode member, DocsNodeInfo info)
1489         {
1490                 XmlElement targets = member.OwnerDocument.CreateElement ("Targets");
1491                 member.PrependChild (targets);
1492                 if (!(info.Parameters [0].ParameterType is GenericParameter)) {
1493                         AppendElementAttributeText (targets, "Target", "Type",
1494                                 slashdocFormatter.GetDeclaration (info.Parameters [0].ParameterType));
1495                 }
1496                 else {
1497                         GenericParameter gp = (GenericParameter) info.Parameters [0].ParameterType;
1498                         IList<TypeReference> constraints = gp.Constraints;
1499                         if (constraints.Count == 0)
1500                                 AppendElementAttributeText (targets, "Target", "Type", "System.Object");
1501                         else
1502                                 foreach (TypeReference c in constraints)
1503                                         AppendElementAttributeText(targets, "Target", "Type",
1504                                                 slashdocFormatter.GetDeclaration (c));
1505                 }
1506         }
1507         
1508         private static bool GetFieldConstValue (FieldDefinition field, out string value)
1509         {
1510                 value = null;
1511                 TypeDefinition type = field.DeclaringType.Resolve ();
1512                 if (type != null && type.IsEnum) return false;
1513                 
1514                 if (type != null && type.IsGenericType ()) return false;
1515                 if (!field.HasConstant)
1516                         return false;
1517                 if (field.IsLiteral) {
1518                         object val = field.Constant;
1519                         if (val == null) value = "null";
1520                         else if (val is Enum) value = val.ToString();
1521                         else if (val is IFormattable) {
1522                                 value = ((IFormattable)val).ToString();
1523                                 if (val is string)
1524                                         value = "\"" + value + "\"";
1525                         }
1526                         if (value != null && value != "")
1527                                 return true;
1528                 }
1529                 return false;
1530         }
1531         
1532         // XML HELPER FUNCTIONS
1533         
1534         internal static XmlElement WriteElement(XmlNode parent, string element) {
1535                 XmlElement ret = (XmlElement)parent.SelectSingleNode(element);
1536                 if (ret == null) {
1537                         string[] path = element.Split('/');
1538                         foreach (string p in path) {
1539                                 ret = (XmlElement)parent.SelectSingleNode(p);
1540                                 if (ret == null) {
1541                                         string ename = p;
1542                                         if (ename.IndexOf('[') >= 0) // strip off XPath predicate
1543                                                 ename = ename.Substring(0, ename.IndexOf('['));
1544                                         ret = parent.OwnerDocument.CreateElement(ename);
1545                                         parent.AppendChild(ret);
1546                                         parent = ret;
1547                                 } else {
1548                                         parent = ret;
1549                                 }
1550                         }
1551                 }
1552                 return ret;
1553         }
1554         private static void WriteElementText(XmlNode parent, string element, string value) {
1555                 XmlElement node = WriteElement(parent, element);
1556                 node.InnerText = value;
1557         }
1558
1559         static XmlElement AppendElementText (XmlNode parent, string element, string value)
1560         {
1561                 XmlElement n = parent.OwnerDocument.CreateElement (element);
1562                 parent.AppendChild (n);
1563                 n.InnerText = value;
1564                 return n;
1565         }
1566
1567         static XmlElement AppendElementAttributeText (XmlNode parent, string element, string attribute, string value)
1568         {
1569                 XmlElement n = parent.OwnerDocument.CreateElement (element);
1570                 parent.AppendChild (n);
1571                 n.SetAttribute (attribute, value);
1572                 return n;
1573         }
1574
1575         internal static XmlNode CopyNode (XmlNode source, XmlNode dest)
1576         {
1577                 XmlNode copy = dest.OwnerDocument.ImportNode (source, true);
1578                 dest.AppendChild (copy);
1579                 return copy;
1580         }
1581
1582         private static void WriteElementInitialText(XmlElement parent, string element, string value) {
1583                 XmlElement node = (XmlElement)parent.SelectSingleNode(element);
1584                 if (node != null)
1585                         return;
1586                 node = WriteElement(parent, element);
1587                 node.InnerText = value;
1588         }
1589         private static void WriteElementAttribute(XmlElement parent, string element, string attribute, string value) {
1590                 XmlElement node = WriteElement(parent, element);
1591                 if (node.GetAttribute(attribute) == value) return;
1592                 node.SetAttribute(attribute, value);
1593         }
1594         internal static void ClearElement(XmlElement parent, string name) {
1595                 XmlElement node = (XmlElement)parent.SelectSingleNode(name);
1596                 if (node != null)
1597                         parent.RemoveChild(node);
1598         }
1599         
1600         // DOCUMENTATION HELPER FUNCTIONS
1601         
1602         private void MakeDocNode (DocsNodeInfo info)
1603         {
1604                 List<GenericParameter> genericParams      = info.GenericParameters;
1605                 IList<ParameterDefinition> parameters  = info.Parameters;
1606                 TypeReference returntype                  = info.ReturnType;
1607                 bool returnisreturn         = info.ReturnIsReturn;
1608                 XmlElement e                = info.Node;
1609                 bool addremarks             = info.AddRemarks;
1610
1611                 WriteElementInitialText(e, "summary", "To be added.");
1612                 
1613                 if (parameters != null) {
1614                         string[] values = new string [parameters.Count];
1615                         for (int i = 0; i < values.Length; ++i)
1616                                 values [i] = parameters [i].Name;
1617                         UpdateParameters (e, "param", values);
1618                 }
1619
1620                 if (genericParams != null) {
1621                         string[] values = new string [genericParams.Count];
1622                         for (int i = 0; i < values.Length; ++i)
1623                                 values [i] = genericParams [i].Name;
1624                         UpdateParameters (e, "typeparam", values);
1625                 }
1626
1627                 string retnodename = null;
1628                 if (returntype != null && returntype.FullName != "System.Void") { // FIXME
1629                         retnodename = returnisreturn ? "returns" : "value";
1630                         string retnodename_other = !returnisreturn ? "returns" : "value";
1631                         
1632                         // If it has a returns node instead of a value node, change its name.
1633                         XmlElement retother = (XmlElement)e.SelectSingleNode(retnodename_other);
1634                         if (retother != null) {
1635                                 XmlElement retnode = e.OwnerDocument.CreateElement(retnodename);
1636                                 foreach (XmlNode node in retother)
1637                                         retnode.AppendChild(node.CloneNode(true));
1638                                 e.ReplaceChild(retnode, retother);
1639                         } else {
1640                                 WriteElementInitialText(e, retnodename, "To be added.");
1641                         }
1642                 } else {
1643                         ClearElement(e, "returns");
1644                         ClearElement(e, "value");
1645                 }
1646
1647                 if (addremarks)
1648                         WriteElementInitialText(e, "remarks", "To be added.");
1649
1650                 if (exceptions.HasValue && info.Member != null &&
1651                                 (exceptions.Value & ExceptionLocations.AddedMembers) == 0) {
1652                         UpdateExceptions (e, info.Member);
1653                 }
1654
1655                 foreach (DocumentationImporter importer in importers)
1656                         importer.ImportDocumentation (info);
1657                 
1658                 OrderDocsNodes (e, e.ChildNodes);
1659                 NormalizeWhitespace(e);
1660         }
1661
1662         static readonly string[] DocsNodeOrder = {
1663                 "typeparam", "param", "summary", "returns", "value", "remarks",
1664         };
1665
1666         private static void OrderDocsNodes (XmlNode docs, XmlNodeList children)
1667         {
1668                 ReorderNodes (docs, children, DocsNodeOrder);
1669         }
1670         
1671
1672         private void UpdateParameters (XmlElement e, string element, string[] values)
1673         {       
1674                 if (values != null) {
1675                         XmlNode[] paramnodes = new XmlNode[values.Length];
1676                         
1677                         // Some documentation had param nodes with leading spaces.
1678                         foreach (XmlElement paramnode in e.SelectNodes(element)){
1679                                 paramnode.SetAttribute("name", paramnode.GetAttribute("name").Trim());
1680                         }
1681                         
1682                         // If a member has only one parameter, we can track changes to
1683                         // the name of the parameter easily.
1684                         if (values.Length == 1 && e.SelectNodes(element).Count == 1) {
1685                                 UpdateParameterName (e, (XmlElement) e.SelectSingleNode(element), values [0]);
1686                         }
1687
1688                         bool reinsert = false;
1689
1690                         // Pick out existing and still-valid param nodes, and
1691                         // create nodes for parameters not in the file.
1692                         Hashtable seenParams = new Hashtable();
1693                         for (int pi = 0; pi < values.Length; pi++) {
1694                                 string p = values [pi];
1695                                 seenParams[p] = pi;
1696                                 
1697                                 paramnodes[pi] = e.SelectSingleNode(element + "[@name='" + p + "']");
1698                                 if (paramnodes[pi] != null) continue;
1699                                 
1700                                 XmlElement pe = e.OwnerDocument.CreateElement(element);
1701                                 pe.SetAttribute("name", p);
1702                                 pe.InnerText = "To be added.";
1703                                 paramnodes[pi] = pe;
1704                                 reinsert = true;
1705                         }
1706
1707                         // Remove parameters that no longer exist and check all params are in the right order.
1708                         int idx = 0;
1709                         MyXmlNodeList todelete = new MyXmlNodeList ();
1710                         foreach (XmlElement paramnode in e.SelectNodes(element)) {
1711                                 string name = paramnode.GetAttribute("name");
1712                                 if (!seenParams.ContainsKey(name)) {
1713                                         if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1714                                                 Warning ("The following param node can only be deleted if the --delete option is given: ");
1715                                                 if (e.ParentNode == e.OwnerDocument.DocumentElement) {
1716                                                         // delegate type
1717                                                         Warning ("\tXPath=/Type[@FullName=\"{0}\"]/Docs/param[@name=\"{1}\"]",
1718                                                                         e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1719                                                                         name);
1720                                                 }
1721                                                 else {
1722                                                         Warning ("\tXPath=/Type[@FullName=\"{0}\"]//Member[@MemberName=\"{1}\"]/Docs/param[@name=\"{2}\"]",
1723                                                                         e.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
1724                                                                         e.ParentNode.Attributes ["MemberName"].Value, 
1725                                                                         name);
1726                                                 }
1727                                                 Warning ("\tValue={0}", paramnode.OuterXml);
1728                                         } else {
1729                                                 todelete.Add (paramnode);
1730                                         }
1731                                         continue;
1732                                 }
1733
1734                                 if ((int)seenParams[name] != idx)
1735                                         reinsert = true;
1736                                 
1737                                 idx++;
1738                         }
1739
1740                         foreach (XmlNode n in todelete) {
1741                                 n.ParentNode.RemoveChild (n);
1742                         }
1743                         
1744                         // Re-insert the parameter nodes at the top of the doc section.
1745                         if (reinsert)
1746                                 for (int pi = values.Length-1; pi >= 0; pi--)
1747                                         e.PrependChild(paramnodes[pi]);
1748                 } else {
1749                         // Clear all existing param nodes
1750                         foreach (XmlNode paramnode in e.SelectNodes(element)) {
1751                                 if (!delete && !paramnode.InnerText.StartsWith("To be added")) {
1752                                         Console.WriteLine("The following param node can only be deleted if the --delete option is given:");
1753                                         Console.WriteLine(paramnode.OuterXml);
1754                                 } else {
1755                                         paramnode.ParentNode.RemoveChild(paramnode);
1756                                 }
1757                         }
1758                 }
1759         }
1760
1761         private static void UpdateParameterName (XmlElement docs, XmlElement pe, string newName)
1762         {
1763                 string existingName = pe.GetAttribute ("name");
1764                 pe.SetAttribute ("name", newName);
1765                 if (existingName == newName)
1766                         return;
1767                 foreach (XmlElement paramref in docs.SelectNodes (".//paramref"))
1768                         if (paramref.GetAttribute ("name").Trim () == existingName)
1769                                 paramref.SetAttribute ("name", newName);
1770         }
1771
1772         class CrefComparer : XmlNodeComparer {
1773
1774                 public CrefComparer ()
1775                 {
1776                 }
1777
1778                 public override int Compare (XmlNode x, XmlNode y)
1779                 {
1780                         string xType = x.Attributes ["cref"].Value;
1781                         string yType = y.Attributes ["cref"].Value;
1782                         string xNamespace = GetNamespace (xType);
1783                         string yNamespace = GetNamespace (yType);
1784
1785                         int c = xNamespace.CompareTo (yNamespace);
1786                         if (c != 0)
1787                                 return c;
1788                         return xType.CompareTo (yType);
1789                 }
1790
1791                 static string GetNamespace (string type)
1792                 {
1793                         int n = type.LastIndexOf ('.');
1794                         if (n >= 0)
1795                                 return type.Substring (0, n);
1796                         return string.Empty;
1797                 }
1798         }
1799         
1800         private void UpdateExceptions (XmlNode docs, MemberReference member)
1801         {
1802                 foreach (var source in new ExceptionLookup (exceptions.Value)[member]) {
1803                         string cref = slashdocFormatter.GetDeclaration (source.Exception);
1804                         var node = docs.SelectSingleNode ("exception[@cref='" + cref + "']");
1805                         if (node != null)
1806                                 continue;
1807                         XmlElement e = docs.OwnerDocument.CreateElement ("exception");
1808                         e.SetAttribute ("cref", cref);
1809                         e.InnerXml = "To be added; from: <see cref=\"" + 
1810                                 string.Join ("\" />, <see cref=\"", 
1811                                                 source.Sources.Select (m => slashdocFormatter.GetDeclaration (m))
1812                                                 .ToArray ()) +
1813                                 "\" />";
1814                         docs.AppendChild (e);
1815                 }
1816                 SortXmlNodes (docs, docs.SelectNodes ("exception"), 
1817                                 new CrefComparer ());
1818         }
1819
1820         private static void NormalizeWhitespace(XmlElement e) {
1821                 // Remove all text and whitespace nodes from the element so it
1822                 // is outputted with nice indentation and no blank lines.
1823                 ArrayList deleteNodes = new ArrayList();
1824                 foreach (XmlNode n in e)
1825                         if (n is XmlText || n is XmlWhitespace || n is XmlSignificantWhitespace)
1826                                 deleteNodes.Add(n);
1827                 foreach (XmlNode n in deleteNodes)
1828                                 n.ParentNode.RemoveChild(n);
1829         }
1830         
1831         private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
1832         {
1833                 TypeDefinition type = member as TypeDefinition;
1834                 if (type == null)
1835                         type = member.DeclaringType as TypeDefinition;
1836                 return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
1837         }
1838         
1839         private static string GetAssemblyVersion (AssemblyDefinition assembly)
1840         {
1841                 return assembly.Name.Version.ToString();
1842         }
1843         
1844         private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
1845         {
1846                 XmlElement e = (XmlElement) root.SelectSingleNode ("AssemblyInfo");
1847                 if (e == null) {
1848                         e = root.OwnerDocument.CreateElement("AssemblyInfo");
1849                         root.AppendChild(e);
1850                 }
1851                 List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
1852                         .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
1853                         .ToList ();
1854                 // matches.Count > 0 && add: ignore -- already present
1855                 if (matches.Count > 0 && !add) {
1856                         foreach (XmlNode c in matches)
1857                                 e.RemoveChild (c);
1858                 }
1859                 else if (matches.Count == 0 && add) {
1860                         foreach (string sv in assemblyVersions) {
1861                                 XmlElement c = root.OwnerDocument.CreateElement("AssemblyVersion");
1862                                 c.InnerText = sv;
1863                                 e.AppendChild(c);
1864                         }
1865                 }
1866                 // matches.Count == 0 && !add: ignore -- already not present
1867
1868                 XmlNodeList avs = e.SelectNodes ("AssemblyVersion");
1869                 SortXmlNodes (e, avs, new VersionComparer ());
1870
1871                 return avs.Count != 0;
1872         }
1873
1874         // FIXME: get TypeReferences instead of string comparison?
1875         private static string[] IgnorableAttributes = {
1876                 // Security related attributes
1877                 "System.Reflection.AssemblyKeyFileAttribute",
1878                 "System.Reflection.AssemblyDelaySignAttribute",
1879                 // Present in @RefType
1880                 "System.Runtime.InteropServices.OutAttribute",
1881                 // For naming the indexer to use when not using indexers
1882                 "System.Reflection.DefaultMemberAttribute",
1883                 // for decimal constants
1884                 "System.Runtime.CompilerServices.DecimalConstantAttribute",
1885                 // compiler generated code
1886                 "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
1887                 // more compiler generated code, e.g. iterator methods
1888                 "System.Diagnostics.DebuggerHiddenAttribute",
1889                 "System.Runtime.CompilerServices.FixedBufferAttribute",
1890                 "System.Runtime.CompilerServices.UnsafeValueTypeAttribute",
1891                 // extension methods
1892                 "System.Runtime.CompilerServices.ExtensionAttribute",
1893                 // Used to differentiate 'object' from C#4 'dynamic'
1894                 "System.Runtime.CompilerServices.DynamicAttribute",
1895         };
1896
1897         private void MakeAttributes (XmlElement root, IEnumerable<string> attributes)
1898         {
1899                 if (!attributes.Any ()) {
1900                         ClearElement (root, "Attributes");
1901                         return;
1902                 }
1903
1904                 XmlElement e = (XmlElement)root.SelectSingleNode("Attributes");
1905                 if (e != null)
1906                         e.RemoveAll();
1907                 else if (e == null)
1908                         e = root.OwnerDocument.CreateElement("Attributes");
1909                 
1910                 foreach (string attribute in attributes) {
1911                         XmlElement ae = root.OwnerDocument.CreateElement("Attribute");
1912                         e.AppendChild(ae);
1913                         
1914                         WriteElementText(ae, "AttributeName", attribute);
1915                 }
1916                 
1917                 if (e.ParentNode == null)
1918                         root.AppendChild(e);
1919
1920                 NormalizeWhitespace(e);
1921         }
1922
1923         private static string MakeAttributesValueString (object v, TypeReference valueType)
1924         {
1925                 if (v == null)
1926                         return "null";
1927                 if (valueType.FullName == "System.Type")
1928                         return "typeof(" + v.ToString () + ")";
1929                 if (valueType.FullName == "System.String")
1930                         return "\"" + v.ToString () + "\"";
1931                 if (v is Boolean)
1932                         return (bool)v ? "true" : "false";
1933                 TypeDefinition valueDef = valueType.Resolve ();
1934                 if (valueDef == null || !valueDef.IsEnum)
1935                         return v.ToString ();
1936                 string typename = GetDocTypeFullName (valueType);
1937                 var values = GetEnumerationValues (valueDef);
1938                 long c = ToInt64 (v);
1939                 if (values.ContainsKey (c))
1940                         return typename + "." + values [c];
1941                 if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
1942                         return string.Join (" | ",
1943                                         (from i in values.Keys
1944                                          where (c & i) != 0
1945                                          select typename + "." + values [i])
1946                                         .ToArray ());
1947                 }
1948                 return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
1949         }
1950
1951         private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
1952         {
1953                 var values = new Dictionary<long, string> ();
1954                 foreach (var f in 
1955                                 (from f in type.Fields
1956                                  where !(f.IsRuntimeSpecialName || f.IsSpecialName)
1957                                  select f)) {
1958                         values [ToInt64 (f.Constant)] = f.Name;
1959                 }
1960                 return values;
1961         }
1962
1963         static long ToInt64 (object value)
1964         {
1965                 if (value is ulong)
1966                         return (long) (ulong) value;
1967                 return Convert.ToInt64 (value);
1968         }
1969         
1970         private void MakeParameters (XmlElement root, IList<ParameterDefinition> parameters)
1971         {
1972                 XmlElement e = WriteElement(root, "Parameters");
1973                 e.RemoveAll();
1974                 foreach (ParameterDefinition p in parameters) {
1975                         XmlElement pe = root.OwnerDocument.CreateElement("Parameter");
1976                         e.AppendChild(pe);
1977                         pe.SetAttribute("Name", p.Name);
1978                         pe.SetAttribute("Type", GetDocParameterType (p.ParameterType));
1979                         if (p.ParameterType is ByReferenceType) {
1980                                 if (p.IsOut) pe.SetAttribute("RefType", "out");
1981                                 else pe.SetAttribute("RefType", "ref");
1982                         }
1983                         MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
1984                 }
1985         }
1986         
1987         private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams)
1988         {
1989                 if (typeParams == null || typeParams.Count == 0) {
1990                         XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
1991                         if (f != null)
1992                                 root.RemoveChild (f);
1993                         return;
1994                 }
1995                 XmlElement e = WriteElement(root, "TypeParameters");
1996                 e.RemoveAll();
1997                 foreach (GenericParameter t in typeParams) {
1998                         XmlElement pe = root.OwnerDocument.CreateElement("TypeParameter");
1999                         e.AppendChild(pe);
2000                         pe.SetAttribute("Name", t.Name);
2001                         MakeAttributes (pe, GetCustomAttributes (t.CustomAttributes, ""));
2002                         XmlElement ce = (XmlElement) e.SelectSingleNode ("Constraints");
2003                         IList<TypeReference> constraints = t.Constraints;
2004                         GenericParameterAttributes attrs = t.Attributes;
2005                         if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) {
2006                                 if (ce != null)
2007                                         e.RemoveChild (ce);
2008                                 continue;
2009                         }
2010                         if (ce != null)
2011                                 ce.RemoveAll();
2012                         else {
2013                                 ce = root.OwnerDocument.CreateElement ("Constraints");
2014                         }
2015                         pe.AppendChild (ce);
2016                         if ((attrs & GenericParameterAttributes.Contravariant) != 0)
2017                                 AppendElementText (ce, "ParameterAttribute", "Contravariant");
2018                         if ((attrs & GenericParameterAttributes.Covariant) != 0)
2019                                 AppendElementText (ce, "ParameterAttribute", "Covariant");
2020                         if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
2021                                 AppendElementText (ce, "ParameterAttribute", "DefaultConstructorConstraint");
2022                         if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
2023                                 AppendElementText (ce, "ParameterAttribute", "NotNullableValueTypeConstraint");
2024                         if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
2025                                 AppendElementText (ce, "ParameterAttribute", "ReferenceTypeConstraint");
2026                         foreach (TypeReference c in constraints) {
2027                                 TypeDefinition cd = c.Resolve ();
2028                                 AppendElementText (ce,
2029                                                 (cd != null && cd.IsInterface) ? "InterfaceName" : "BaseTypeName",
2030                                                 GetDocTypeFullName (c));
2031                         }
2032                 }
2033         }
2034
2035         private void MakeParameters (XmlElement root, MemberReference mi)
2036         {
2037                 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2038                         MakeParameters (root, ((MethodDefinition)mi).Parameters);
2039                 else if (mi is MethodDefinition) {
2040                         MethodDefinition mb = (MethodDefinition) mi;
2041                         IList<ParameterDefinition> parameters = mb.Parameters;
2042                         MakeParameters(root, parameters);
2043                         if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
2044                                 XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
2045                                 p.SetAttribute ("RefType", "this");
2046                         }
2047                 }
2048                 else if (mi is PropertyDefinition) {
2049                         IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
2050                         if (parameters.Count > 0)
2051                                 MakeParameters(root, parameters);
2052                         else
2053                                 return;
2054                 }
2055                 else if (mi is FieldDefinition) return;
2056                 else if (mi is EventDefinition) return;
2057                 else throw new ArgumentException();
2058         }
2059
2060         internal static string GetDocParameterType (TypeReference type)
2061         {
2062                 return GetDocTypeFullName (type).Replace ("@", "&");
2063         }
2064
2065         private void MakeReturnValue (XmlElement root, TypeReference type, IList<CustomAttribute> attributes) 
2066         {
2067                 XmlElement e = WriteElement(root, "ReturnValue");
2068                 e.RemoveAll();
2069                 WriteElementText(e, "ReturnType", GetDocTypeFullName (type));
2070                 if (attributes != null)
2071                         MakeAttributes(e, GetCustomAttributes (attributes, ""));
2072         }
2073         
2074         private void MakeReturnValue (XmlElement root, MemberReference mi)
2075         {
2076                 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2077                         return;
2078                 else if (mi is MethodDefinition)
2079                         MakeReturnValue (root, ((MethodDefinition)mi).ReturnType, ((MethodDefinition)mi).MethodReturnType.CustomAttributes);
2080                 else if (mi is PropertyDefinition)
2081                         MakeReturnValue (root, ((PropertyDefinition)mi).PropertyType, null);
2082                 else if (mi is FieldDefinition)
2083                         MakeReturnValue (root, ((FieldDefinition)mi).FieldType, null);
2084                 else if (mi is EventDefinition)
2085                         MakeReturnValue (root, ((EventDefinition)mi).EventType, null);
2086                 else
2087                         throw new ArgumentException(mi + " is a " + mi.GetType().FullName);
2088         }
2089         
2090         private XmlElement MakeMember(XmlDocument doc, DocsNodeInfo info)
2091         {
2092                 MemberReference mi = info.Member;
2093                 if (mi is TypeDefinition) return null;
2094
2095                 string sigs = memberFormatters [0].GetDeclaration (mi);
2096                 if (sigs == null) return null; // not publicly visible
2097                 
2098                 // no documentation for property/event accessors.  Is there a better way of doing this?
2099                 if (mi.Name.StartsWith("get_")) return null;
2100                 if (mi.Name.StartsWith("set_")) return null;
2101                 if (mi.Name.StartsWith("add_")) return null;
2102                 if (mi.Name.StartsWith("remove_")) return null;
2103                 if (mi.Name.StartsWith("raise_")) return null;
2104                 
2105                 XmlElement me = doc.CreateElement("Member");
2106                 me.SetAttribute("MemberName", GetMemberName (mi));
2107                 
2108                 info.Node = me;
2109                 UpdateMember(info);
2110                 if (exceptions.HasValue && 
2111                                 (exceptions.Value & ExceptionLocations.AddedMembers) != 0)
2112                         UpdateExceptions (info.Node, info.Member);
2113
2114                 if (since != null) {
2115                         XmlNode docs = me.SelectSingleNode("Docs");
2116                         docs.AppendChild (CreateSinceNode (doc));
2117                 }
2118                 
2119                 return me;
2120         }
2121
2122         internal static string GetMemberName (MemberReference mi)
2123         {
2124                 MethodDefinition mb = mi as MethodDefinition;
2125                 if (mb == null) {
2126                         PropertyDefinition pi = mi as PropertyDefinition;
2127                         if (pi == null)
2128                                 return mi.Name;
2129                         return DocUtils.GetPropertyName (pi);
2130                 }
2131                 StringBuilder sb = new StringBuilder (mi.Name.Length);
2132                 if (!DocUtils.IsExplicitlyImplemented (mb))
2133                         sb.Append (mi.Name);
2134                 else {
2135                         TypeReference iface;
2136                         MethodReference ifaceMethod;
2137                         DocUtils.GetInfoForExplicitlyImplementedMethod (mb, out iface, out ifaceMethod);
2138                         sb.Append (GetDocTypeFullName (iface));
2139                         sb.Append ('.');
2140                         sb.Append (ifaceMethod.Name);
2141                 }
2142                 if (mb.IsGenericMethod ()) {
2143                         IList<GenericParameter> typeParams = mb.GenericParameters;
2144                         if (typeParams.Count > 0) {
2145                                 sb.Append ("<");
2146                                 sb.Append (typeParams [0].Name);
2147                                 for (int i = 1; i < typeParams.Count; ++i)
2148                                         sb.Append (",").Append (typeParams [i].Name);
2149                                 sb.Append (">");
2150                         }
2151                 }
2152                 return sb.ToString ();
2153         }
2154         
2155         /// SIGNATURE GENERATION FUNCTIONS
2156         internal static bool IsPrivate (MemberReference mi)
2157         {
2158                 return memberFormatters [0].GetDeclaration (mi) == null;
2159         }
2160
2161         internal static string GetMemberType (MemberReference mi)
2162         {
2163                 if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
2164                         return "Constructor";
2165                 if (mi is MethodDefinition)
2166                         return "Method";
2167                 if (mi is PropertyDefinition)
2168                         return "Property";
2169                 if (mi is FieldDefinition)
2170                         return "Field";
2171                 if (mi is EventDefinition)
2172                         return "Event";
2173                 throw new ArgumentException();
2174         }
2175
2176         private static string GetDocTypeName (TypeReference type)
2177         {
2178                 return docTypeFormatter.GetName (type);
2179         }
2180
2181         internal static string GetDocTypeFullName (TypeReference type)
2182         {
2183                 return DocTypeFullMemberFormatter.Default.GetName (type);
2184         }
2185
2186         internal static string GetXPathForMember (DocumentationMember member)
2187         {
2188                 StringBuilder xpath = new StringBuilder ();
2189                 xpath.Append ("//Members/Member[@MemberName=\"")
2190                         .Append (member.MemberName)
2191                         .Append ("\"]");
2192                 if (member.Parameters != null && member.Parameters.Count > 0) {
2193                         xpath.Append ("/Parameters[count(Parameter) = ")
2194                                 .Append (member.Parameters.Count);
2195                         for (int i = 0; i < member.Parameters.Count; ++i) {
2196                                 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2197                                 xpath.Append (member.Parameters [i]);
2198                                 xpath.Append ("\"");
2199                         }
2200                         xpath.Append ("]/..");
2201                 }
2202                 return xpath.ToString ();
2203         }
2204
2205         public static string GetXPathForMember (XPathNavigator member)
2206         {
2207                 StringBuilder xpath = new StringBuilder ();
2208                 xpath.Append ("//Type[@FullName=\"")
2209                         .Append (member.SelectSingleNode ("../../@FullName").Value)
2210                         .Append ("\"]/");
2211                 xpath.Append ("Members/Member[@MemberName=\"")
2212                         .Append (member.SelectSingleNode ("@MemberName").Value)
2213                         .Append ("\"]");
2214                 XPathNodeIterator parameters = member.Select ("Parameters/Parameter");
2215                 if (parameters.Count > 0) {
2216                         xpath.Append ("/Parameters[count(Parameter) = ")
2217                                 .Append (parameters.Count);
2218                         int i = 0;
2219                         while (parameters.MoveNext ()) {
2220                                 ++i;
2221                                 xpath.Append (" and Parameter [").Append (i).Append ("]/@Type=\"");
2222                                 xpath.Append (parameters.Current.Value);
2223                                 xpath.Append ("\"");
2224                         }
2225                         xpath.Append ("]/..");
2226                 }
2227                 return xpath.ToString ();
2228         }
2229
2230         public static string GetXPathForMember (MemberReference member)
2231         {
2232                 StringBuilder xpath = new StringBuilder ();
2233                 xpath.Append ("//Type[@FullName=\"")
2234                         .Append (member.DeclaringType.FullName)
2235                         .Append ("\"]/");
2236                 xpath.Append ("Members/Member[@MemberName=\"")
2237                         .Append (GetMemberName (member))
2238                         .Append ("\"]");
2239
2240                 IList<ParameterDefinition> parameters = null;
2241                 if (member is MethodDefinition)
2242                         parameters = ((MethodDefinition) member).Parameters;
2243                 else if (member is PropertyDefinition) {
2244                         parameters = ((PropertyDefinition) member).Parameters;
2245                 }
2246                 if (parameters != null && parameters.Count > 0) {
2247                         xpath.Append ("/Parameters[count(Parameter) = ")
2248                                 .Append (parameters.Count);
2249                         for (int i = 0; i < parameters.Count; ++i) {
2250                                 xpath.Append (" and Parameter [").Append (i+1).Append ("]/@Type=\"");
2251                                 xpath.Append (GetDocParameterType (parameters [i].ParameterType));
2252                                 xpath.Append ("\"");
2253                         }
2254                         xpath.Append ("]/..");
2255                 }
2256                 return xpath.ToString ();
2257         }
2258 }
2259
2260 static class CecilExtensions {
2261         public static string GetDeclaringType(this CustomAttribute attribute)
2262         {
2263                 return attribute.Constructor.DeclaringType.FullName;
2264         }
2265
2266         public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type)
2267         {
2268                 foreach (var c in type.Methods.Where (m => m.IsConstructor))
2269                         yield return (MemberReference) c;
2270                 foreach (var e in type.Events)
2271                         yield return (MemberReference) e;
2272                 foreach (var f in type.Fields)
2273                         yield return (MemberReference) f;
2274                 foreach (var m in type.Methods.Where (m => !m.IsConstructor))
2275                         yield return (MemberReference) m;
2276                 foreach (var t in type.NestedTypes)
2277                         yield return (MemberReference) t;
2278                 foreach (var p in type.Properties)
2279                         yield return (MemberReference) p;
2280         }
2281
2282         public static IEnumerable<MemberReference> GetMembers (this TypeDefinition type, string member)
2283         {
2284                 return GetMembers (type).Where (m => m.Name == member);
2285         }
2286
2287         public static MemberReference GetMember (this TypeDefinition type, string member)
2288         {
2289                 return GetMembers (type, member).EnsureZeroOrOne ();
2290         }
2291
2292         static T EnsureZeroOrOne<T> (this IEnumerable<T> source)
2293         {
2294                 if (source.Count () > 1)
2295                         throw new InvalidOperationException ("too many matches");
2296                 return source.FirstOrDefault ();
2297         }
2298
2299         public static MethodDefinition GetMethod (this TypeDefinition type, string method)
2300         {
2301                 return type.Methods
2302                         .Where (m => m.Name == method)
2303                         .EnsureZeroOrOne ();
2304         }
2305
2306         public static IEnumerable<MemberReference> GetDefaultMembers (this TypeReference type)
2307         {
2308                 TypeDefinition def = type as TypeDefinition;
2309                 if (def == null)
2310                         return new MemberReference [0];
2311                 CustomAttribute defMemberAttr = def.CustomAttributes
2312                                 .FirstOrDefault (c => c.AttributeType.FullName == "System.Reflection.DefaultMemberAttribute");
2313                 if (defMemberAttr == null)
2314                         return new MemberReference [0];
2315                 string name = (string) defMemberAttr.ConstructorArguments [0].Value;
2316                 return def.Properties
2317                                 .Where (p => p.Name == name)
2318                                 .Select (p => (MemberReference) p);
2319         }
2320
2321         public static IEnumerable<TypeDefinition> GetTypes (this AssemblyDefinition assembly)
2322         {
2323                 return assembly.Modules.SelectMany (md => md.GetAllTypes ());
2324         }
2325
2326         public static TypeDefinition GetType (this AssemblyDefinition assembly, string type)
2327         {
2328                 return GetTypes (assembly)
2329                         .Where (td => td.FullName == type)
2330                         .EnsureZeroOrOne ();
2331         }
2332
2333         public static bool IsGenericType (this TypeReference type)
2334         {
2335                 return type.GenericParameters.Count > 0;
2336         }
2337
2338         public static bool IsGenericMethod (this MethodReference method)
2339         {
2340                 return method.GenericParameters.Count > 0;
2341         }
2342
2343         public static MemberReference Resolve (this MemberReference member)
2344         {
2345                 FieldReference fr = member as FieldReference;
2346                 if (fr != null)
2347                         return fr.Resolve ();
2348                 MethodReference mr = member as MethodReference;
2349                 if (mr != null)
2350                         return mr.Resolve ();
2351                 TypeReference tr = member as TypeReference;
2352                 if (tr != null)
2353                         return tr.Resolve ();
2354                 PropertyReference pr = member as PropertyReference;
2355                 if (pr != null)
2356                         return pr;
2357                 EventReference er = member as EventReference;
2358                 if (er != null)
2359                         return er;
2360                 throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
2361         }
2362
2363         public static TypeReference GetUnderlyingType (this TypeDefinition type)
2364         {
2365                 if (!type.IsEnum)
2366                         return type;
2367                 return type.Fields.First (f => f.Name == "value__").FieldType;
2368         }
2369
2370         public static IEnumerable<TypeDefinition> GetAllTypes (this ModuleDefinition self)
2371         {
2372                 return self.Types.SelectMany (t => t.GetAllTypes ());
2373         }
2374
2375         static IEnumerable<TypeDefinition> GetAllTypes (this TypeDefinition self)
2376         {
2377                 yield return self;
2378
2379                 if (!self.HasNestedTypes)
2380                         yield break;
2381
2382                 foreach (var type in self.NestedTypes.SelectMany (t => t.GetAllTypes ()))
2383                         yield return type;
2384         }
2385 }
2386
2387 static class DocUtils {
2388         public static bool IsExplicitlyImplemented (MethodDefinition method)
2389         {
2390                 return method.IsPrivate && method.IsFinal && method.IsVirtual;
2391         }
2392
2393         public static string GetTypeDotMember (string name)
2394         {
2395                 int startType, startMethod;
2396                 startType = startMethod = -1;
2397                 for (int i = 0; i < name.Length; ++i) {
2398                         if (name [i] == '.') {
2399                                 startType = startMethod;
2400                                 startMethod = i;
2401                         }
2402                 }
2403                 return name.Substring (startType+1);
2404         }
2405
2406         public static string GetMember (string name)
2407         {
2408                 int i = name.LastIndexOf ('.');
2409                 if (i == -1)
2410                         return name;
2411                 return name.Substring (i+1);
2412         }
2413
2414         public static void GetInfoForExplicitlyImplementedMethod (
2415                         MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
2416         {
2417                 iface = null;
2418                 ifaceMethod = null;
2419                 if (method.Overrides.Count != 1)
2420                         throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
2421                 iface = method.Overrides [0].DeclaringType;
2422                 ifaceMethod = method.Overrides [0];
2423         }
2424
2425         public static string GetPropertyName (PropertyDefinition pi)
2426         {
2427                 // Issue: (g)mcs-generated assemblies that explicitly implement
2428                 // properties don't specify the full namespace, just the 
2429                 // TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
2430                 MethodDefinition method = pi.GetMethod;
2431                 if (method == null)
2432                         method = pi.SetMethod;
2433                 if (!IsExplicitlyImplemented (method))
2434                         return pi.Name;
2435
2436                 // Need to determine appropriate namespace for this member.
2437                 TypeReference iface;
2438                 MethodReference ifaceMethod;
2439                 GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
2440                 return string.Join (".", new string[]{
2441                                 DocTypeFullMemberFormatter.Default.GetName (iface),
2442                                 GetMember (pi.Name)});
2443         }
2444
2445         public static string GetNamespace (TypeReference type)
2446         {
2447                 if (type.GetElementType ().IsNested)
2448                         type = type.GetElementType ();
2449                 while (type != null && type.IsNested)
2450                         type = type.DeclaringType;
2451                 if (type == null)
2452                         return string.Empty;
2453                 return type.Namespace;
2454         }
2455
2456         public static string PathCombine (string dir, string path)
2457         {
2458                 if (dir == null)
2459                         dir = "";
2460                 if (path == null)
2461                         path = "";
2462                 return Path.Combine (dir, path);
2463         }
2464
2465         public static bool IsExtensionMethod (MethodDefinition method)
2466         {
2467                 return
2468                         method.CustomAttributes
2469                                         .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
2470                         && method.DeclaringType.CustomAttributes
2471                                         .Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
2472         }
2473
2474         public static bool IsDelegate (TypeDefinition type)
2475         {
2476                 TypeReference baseRef = type.BaseType;
2477                 if (baseRef == null)
2478                         return false;
2479                 return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
2480                                 baseRef.FullName == "System.MulticastDelegate";
2481         }
2482
2483         public static List<TypeReference> GetDeclaringTypes (TypeReference type)
2484         {
2485                 List<TypeReference> decls = new List<TypeReference> ();
2486                 decls.Add (type);
2487                 while (type.DeclaringType != null) {
2488                         decls.Add (type.DeclaringType);
2489                         type = type.DeclaringType;
2490                 }
2491                 decls.Reverse ();
2492                 return decls;
2493         }
2494
2495         public static int GetGenericArgumentCount (TypeReference type)
2496         {
2497                 GenericInstanceType inst = type as GenericInstanceType;
2498                 return inst != null
2499                                 ? inst.GenericArguments.Count
2500                                 : type.GenericParameters.Count;
2501         }
2502
2503         public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
2504         {
2505                 HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
2506                 List<TypeReference> userInterfaces = new List<TypeReference> ();
2507                 foreach (TypeReference iface in type.Interfaces) {
2508                         TypeReference lookup = iface.Resolve () ?? iface;
2509                         if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
2510                                 userInterfaces.Add (iface);
2511                 }
2512                 return userInterfaces;
2513         }
2514
2515         private static string GetQualifiedTypeName (TypeReference type)
2516         {
2517                 return "[" + type.Scope.Name + "]" + type.FullName;
2518         }
2519
2520         private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
2521         {
2522                 HashSet<string> inheritedInterfaces = new HashSet<string> ();
2523                 Action<TypeDefinition> a = null;
2524                 a = t => {
2525                         if (t == null) return;
2526                         foreach (TypeReference r in t.Interfaces) {
2527                                 inheritedInterfaces.Add (GetQualifiedTypeName (r));
2528                                 a (r.Resolve ());
2529                         }
2530                 };
2531                 TypeReference baseRef = type.BaseType;
2532                 while (baseRef != null) {
2533                         TypeDefinition baseDef = baseRef.Resolve ();
2534                         if (baseDef != null) {
2535                                 a (baseDef);
2536                                 baseRef = baseDef.BaseType;
2537                         }
2538                         else
2539                                 baseRef = null;
2540                 }
2541                 foreach (TypeReference r in type.Interfaces)
2542                         a (r.Resolve ());
2543                 return inheritedInterfaces;
2544         }
2545 }
2546
2547 class DocsNodeInfo {
2548         public DocsNodeInfo (XmlElement node)
2549         {
2550                 this.Node = node;
2551         }
2552
2553         public DocsNodeInfo (XmlElement node, TypeDefinition type)
2554                 : this (node)
2555         {
2556                 SetType (type);
2557         }
2558
2559         public DocsNodeInfo (XmlElement node, MemberReference member)
2560                 : this (node)
2561         {
2562                 SetMemberInfo (member);
2563         }
2564
2565         void SetType (TypeDefinition type)
2566         {
2567                 if (type == null)
2568                         throw new ArgumentNullException ("type");
2569                 Type = type;
2570                 GenericParameters = new List<GenericParameter> (type.GenericParameters);
2571                 List<TypeReference> declTypes = DocUtils.GetDeclaringTypes (type);
2572                 int maxGenArgs = DocUtils.GetGenericArgumentCount (type);
2573                 for (int i = 0; i < declTypes.Count - 1; ++i) {
2574                         int remove = System.Math.Min (maxGenArgs, 
2575                                         DocUtils.GetGenericArgumentCount (declTypes [i]));
2576                         maxGenArgs -= remove;
2577                         while (remove-- > 0)
2578                                 GenericParameters.RemoveAt (0);
2579                 }
2580                 if (DocUtils.IsDelegate (type)) {
2581                         Parameters = type.GetMethod("Invoke").Parameters;
2582                         ReturnType = type.GetMethod("Invoke").ReturnType;
2583                         ReturnIsReturn = true;
2584                 }
2585         }
2586
2587         void SetMemberInfo (MemberReference member)
2588         {
2589                 if (member == null)
2590                         throw new ArgumentNullException ("member");
2591                 ReturnIsReturn = true;
2592                 AddRemarks = true;
2593                 Member = member;
2594                 
2595                 if (member is MethodReference ) {
2596                         MethodReference mr = (MethodReference) member;
2597                         Parameters = mr.Parameters;
2598                         if (mr.IsGenericMethod ()) {
2599                                 GenericParameters = new List<GenericParameter> (mr.GenericParameters);
2600                         }
2601                 }
2602                 else if (member is PropertyDefinition) {
2603                         Parameters = ((PropertyDefinition) member).Parameters;
2604                 }
2605                         
2606                 if (member is MethodDefinition) {
2607                         ReturnType = ((MethodDefinition) member).ReturnType;
2608                 } else if (member is PropertyDefinition) {
2609                         ReturnType = ((PropertyDefinition) member).PropertyType;
2610                         ReturnIsReturn = false;
2611                 }
2612
2613                 // no remarks section for enum members
2614                 if (member.DeclaringType != null && ((TypeDefinition) member.DeclaringType).IsEnum)
2615                         AddRemarks = false;
2616         }
2617
2618         public TypeReference ReturnType;
2619         public List<GenericParameter> GenericParameters;
2620         public IList<ParameterDefinition> Parameters;
2621         public bool ReturnIsReturn;
2622         public XmlElement Node;
2623         public bool AddRemarks = true;
2624         public MemberReference Member;
2625         public TypeDefinition Type;
2626 }
2627
2628 class DocumentationEnumerator {
2629
2630         public virtual IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2631         {
2632                 return GetDocumentationTypes (assembly, forTypes, null);
2633         }
2634
2635         protected IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2636         {
2637                 foreach (TypeDefinition type in assembly.GetTypes()) {
2638                         if (forTypes != null && forTypes.BinarySearch (type.FullName) < 0)
2639                                 continue;
2640                         if (seen != null && seen.Contains (type.FullName))
2641                                 continue;
2642                         yield return type;
2643                         foreach (TypeDefinition nested in type.NestedTypes)
2644                                 yield return nested;
2645                 }
2646         }
2647
2648         public virtual IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2649         {
2650                 foreach (XmlElement oldmember in basefile.SelectNodes("Type/Members/Member")) {
2651                         if (oldmember.GetAttribute ("__monodocer-seen__") == "true") {
2652                                 oldmember.RemoveAttribute ("__monodocer-seen__");
2653                                 continue;
2654                         }
2655                         MemberReference m = GetMember (type, new DocumentationMember (oldmember));
2656                         if (m == null) {
2657                                 yield return new DocsNodeInfo (oldmember);
2658                         }
2659                         else {
2660                                 yield return new DocsNodeInfo (oldmember, m);
2661                         }
2662                 }
2663         }
2664
2665         protected static MemberReference GetMember (TypeDefinition type, DocumentationMember member)
2666         {
2667                 string membertype = member.MemberType;
2668                 
2669                 string returntype = member.ReturnType;
2670                 
2671                 string docName = member.MemberName;
2672                 string[] docTypeParams = GetTypeParameters (docName);
2673
2674                 // Loop through all members in this type with the same name
2675                 foreach (MemberReference mi in GetReflectionMembers (type, docName)) {
2676                         if (mi is TypeDefinition) continue;
2677                         if (MDocUpdater.GetMemberType(mi) != membertype) continue;
2678
2679                         if (MDocUpdater.IsPrivate (mi))
2680                                 continue;
2681
2682                         IList<ParameterDefinition> pis = null;
2683                         string[] typeParams = null;
2684                         if (mi is MethodDefinition) {
2685                                 MethodDefinition mb = (MethodDefinition) mi;
2686                                 pis = mb.Parameters;
2687                                 if (docTypeParams != null && mb.IsGenericMethod ()) {
2688                                         IList<GenericParameter> args = mb.GenericParameters;
2689                                         if (args.Count == docTypeParams.Length) {
2690                                                 typeParams = args.Select (p => p.Name).ToArray ();
2691                                         }
2692                                 }
2693                         }
2694                         else if (mi is PropertyDefinition)
2695                                 pis = ((PropertyDefinition)mi).Parameters;
2696                         
2697                         int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
2698                         int pcount = pis == null ? 0 : pis.Count;
2699                         if (mcount != pcount)
2700                                 continue;
2701
2702                         MethodDefinition mDef = mi as MethodDefinition;
2703                         if (mDef != null && !mDef.IsConstructor) {
2704                                 // Casting operators can overload based on return type.
2705                                 if (returntype != GetReplacedString (
2706                                                         MDocUpdater.GetDocTypeFullName (((MethodDefinition)mi).ReturnType), 
2707                                                         typeParams, docTypeParams)) {
2708                                         continue;
2709                                 }
2710                         }
2711
2712                         if (pcount == 0)
2713                                 return mi;
2714                         bool good = true;
2715                         for (int i = 0; i < pis.Count; i++) {
2716                                 string paramType = GetReplacedString (
2717                                         MDocUpdater.GetDocParameterType (pis [i].ParameterType),
2718                                         typeParams, docTypeParams);
2719                                 if (paramType != (string) member.Parameters [i]) {
2720                                         good = false;
2721                                         break;
2722                                 }
2723                         }
2724                         if (!good) continue;
2725
2726                         return mi;
2727                 }
2728                 
2729                 return null;
2730         }
2731
2732         static string[] GetTypeParameters (string docName)
2733         {
2734                 if (docName [docName.Length-1] != '>')
2735                         return null;
2736                 StringList types = new StringList ();
2737                 int endToken = docName.Length-2;
2738                 int i = docName.Length-2;
2739                 do {
2740                         if (docName [i] == ',' || docName [i] == '<') {
2741                                 types.Add (docName.Substring (i + 1, endToken - i));
2742                                 endToken = i-1;
2743                         }
2744                         if (docName [i] == '<')
2745                                 break;
2746                 } while (--i >= 0);
2747
2748                 types.Reverse ();
2749                 return types.ToArray ();
2750         }
2751
2752         protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
2753         {
2754                 // need to worry about 4 forms of //@MemberName values:
2755                 //  1. "Normal" (non-generic) member names: GetEnumerator
2756                 //    - Lookup as-is.
2757                 //  2. Explicitly-implemented interface member names: System.Collections.IEnumerable.Current
2758                 //    - try as-is, and try type.member (due to "kludge" for property
2759                 //      support.
2760                 //  3. "Normal" Generic member names: Sort<T> (CSC)
2761                 //    - need to remove generic parameters --> "Sort"
2762                 //  4. Explicitly-implemented interface members for generic interfaces: 
2763                 //    -- System.Collections.Generic.IEnumerable<T>.Current
2764                 //    - Try as-is, and try type.member, *keeping* the generic parameters.
2765                 //     --> System.Collections.Generic.IEnumerable<T>.Current, IEnumerable<T>.Current
2766                 //  5. As of 2008-01-02, gmcs will do e.g. 'IFoo`1[A].Method' instead of
2767                 //    'IFoo<A>.Method' for explicitly implemented methods; don't interpret
2768                 //    this as (1) or (2).
2769                 if (docName.IndexOf ('<') == -1 && docName.IndexOf ('[') == -1) {
2770                         // Cases 1 & 2
2771                         foreach (MemberReference mi in type.GetMembers (docName))
2772                                 yield return mi;
2773                         if (CountChars (docName, '.') > 0)
2774                                 // might be a property; try only type.member instead of
2775                                 // namespace.type.member.
2776                                 foreach (MemberReference mi in 
2777                                                 type.GetMembers (DocUtils.GetTypeDotMember (docName)))
2778                                         yield return mi;
2779                         yield break;
2780                 }
2781                 // cases 3 & 4
2782                 int numLt = 0;
2783                 int numDot = 0;
2784                 int startLt, startType, startMethod;
2785                 startLt = startType = startMethod = -1;
2786                 for (int i = 0; i < docName.Length; ++i) {
2787                         switch (docName [i]) {
2788                                 case '<':
2789                                         if (numLt == 0) {
2790                                                 startLt = i;
2791                                         }
2792                                         ++numLt;
2793                                         break;
2794                                 case '>':
2795                                         --numLt;
2796                                         if (numLt == 0 && (i + 1) < docName.Length)
2797                                                 // there's another character in docName, so this <...> sequence is
2798                                                 // probably part of a generic type -- case 4.
2799                                                 startLt = -1;
2800                                         break;
2801                                 case '.':
2802                                         startType = startMethod;
2803                                         startMethod = i;
2804                                         ++numDot;
2805                                         break;
2806                         }
2807                 }
2808                 string refName = startLt == -1 ? docName : docName.Substring (0, startLt);
2809                 // case 3
2810                 foreach (MemberReference mi in type.GetMembers (refName))
2811                         yield return mi;
2812
2813                 // case 4
2814                 foreach (MemberReference mi in type.GetMembers (refName.Substring (startType + 1)))
2815                         yield return mi;
2816
2817                 // If we _still_ haven't found it, we've hit another generic naming issue:
2818                 // post Mono 1.1.18, gmcs generates [[FQTN]] instead of <TypeName> for
2819                 // explicitly-implemented METHOD names (not properties), e.g. 
2820                 // "System.Collections.Generic.IEnumerable`1[[Foo, test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].GetEnumerator"
2821                 // instead of "System.Collections.Generic.IEnumerable<Foo>.GetEnumerator",
2822                 // which the XML docs will contain.
2823                 //
2824                 // Alas, we can't derive the Mono name from docName, so we need to iterate
2825                 // over all member names, convert them into CSC format, and compare... :-(
2826                 if (numDot == 0)
2827                         yield break;
2828                 foreach (MemberReference mi in type.GetMembers ()) {
2829                         if (MDocUpdater.GetMemberName (mi) == docName)
2830                                 yield return mi;
2831                 }
2832         }
2833
2834         static string GetReplacedString (string typeName, string[] from, string[] to)
2835         {
2836                 if (from == null)
2837                         return typeName;
2838                 for (int i = 0; i < from.Length; ++i)
2839                         typeName = typeName.Replace (from [i], to [i]);
2840                 return typeName;
2841         }
2842
2843         private static int CountChars (string s, char c)
2844         {
2845                 int count = 0;
2846                 for (int i = 0; i < s.Length; ++i) {
2847                         if (s [i] == c)
2848                                 ++count;
2849                 }
2850                 return count;
2851         }
2852 }
2853
2854 class EcmaDocumentationEnumerator : DocumentationEnumerator {
2855
2856         XmlReader ecmadocs;
2857         MDocUpdater app;
2858
2859         public EcmaDocumentationEnumerator (MDocUpdater app, XmlReader ecmaDocs)
2860         {
2861                 this.app      = app;
2862                 this.ecmadocs = ecmaDocs;
2863         }
2864
2865         public override IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes)
2866         {
2867                 HashSet<string> seen = new HashSet<string> ();
2868                 return GetDocumentationTypes (assembly, forTypes, seen)
2869                         .Concat (base.GetDocumentationTypes (assembly, forTypes, seen));
2870         }
2871
2872         new IEnumerable<TypeDefinition> GetDocumentationTypes (AssemblyDefinition assembly, List<string> forTypes, HashSet<string> seen)
2873         {
2874                 int typeDepth = -1;
2875                 while (ecmadocs.Read ()) {
2876                         switch (ecmadocs.Name) {
2877                                 case "Type": {
2878                                         if (typeDepth == -1)
2879                                                 typeDepth = ecmadocs.Depth;
2880                                         if (ecmadocs.NodeType != XmlNodeType.Element)
2881                                                 continue;
2882                                         if (typeDepth != ecmadocs.Depth) // nested <TypeDefinition/> element?
2883                                                 continue;
2884                                         string typename = ecmadocs.GetAttribute ("FullName");
2885                                         string typename2 = MDocUpdater.GetTypeFileName (typename);
2886                                         if (forTypes != null && 
2887                                                         forTypes.BinarySearch (typename) < 0 &&
2888                                                         typename != typename2 &&
2889                                                         forTypes.BinarySearch (typename2) < 0)
2890                                                 continue;
2891                                         TypeDefinition t;
2892                                         if ((t = assembly.GetType (typename)) == null && 
2893                                                         (t = assembly.GetType (typename2)) == null)
2894                                                 continue;
2895                                         seen.Add (typename);
2896                                         if (typename != typename2)
2897                                                 seen.Add (typename2);
2898                                         Console.WriteLine ("  Import: {0}", t.FullName);
2899                                         if (ecmadocs.Name != "Docs") {
2900                                                 int depth = ecmadocs.Depth;
2901                                                 while (ecmadocs.Read ()) {
2902                                                         if (ecmadocs.Name == "Docs" && ecmadocs.Depth == depth + 1)
2903                                                                 break;
2904                                                 }
2905                                         }
2906                                         if (!ecmadocs.IsStartElement ("Docs"))
2907                                                 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expecting <Docs/>!");
2908                                         yield return t;
2909                                         break;
2910                                 }
2911                                 default:
2912                                         break;
2913                         }
2914                 }
2915         }
2916
2917         public override IEnumerable<DocsNodeInfo> GetDocumentationMembers (XmlDocument basefile, TypeDefinition type)
2918         {
2919                 return GetMembers (basefile, type)
2920                         .Concat (base.GetDocumentationMembers (basefile, type));
2921         }
2922
2923         private IEnumerable<DocsNodeInfo> GetMembers (XmlDocument basefile, TypeDefinition type)
2924         {
2925                 while (ecmadocs.Name != "Members" && ecmadocs.Read ()) {
2926                         // do nothing
2927                 }
2928                 if (ecmadocs.IsEmptyElement)
2929                         yield break;
2930
2931                 int membersDepth = ecmadocs.Depth;
2932                 bool go = true;
2933                 while (go && ecmadocs.Read ()) {
2934                         switch (ecmadocs.Name) {
2935                                 case "Member": {
2936                                         if (membersDepth != ecmadocs.Depth - 1 || ecmadocs.NodeType != XmlNodeType.Element)
2937                                                 continue;
2938                                         DocumentationMember dm = new DocumentationMember (ecmadocs);
2939                                         string xp = MDocUpdater.GetXPathForMember (dm);
2940                                         XmlElement oldmember = (XmlElement) basefile.SelectSingleNode (xp);
2941                                         MemberReference m;
2942                                         if (oldmember == null) {
2943                                                 m = GetMember (type, dm);
2944                                                 if (m == null) {
2945                                                         app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2946                                                                         type.FullName, dm.MemberSignatures ["C#"]);
2947                                                                         // SelectSingleNode (ecmaDocsMember, "MemberSignature[@Language=\"C#\"]/@Value").Value);
2948                                                         continue;
2949                                                 }
2950                                                 // oldmember lookup may have failed due to type parameter renames.
2951                                                 // Try again.
2952                                                 oldmember = (XmlElement) basefile.SelectSingleNode (MDocUpdater.GetXPathForMember (m));
2953                                                 if (oldmember == null) {
2954                                                         XmlElement members = MDocUpdater.WriteElement (basefile.DocumentElement, "Members");
2955                                                         oldmember = basefile.CreateElement ("Member");
2956                                                         oldmember.SetAttribute ("MemberName", dm.MemberName);
2957                                                         members.AppendChild (oldmember);
2958                                                         foreach (string key in MDocUpdater.Sort (dm.MemberSignatures.Keys)) {
2959                                                                 XmlElement ms = basefile.CreateElement ("MemberSignature");
2960                                                                 ms.SetAttribute ("Language", key);
2961                                                                 ms.SetAttribute ("Value", (string) dm.MemberSignatures [key]);
2962                                                                 oldmember.AppendChild (ms);
2963                                                         }
2964                                                         oldmember.SetAttribute ("__monodocer-seen__", "true");
2965                                                         Console.WriteLine ("Member Added: {0}", oldmember.SelectSingleNode("MemberSignature[@Language='C#']/@Value").InnerText);
2966                                                         app.additions++;
2967                                                 }
2968                                         }
2969                                         else {
2970                                                 m = GetMember (type, new DocumentationMember (oldmember));
2971                                                 if (m == null) {
2972                                                         app.Warning ("Could not import ECMA docs for `{0}'s `{1}': Member not found.",
2973                                                                         type.FullName, dm.MemberSignatures ["C#"]);
2974                                                         continue;
2975                                                 }
2976                                                 oldmember.SetAttribute ("__monodocer-seen__", "true");
2977                                         }
2978                                         DocsNodeInfo node = new DocsNodeInfo (oldmember, m);
2979                                         if (ecmadocs.Name != "Docs")
2980                                                 throw new InvalidOperationException ("Found " + ecmadocs.Name + "; expected <Docs/>!");
2981                                         yield return node;
2982                                         break;
2983                                 }
2984                                 case "Members":
2985                                         if (membersDepth == ecmadocs.Depth && ecmadocs.NodeType == XmlNodeType.EndElement) {
2986                                                 go = false;
2987                                         }
2988                                         break;
2989                         }
2990                 }
2991         }
2992 }
2993
2994 abstract class DocumentationImporter {
2995
2996         public abstract void ImportDocumentation (DocsNodeInfo info);
2997 }
2998
2999 class MsxdocDocumentationImporter : DocumentationImporter {
3000
3001         XmlDocument slashdocs;
3002
3003         public MsxdocDocumentationImporter (string file)
3004         {
3005                 var xml = File.ReadAllText (file);
3006
3007                 // Ensure Unix line endings
3008                 xml = xml.Replace ("\r", "");
3009
3010                 slashdocs = new XmlDocument();
3011                 slashdocs.LoadXml (xml);
3012         }
3013
3014         public override void ImportDocumentation (DocsNodeInfo info)
3015         {
3016                 XmlNode elem = GetDocs (info.Member ?? info.Type);
3017
3018                 if (elem == null)
3019                         return;
3020
3021                 XmlElement e = info.Node;
3022
3023                 if (elem.SelectSingleNode("summary") != null)
3024                         MDocUpdater.ClearElement(e, "summary");
3025                 if (elem.SelectSingleNode("remarks") != null)
3026                         MDocUpdater.ClearElement(e, "remarks");
3027                 if (elem.SelectSingleNode ("value") != null || elem.SelectSingleNode ("returns") != null) {
3028                         MDocUpdater.ClearElement(e, "value");
3029                         MDocUpdater.ClearElement(e, "returns");
3030                 }
3031
3032                 foreach (XmlNode child in elem.ChildNodes) {
3033                         switch (child.Name) {
3034                                 case "param":
3035                                 case "typeparam": {
3036                                         XmlAttribute name = child.Attributes ["name"];
3037                                         if (name == null)
3038                                                 break;
3039                                         XmlElement p2 = (XmlElement) e.SelectSingleNode (child.Name + "[@name='" + name.Value + "']");
3040                                         if (p2 != null)
3041                                                 p2.InnerXml = child.InnerXml;
3042                                         break;
3043                                 }
3044                                 // Occasionally XML documentation will use <returns/> on
3045                                 // properties, so let's try to normalize things.
3046                                 case "value":
3047                                 case "returns": {
3048                                         XmlElement v = e.OwnerDocument.CreateElement (info.ReturnIsReturn ? "returns" : "value");
3049                                         v.InnerXml = child.InnerXml;
3050                                         e.AppendChild (v);
3051                                         break;
3052                                 }
3053                                 case "altmember":
3054                                 case "exception":
3055                                 case "permission": {
3056                                         XmlAttribute cref = child.Attributes ["cref"] ?? child.Attributes ["name"];
3057                                         if (cref == null)
3058                                                 break;
3059                                         XmlElement a = (XmlElement) e.SelectSingleNode (child.Name + "[@cref='" + cref.Value + "']");
3060                                         if (a == null) {
3061                                                 a = e.OwnerDocument.CreateElement (child.Name);
3062                                                 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3063                                                 e.AppendChild (a);
3064                                         }
3065                                         a.InnerXml = child.InnerXml;
3066                                         break;
3067                                 }
3068                                 case "seealso": {
3069                                         XmlAttribute cref = child.Attributes ["cref"];
3070                                         if (cref == null)
3071                                                 break;
3072                                         XmlElement a = (XmlElement) e.SelectSingleNode ("altmember[@cref='" + cref.Value + "']");
3073                                         if (a == null) {
3074                                                 a = e.OwnerDocument.CreateElement ("altmember");
3075                                                 a.SetAttribute ("cref", child.Attributes ["cref"].Value);
3076                                                 e.AppendChild (a);
3077                                         }
3078                                         break;
3079                                 }
3080                                 default: {
3081                                         bool add = true;
3082                                         if (child.NodeType == XmlNodeType.Element && 
3083                                                         e.SelectNodes (child.Name).Cast<XmlElement>().Any (n => n.OuterXml == child.OuterXml))
3084                                                 add = false;
3085                                         if (add)
3086                                                 MDocUpdater.CopyNode (child, e);
3087                                         break;
3088                                 }
3089                         }
3090                 }
3091         }
3092
3093         private XmlNode GetDocs (MemberReference member)
3094         {
3095                 string slashdocsig = MDocUpdater.slashdocFormatter.GetDeclaration (member);
3096                 if (slashdocsig != null)
3097                         return slashdocs.SelectSingleNode ("doc/members/member[@name='" + slashdocsig + "']");
3098                 return null;
3099         }
3100 }
3101
3102 class EcmaDocumentationImporter : DocumentationImporter {
3103
3104         XmlReader ecmadocs;
3105
3106         public EcmaDocumentationImporter (XmlReader ecmaDocs)
3107         {
3108                 this.ecmadocs = ecmaDocs;
3109         }
3110
3111         public override void ImportDocumentation (DocsNodeInfo info)
3112         {
3113                 if (!ecmadocs.IsStartElement ("Docs")) {
3114                         return;
3115                 }
3116
3117                 XmlElement e = info.Node;
3118
3119                 int depth = ecmadocs.Depth;
3120                 ecmadocs.ReadStartElement ("Docs");
3121                 while (ecmadocs.Read ()) {
3122                         if (ecmadocs.Name == "Docs") {
3123                                 if (ecmadocs.Depth == depth && ecmadocs.NodeType == XmlNodeType.EndElement)
3124                                         break;
3125                                 else
3126                                         throw new InvalidOperationException ("Skipped past current <Docs/> element!");
3127                         }
3128                         if (!ecmadocs.IsStartElement ())
3129                                 continue;
3130                         switch (ecmadocs.Name) {
3131                                 case "param":
3132                                 case "typeparam": {
3133                                         string name = ecmadocs.GetAttribute ("name");
3134                                         if (name == null)
3135                                                 break;
3136                                         XmlNode doc = e.SelectSingleNode (
3137                                                         ecmadocs.Name + "[@name='" + name + "']");
3138                                         string value = ecmadocs.ReadInnerXml ();
3139                                         if (doc != null)
3140                                                 doc.InnerXml = value.Replace ("\r", "");
3141                                         break;
3142                                 }
3143                                 case "altmember":
3144                                 case "exception":
3145                                 case "permission":
3146                                 case "seealso": {
3147                                         string name = ecmadocs.Name;
3148                                         string cref = ecmadocs.GetAttribute ("cref");
3149                                         if (cref == null)
3150                                                 break;
3151                                         XmlNode doc = e.SelectSingleNode (
3152                                                         ecmadocs.Name + "[@cref='" + cref + "']");
3153                                         string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3154                                         if (doc != null)
3155                                                 doc.InnerXml = value;
3156                                         else {
3157                                                 XmlElement n = e.OwnerDocument.CreateElement (name);
3158                                                 n.SetAttribute ("cref", cref);
3159                                                 n.InnerXml = value;
3160                                                 e.AppendChild (n);
3161                                         }
3162                                         break;
3163                                 }
3164                                 default: {
3165                                         string name = ecmadocs.Name;
3166                                         string xpath = ecmadocs.Name;
3167                                         StringList attributes = new StringList (ecmadocs.AttributeCount);
3168                                         if (ecmadocs.MoveToFirstAttribute ()) {
3169                                                 do {
3170                                                         attributes.Add ("@" + ecmadocs.Name + "=\"" + ecmadocs.Value + "\"");
3171                                                 } while (ecmadocs.MoveToNextAttribute ());
3172                                                 ecmadocs.MoveToContent ();
3173                                         }
3174                                         if (attributes.Count > 0) {
3175                                                 xpath += "[" + string.Join (" and ", attributes.ToArray ()) + "]";
3176                                         }
3177                                         XmlNode doc = e.SelectSingleNode (xpath);
3178                                         string value = ecmadocs.ReadInnerXml ().Replace ("\r", "");
3179                                         if (doc != null) {
3180                                                 doc.InnerXml = value;
3181                                         }
3182                                         else {
3183                                                 XmlElement n = e.OwnerDocument.CreateElement (name);
3184                                                 n.InnerXml = value;
3185                                                 foreach (string a in attributes) {
3186                                                         int eq = a.IndexOf ('=');
3187                                                         n.SetAttribute (a.Substring (1, eq-1), a.Substring (eq+2, a.Length-eq-3));
3188                                                 }
3189                                                 e.AppendChild (n);
3190                                         }
3191                                         break;
3192                                 }
3193                         }
3194                 }
3195         }
3196 }
3197
3198 class DocumentationMember {
3199         public StringToStringMap MemberSignatures = new StringToStringMap ();
3200         public string ReturnType;
3201         public StringList Parameters;
3202         public string MemberName;
3203         public string MemberType;
3204
3205         public DocumentationMember (XmlReader reader)
3206         {
3207                 MemberName = reader.GetAttribute ("MemberName");
3208                 int depth = reader.Depth;
3209                 bool go = true;
3210                 StringList p = new StringList ();
3211                 do {
3212                         if (reader.NodeType != XmlNodeType.Element)
3213                                 continue;
3214                         switch (reader.Name) {
3215                                 case "MemberSignature":
3216                                         MemberSignatures [reader.GetAttribute ("Language")] = reader.GetAttribute ("Value");
3217                                         break;
3218                                 case "MemberType":
3219                                         MemberType = reader.ReadElementString ();
3220                                         break;
3221                                 case "ReturnType":
3222                                         if (reader.Depth == depth + 2)
3223                                                 ReturnType = reader.ReadElementString ();
3224                                         break;
3225                                 case "Parameter":
3226                                         if (reader.Depth == depth + 2)
3227                                                 p.Add (reader.GetAttribute ("Type"));
3228                                         break;
3229                                 case "Docs":
3230                                         if (reader.Depth == depth + 1)
3231                                                 go = false;
3232                                         break;
3233                         }
3234                 } while (go && reader.Read () && reader.Depth >= depth);
3235                 if (p.Count > 0) {
3236                         Parameters = p;
3237                 }
3238         }
3239
3240         public DocumentationMember (XmlNode node)
3241         {
3242                 MemberName = node.Attributes ["MemberName"].Value;
3243                 foreach (XmlNode n in node.SelectNodes ("MemberSignature")) {
3244                         XmlAttribute l = n.Attributes ["Language"];
3245                         XmlAttribute v = n.Attributes ["Value"];
3246                         if (l != null && v != null)
3247                                 MemberSignatures [l.Value] = v.Value;
3248                 }
3249                 MemberType = node.SelectSingleNode ("MemberType").InnerText;
3250                 XmlNode rt = node.SelectSingleNode ("ReturnValue/ReturnType");
3251                 if (rt != null)
3252                         ReturnType = rt.InnerText;
3253                 XmlNodeList p = node.SelectNodes ("Parameters/Parameter");
3254                 if (p.Count > 0) {
3255                         Parameters = new StringList (p.Count);
3256                         for (int i = 0; i < p.Count; ++i)
3257                                 Parameters.Add (p [i].Attributes ["Type"].Value);
3258                 }
3259         }
3260 }
3261
3262 public enum MemberFormatterState {
3263         None,
3264         WithinGenericTypeParameters,
3265 }
3266
3267 public abstract class MemberFormatter {
3268
3269         public virtual string Language {
3270                 get {return "";}
3271         }
3272
3273         public virtual string GetName (MemberReference member)
3274         {
3275                 TypeReference type = member as TypeReference;
3276                 if (type != null)
3277                         return GetTypeName (type);
3278                 MethodReference method  = member as MethodReference;
3279                 if (method != null && method.Name == ".ctor") // method.IsConstructor
3280                         return GetConstructorName (method);
3281                 if (method != null)
3282                         return GetMethodName (method);
3283                 PropertyReference prop = member as PropertyReference;
3284                 if (prop != null)
3285                         return GetPropertyName (prop);
3286                 FieldReference field = member as FieldReference;
3287                 if (field != null)
3288                         return GetFieldName (field);
3289                 EventReference e = member as EventReference;
3290                 if (e != null)
3291                         return GetEventName (e);
3292                 throw new NotSupportedException ("Can't handle: " +
3293                                         (member == null ? "null" : member.GetType().ToString()));
3294         }
3295
3296         protected virtual string GetTypeName (TypeReference type)
3297         {
3298                 if (type == null)
3299                         throw new ArgumentNullException ("type");
3300                 return _AppendTypeName (new StringBuilder (type.Name.Length), type).ToString ();
3301         }
3302
3303         protected virtual char[] ArrayDelimeters {
3304                 get {return new char[]{'[', ']'};}
3305         }
3306
3307         protected virtual MemberFormatterState MemberFormatterState { get; set; }
3308
3309         protected StringBuilder _AppendTypeName (StringBuilder buf, TypeReference type)
3310         {
3311                 if (type is ArrayType) {
3312                         TypeSpecification spec = type as TypeSpecification;
3313                         _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType ());
3314                         return AppendArrayModifiers (buf, (ArrayType) type);
3315                 }
3316                 if (type is ByReferenceType) {
3317                         return AppendRefTypeName (buf, type);
3318                 }
3319                 if (type is PointerType) {
3320                         return AppendPointerTypeName (buf, type);
3321                 }
3322                 AppendNamespace (buf, type);
3323                 if (type is GenericParameter) {
3324                         return AppendTypeName (buf, type);
3325                 }
3326                 GenericInstanceType genInst = type as GenericInstanceType;
3327                 if (type.GenericParameters.Count == 0 &&
3328                                 (genInst == null ? true : genInst.GenericArguments.Count == 0)) {
3329                         return AppendFullTypeName (buf, type);
3330                 }
3331                 return AppendGenericType (buf, type);
3332         }
3333
3334         protected virtual StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3335         {
3336                 string ns = DocUtils.GetNamespace (type);
3337                 if (ns != null && ns.Length > 0)
3338                         buf.Append (ns).Append ('.');
3339                 return buf;
3340         }
3341
3342         protected virtual StringBuilder AppendFullTypeName (StringBuilder buf, TypeReference type)
3343         {
3344                 if (type.DeclaringType != null)
3345                         AppendFullTypeName (buf, type.DeclaringType).Append (NestedTypeSeparator);
3346                 return AppendTypeName (buf, type);
3347         }
3348
3349         protected virtual StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3350         {
3351                 return AppendTypeName (buf, type.Name);
3352         }
3353
3354         protected virtual StringBuilder AppendTypeName (StringBuilder buf, string typename)
3355         {
3356                 int n = typename.IndexOf ("`");
3357                 if (n >= 0)
3358                         return buf.Append (typename.Substring (0, n));
3359                 return buf.Append (typename);
3360         }
3361
3362         protected virtual StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
3363         {
3364                 buf.Append (ArrayDelimeters [0]);
3365                 int rank = array.Rank;
3366                 if (rank > 1)
3367                         buf.Append (new string (',', rank-1));
3368                 return buf.Append (ArrayDelimeters [1]);
3369         }
3370
3371         protected virtual string RefTypeModifier {
3372                 get {return "@";}
3373         }
3374
3375         protected virtual StringBuilder AppendRefTypeName (StringBuilder buf, TypeReference type)
3376         {
3377                 TypeSpecification spec = type as TypeSpecification;
3378                 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType ())
3379                                 .Append (RefTypeModifier);
3380         }
3381
3382         protected virtual string PointerModifier {
3383                 get {return "*";}
3384         }
3385
3386         protected virtual StringBuilder AppendPointerTypeName (StringBuilder buf, TypeReference type)
3387         {
3388                 TypeSpecification spec = type as TypeSpecification;
3389                 return _AppendTypeName (buf, spec != null ? spec.ElementType : type.GetElementType ())
3390                                 .Append (PointerModifier);
3391         }
3392
3393         protected virtual char[] GenericTypeContainer {
3394                 get {return new char[]{'<', '>'};}
3395         }
3396
3397         protected virtual char NestedTypeSeparator {
3398                 get {return '.';}
3399         }
3400
3401         protected virtual StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3402         {
3403                 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3404                                 type is GenericInstanceType ? type.GetElementType () : type);
3405                 List<TypeReference> genArgs = GetGenericArguments (type);
3406                 int argIdx = 0;
3407                 int prev = 0;
3408                 bool insertNested = false;
3409                 foreach (var decl in decls) {
3410                         TypeReference declDef = decl.Resolve () ?? decl;
3411                         if (insertNested) {
3412                                 buf.Append (NestedTypeSeparator);
3413                         }
3414                         insertNested = true;
3415                         AppendTypeName (buf, declDef);
3416                         int ac = DocUtils.GetGenericArgumentCount (declDef);
3417                         int c = ac - prev;
3418                         prev = ac;
3419                         if (c > 0) {
3420                                 buf.Append (GenericTypeContainer [0]);
3421                                 var origState = MemberFormatterState;
3422                                 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3423                                 _AppendTypeName (buf, genArgs [argIdx++]);
3424                                 for (int i = 1; i < c; ++i)
3425                                         _AppendTypeName (buf.Append (","), genArgs [argIdx++]);
3426                                 MemberFormatterState = origState;
3427                                 buf.Append (GenericTypeContainer [1]);
3428                         }
3429                 }
3430                 return buf;
3431         }
3432
3433         protected List<TypeReference> GetGenericArguments (TypeReference type)
3434         {
3435                 var args = new List<TypeReference> ();
3436                 GenericInstanceType inst = type as GenericInstanceType;
3437                 if (inst != null)
3438                         args.AddRange (inst.GenericArguments.Cast<TypeReference> ());
3439                 else
3440                         args.AddRange (type.GenericParameters.Cast<TypeReference> ());
3441                 return args;
3442         }
3443
3444         protected virtual StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
3445         {
3446                 return buf;
3447         }
3448
3449         protected virtual string GetConstructorName (MethodReference constructor)
3450         {
3451                 return constructor.Name;
3452         }
3453
3454         protected virtual string GetMethodName (MethodReference method)
3455         {
3456                 return method.Name;
3457         }
3458
3459         protected virtual string GetPropertyName (PropertyReference property)
3460         {
3461                 return property.Name;
3462         }
3463
3464         protected virtual string GetFieldName (FieldReference field)
3465         {
3466                 return field.Name;
3467         }
3468
3469         protected virtual string GetEventName (EventReference e)
3470         {
3471                 return e.Name;
3472         }
3473
3474         public virtual string GetDeclaration (MemberReference member)
3475         {
3476                 if (member == null)
3477                         throw new ArgumentNullException ("member");
3478                 TypeDefinition type = member as TypeDefinition;
3479                 if (type != null)
3480                         return GetTypeDeclaration (type);
3481                 MethodDefinition method = member as MethodDefinition;
3482                 if (method != null && method.IsConstructor)
3483                         return GetConstructorDeclaration (method);
3484                 if (method != null)
3485                         return GetMethodDeclaration (method);
3486                 PropertyDefinition prop = member as PropertyDefinition;
3487                 if (prop != null)
3488                         return GetPropertyDeclaration (prop);
3489                 FieldDefinition field = member as FieldDefinition;
3490                 if (field != null)
3491                         return GetFieldDeclaration (field);
3492                 EventDefinition e = member as EventDefinition;
3493                 if (e != null)
3494                         return GetEventDeclaration (e);
3495                 throw new NotSupportedException ("Can't handle: " + member.GetType().ToString());
3496         }
3497
3498         protected virtual string GetTypeDeclaration (TypeDefinition type)
3499         {
3500                 if (type == null)
3501                         throw new ArgumentNullException ("type");
3502                 StringBuilder buf = new StringBuilder (type.Name.Length);
3503                 _AppendTypeName (buf, type);
3504                 AppendGenericTypeConstraints (buf, type);
3505                 return buf.ToString ();
3506         }
3507
3508         protected virtual string GetConstructorDeclaration (MethodDefinition constructor)
3509         {
3510                 return GetConstructorName (constructor);
3511         }
3512
3513         protected virtual string GetMethodDeclaration (MethodDefinition method)
3514         {
3515                 if (method.HasCustomAttributes && method.CustomAttributes.Cast<CustomAttribute>().Any(
3516                                         ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute"))
3517                         return null;
3518
3519                 // Special signature for destructors.
3520                 if (method.Name == "Finalize" && method.Parameters.Count == 0)
3521                         return GetFinalizerName (method);
3522
3523                 StringBuilder buf = new StringBuilder ();
3524
3525                 AppendVisibility (buf, method);
3526                 if (buf.Length == 0 && 
3527                                 !(DocUtils.IsExplicitlyImplemented (method) && !method.IsSpecialName))
3528                         return null;
3529
3530                 AppendModifiers (buf, method);
3531
3532                 if (buf.Length != 0)
3533                         buf.Append (" ");
3534                 buf.Append (GetTypeName (method.MethodReturnType)).Append (" ");
3535
3536                 AppendMethodName (buf, method);
3537                 AppendGenericMethod (buf, method).Append (" ");
3538                 AppendParameters (buf, method, method.Parameters);
3539                 AppendGenericMethodConstraints (buf, method);
3540                 return buf.ToString ();
3541         }
3542
3543         protected virtual string GetTypeName (MethodReturnType returnType)
3544         {
3545                 return GetName (returnType.ReturnType);
3546         }
3547
3548         protected virtual StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3549         {
3550                 return buf.Append (method.Name);
3551         }
3552
3553         protected virtual string GetFinalizerName (MethodDefinition method)
3554         {
3555                 return "Finalize";
3556         }
3557
3558         protected virtual StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3559         {
3560                 return buf;
3561         }
3562
3563         protected virtual StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3564         {
3565                 return buf;
3566         }
3567
3568         protected virtual StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3569         {
3570                 return buf;
3571         }
3572
3573         protected virtual StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3574         {
3575                 return buf;
3576         }
3577
3578         protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
3579         {
3580                 return buf;
3581         }
3582
3583         protected virtual string GetPropertyDeclaration (PropertyDefinition property)
3584         {
3585                 return GetPropertyName (property);
3586         }
3587
3588         protected virtual string GetFieldDeclaration (FieldDefinition field)
3589         {
3590                 return GetFieldName (field);
3591         }
3592
3593         protected virtual string GetEventDeclaration (EventDefinition e)
3594         {
3595                 return GetEventName (e);
3596         }
3597 }
3598
3599 class ILFullMemberFormatter : MemberFormatter {
3600
3601         public override string Language {
3602                 get {return "ILAsm";}
3603         }
3604
3605         protected override char NestedTypeSeparator {
3606                 get {
3607                         return '/';
3608                 }
3609         }
3610
3611         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
3612         {
3613                 if (GetBuiltinType (type.FullName) != null)
3614                         return buf;
3615                 string ns = DocUtils.GetNamespace (type);
3616                 if (ns != null && ns.Length > 0) {
3617                         if (type.IsValueType)
3618                                 buf.Append ("valuetype ");
3619                         else
3620                                 buf.Append ("class ");
3621                         buf.Append (ns).Append ('.');
3622                 }
3623                 return buf;
3624         }
3625
3626         private static string GetBuiltinType (string t)
3627         {
3628                 switch (t) {
3629                 case "System.Byte":    return "unsigned int8";
3630                 case "System.SByte":   return "int8";
3631                 case "System.Int16":   return "int16";
3632                 case "System.Int32":   return "int32";
3633                 case "System.Int64":   return "int64";
3634                 case "System.IntPtr":  return "native int";
3635
3636                 case "System.UInt16":  return "unsigned int16";
3637                 case "System.UInt32":  return "unsigned int32";
3638                 case "System.UInt64":  return "unsigned int64";
3639                 case "System.UIntPtr": return "native unsigned int";
3640
3641                 case "System.Single":  return "float32";
3642                 case "System.Double":  return "float64";
3643                 case "System.Boolean": return "bool";
3644                 case "System.Char":    return "char";
3645                 case "System.Void":    return "void";
3646                 case "System.String":  return "string";
3647                 case "System.Object":  return "object";
3648                 }
3649                 return null;
3650         }
3651
3652         protected override StringBuilder AppendTypeName (StringBuilder buf, string typename)
3653         {
3654                 return buf.Append (typename);
3655         }
3656
3657         protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
3658         {
3659                 if (type is GenericParameter)
3660                         return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
3661
3662                 string s = GetBuiltinType (type.FullName);
3663                 if (s != null)
3664                         return buf.Append (s);
3665                 return base.AppendTypeName (buf, type);
3666         }
3667
3668         private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
3669         {
3670                 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters) {
3671                         return buf.Append (type.Owner is TypeReference ? "!" : "!!");
3672                 }
3673                 GenericParameterAttributes attrs = type.Attributes;
3674                 if ((attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
3675                         buf.Append ("class ");
3676                 if ((attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
3677                         buf.Append ("struct ");
3678                 if ((attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
3679                         buf.Append (".ctor ");
3680                 IList<TypeReference> constraints = type.Constraints;
3681                 MemberFormatterState = 0;
3682                 if (constraints.Count > 0) {
3683                         var full = new ILFullMemberFormatter ();
3684                         buf.Append ("(").Append (full.GetName (constraints [0]));
3685                         for (int i = 1; i < constraints.Count; ++i) {
3686                                 buf.Append (", ").Append (full.GetName (constraints [i]));
3687                         }
3688                         buf.Append (") ");
3689                 }
3690                 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3691
3692                 if ((attrs & GenericParameterAttributes.Covariant) != 0)
3693                         buf.Append ("+ ");
3694                 if ((attrs & GenericParameterAttributes.Contravariant) != 0)
3695                         buf.Append ("- ");
3696                 return buf;
3697         }
3698
3699         protected override string GetTypeDeclaration (TypeDefinition type)
3700         {
3701                 string visibility = GetTypeVisibility (type.Attributes);
3702                 if (visibility == null)
3703                         return null;
3704
3705                 StringBuilder buf = new StringBuilder ();
3706
3707                 buf.Append (".class ");
3708                 if (type.IsNested)
3709                         buf.Append ("nested ");
3710                 buf.Append (visibility).Append (" ");
3711                 if (type.IsInterface)
3712                         buf.Append ("interface ");
3713                 if (type.IsSequentialLayout)
3714                         buf.Append ("sequential ");
3715                 if (type.IsAutoLayout)
3716                         buf.Append ("auto ");
3717                 if (type.IsAnsiClass)
3718                         buf.Append ("ansi ");
3719                 if (type.IsAbstract)
3720                         buf.Append ("abstract ");
3721                 if (type.IsSerializable)
3722                         buf.Append ("serializable ");
3723                 if (type.IsSealed)
3724                         buf.Append ("sealed ");
3725                 if (type.IsBeforeFieldInit)
3726                         buf.Append ("beforefieldinit ");
3727                 var state = MemberFormatterState;
3728                 MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3729                 buf.Append (GetName (type));
3730                 MemberFormatterState = state;
3731                 var full = new ILFullMemberFormatter ();
3732                 if (type.BaseType != null) {
3733                         buf.Append (" extends ");
3734                         if (type.BaseType.FullName == "System.Object")
3735                                 buf.Append ("System.Object");
3736                         else
3737                                 buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
3738                 }
3739                 bool first = true;
3740                 foreach (var name in type.Interfaces
3741                                 .Select (i => full.GetName (i))
3742                                 .OrderBy (n => n)) {
3743                         if (first) {
3744                                 buf.Append (" implements ");
3745                                 first = false;
3746                         }
3747                         else {
3748                                 buf.Append (", ");
3749                         }
3750                         buf.Append (name);
3751                 }
3752
3753                 return buf.ToString ();
3754         }
3755
3756         protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
3757         {
3758                 List<TypeReference> decls = DocUtils.GetDeclaringTypes (
3759                                 type is GenericInstanceType ? type.GetElementType () : type);
3760                 bool first = true;
3761                 foreach (var decl in decls) {
3762                         TypeReference declDef = decl.Resolve () ?? decl;
3763                         if (!first) {
3764                                 buf.Append (NestedTypeSeparator);
3765                         }
3766                         first = false;
3767                         AppendTypeName (buf, declDef);
3768                 }
3769                 buf.Append ('<');
3770                 first = true;
3771                 foreach (TypeReference arg in GetGenericArguments (type)) {
3772                         if (!first)
3773                                 buf.Append (", ");
3774                         first = false;
3775                         _AppendTypeName (buf, arg);
3776                 }
3777                 buf.Append ('>');
3778                 return buf;
3779         }
3780
3781         static string GetTypeVisibility (TypeAttributes ta)
3782         {
3783                 switch (ta & TypeAttributes.VisibilityMask) {
3784                 case TypeAttributes.Public:
3785                 case TypeAttributes.NestedPublic:
3786                         return "public";
3787
3788                 case TypeAttributes.NestedFamily:
3789                 case TypeAttributes.NestedFamORAssem:
3790                         return "protected";
3791
3792                 default:
3793                         return null;
3794                 }
3795         }
3796
3797         protected override string GetConstructorDeclaration (MethodDefinition constructor)
3798         {
3799                 return GetMethodDeclaration (constructor);
3800         }
3801
3802         protected override string GetMethodDeclaration (MethodDefinition method)
3803         {
3804                 if (method.IsPrivate && !DocUtils.IsExplicitlyImplemented (method))
3805                         return null;
3806
3807                 var buf = new StringBuilder ();
3808                 buf.Append (".method ");
3809                 AppendVisibility (buf, method);
3810                 if (method.IsStatic)
3811                         buf.Append ("static ");
3812                 if (method.IsHideBySig)
3813                         buf.Append ("hidebysig ");
3814                 if (method.IsPInvokeImpl) {
3815                         var info = method.PInvokeInfo;
3816                         buf.Append ("pinvokeimpl (\"")
3817                                 .Append (info.Module.Name)
3818                                 .Append ("\" as \"")
3819                                 .Append (info.EntryPoint)
3820                                 .Append ("\"");
3821                         if (info.IsCharSetAuto)
3822                                 buf.Append (" auto");
3823                         if (info.IsCharSetUnicode)
3824                                 buf.Append (" unicode");
3825                         if (info.IsCharSetAnsi)
3826                                 buf.Append (" ansi");
3827                         if (info.IsCallConvCdecl)
3828                                 buf.Append (" cdecl");
3829                         if (info.IsCallConvStdCall)
3830                                 buf.Append (" stdcall");
3831                         if (info.IsCallConvWinapi)
3832                                 buf.Append (" winapi");
3833                         if (info.IsCallConvThiscall)
3834                                 buf.Append (" thiscall");
3835                         if (info.SupportsLastError)
3836                                 buf.Append (" lasterr");
3837                         buf.Append (")");
3838                 }
3839                 if (method.IsSpecialName)
3840                         buf.Append ("specialname ");
3841                 if (method.IsRuntimeSpecialName)
3842                         buf.Append ("rtspecialname ");
3843                 if (method.IsNewSlot)
3844                         buf.Append ("newslot ");
3845                 if (method.IsVirtual)
3846                         buf.Append ("virtual ");
3847                 if (!method.IsStatic)
3848                         buf.Append ("instance ");
3849                 _AppendTypeName (buf, method.ReturnType);
3850                 buf.Append (' ')
3851                         .Append (method.Name);
3852                 if (method.IsGenericMethod ()) {
3853                         var state = MemberFormatterState;
3854                         MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters;
3855                         IList<GenericParameter> args = method.GenericParameters;
3856                         if (args.Count > 0) {
3857                                 buf.Append ("<");
3858                                 _AppendTypeName (buf, args [0]);
3859                                 for (int i = 1; i < args.Count; ++i)
3860                                         _AppendTypeName (buf.Append (", "), args [i]);
3861                                 buf.Append (">");
3862                         }
3863                         MemberFormatterState = state;
3864                 }
3865
3866                 buf.Append ('(');
3867                 bool first = true;
3868                 for (int i = 0; i < method.Parameters.Count; ++i) {
3869                         if (!first)
3870                                 buf.Append (", ");
3871                         first = false;
3872                         _AppendTypeName (buf, method.Parameters [i].ParameterType);
3873                         buf.Append (' ');
3874                         buf.Append (method.Parameters [i].Name);
3875                 }
3876                 buf.Append (')');
3877                 if (method.IsIL)
3878                         buf.Append (" cil");
3879                 if (method.IsRuntime)
3880                         buf.Append (" runtime");
3881                 if (method.IsManaged)
3882                         buf.Append (" managed");
3883
3884                 return buf.ToString ();
3885         }
3886
3887         protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
3888         {
3889                 if (DocUtils.IsExplicitlyImplemented (method)) {
3890                         TypeReference iface;
3891                         MethodReference ifaceMethod;
3892                         DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
3893                         return buf.Append (new CSharpMemberFormatter ().GetName (iface))
3894                                 .Append ('.')
3895                                 .Append (ifaceMethod.Name);
3896                 }
3897                 return base.AppendMethodName (buf, method);
3898         }
3899
3900         protected override string RefTypeModifier {
3901                 get {return "";}
3902         }
3903
3904         protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
3905         {
3906                 if (method.IsPublic)
3907                         return buf.Append ("public ");
3908                 if (method.IsFamilyAndAssembly)
3909                         return buf.Append ("familyandassembly");
3910                 if (method.IsFamilyOrAssembly)
3911                         return buf.Append ("familyorassembly");
3912                 if (method.IsFamily)
3913                         return buf.Append ("family");
3914                 return buf;
3915         }
3916
3917         protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
3918         {
3919                 string modifiers = String.Empty;
3920                 if (method.IsStatic) modifiers += " static";
3921                 if (method.IsVirtual && !method.IsAbstract) {
3922                         if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
3923                         else modifiers += " override";
3924                 }
3925                 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
3926                 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
3927                 if (method.IsFinal) modifiers += " sealed";
3928                 if (modifiers == " virtual sealed") modifiers = "";
3929
3930                 return buf.Append (modifiers);
3931         }
3932
3933         protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
3934         {
3935                 if (method.IsGenericMethod ()) {
3936                         IList<GenericParameter> args = method.GenericParameters;
3937                         if (args.Count > 0) {
3938                                 buf.Append ("<");
3939                                 buf.Append (args [0].Name);
3940                                 for (int i = 1; i < args.Count; ++i)
3941                                         buf.Append (",").Append (args [i].Name);
3942                                 buf.Append (">");
3943                         }
3944                 }
3945                 return buf;
3946         }
3947
3948         protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
3949         {
3950                 return AppendParameters (buf, method, parameters, '(', ')');
3951         }
3952
3953         private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
3954         {
3955                 buf.Append (begin);
3956
3957                 if (parameters.Count > 0) {
3958                         if (DocUtils.IsExtensionMethod (method))
3959                                 buf.Append ("this ");
3960                         AppendParameter (buf, parameters [0]);
3961                         for (int i = 1; i < parameters.Count; ++i) {
3962                                 buf.Append (", ");
3963                                 AppendParameter (buf, parameters [i]);
3964                         }
3965                 }
3966
3967                 return buf.Append (end);
3968         }
3969
3970         private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
3971         {
3972                 if (parameter.ParameterType is ByReferenceType) {
3973                         if (parameter.IsOut)
3974                                 buf.Append ("out ");
3975                         else
3976                                 buf.Append ("ref ");
3977                 }
3978                 buf.Append (GetName (parameter.ParameterType)).Append (" ");
3979                 return buf.Append (parameter.Name);
3980         }
3981
3982         protected override string GetPropertyDeclaration (PropertyDefinition property)
3983         {
3984                 MethodDefinition gm = null, sm = null;
3985
3986                 string get_visible = null;
3987                 if ((gm = property.GetMethod) != null &&
3988                                 (DocUtils.IsExplicitlyImplemented (gm) ||
3989                                  (!gm.IsPrivate && !gm.IsAssembly && !gm.IsFamilyAndAssembly)))
3990                         get_visible = AppendVisibility (new StringBuilder (), gm).ToString ();
3991                 string set_visible = null;
3992                 if ((sm = property.SetMethod) != null &&
3993                                 (DocUtils.IsExplicitlyImplemented (sm) ||
3994                                  (!sm.IsPrivate && !sm.IsAssembly && !sm.IsFamilyAndAssembly)))
3995                         set_visible = AppendVisibility (new StringBuilder (), sm).ToString ();
3996
3997                 if ((set_visible == null) && (get_visible == null))
3998                         return null;
3999
4000                 StringBuilder buf = new StringBuilder ()
4001                         .Append (".property ");
4002                 if (!(gm ?? sm).IsStatic)
4003                         buf.Append ("instance ");
4004                 _AppendTypeName (buf, property.PropertyType);
4005                 buf.Append (' ').Append (property.Name);
4006                 if (!property.HasParameters || property.Parameters.Count == 0)
4007                         return buf.ToString ();
4008
4009                 buf.Append ('(');
4010                 bool first = true;
4011                 foreach (ParameterDefinition p in property.Parameters) {
4012                         if (!first)
4013                                 buf.Append (", ");
4014                         first = false;
4015                         _AppendTypeName (buf, p.ParameterType);
4016                 }
4017                 buf.Append (')');
4018
4019                 return buf.ToString ();
4020         }
4021
4022         protected override string GetFieldDeclaration (FieldDefinition field)
4023         {
4024                 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4025                 if (declType.IsEnum && field.Name == "value__")
4026                         return null; // This member of enums aren't documented.
4027
4028                 StringBuilder buf = new StringBuilder ();
4029                 AppendFieldVisibility (buf, field);
4030                 if (buf.Length == 0)
4031                         return null;
4032
4033                 buf.Insert (0, ".field ");
4034
4035                 if (field.IsStatic)
4036                         buf.Append ("static ");
4037                 if (field.IsInitOnly)
4038                         buf.Append ("initonly ");
4039                 if (field.IsLiteral)
4040                         buf.Append ("literal ");
4041                 _AppendTypeName (buf, field.FieldType);
4042                 buf.Append (' ').Append (field.Name);
4043                 AppendFieldValue (buf, field);
4044
4045                 return buf.ToString ();
4046         }
4047
4048         static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4049         {
4050                 if (field.IsPublic)
4051                         return buf.Append ("public ");
4052                 if (field.IsFamilyAndAssembly)
4053                         return buf.Append ("familyandassembly ");
4054                 if (field.IsFamilyOrAssembly)
4055                         return buf.Append ("familyorassembly ");
4056                 if (field.IsFamily)
4057                         return buf.Append ("family ");
4058                 return buf;
4059         }
4060
4061         static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4062         {
4063                 // enums have a value__ field, which we ignore
4064                 if (field.DeclaringType.IsGenericType ())
4065                         return buf;
4066                 if (field.HasConstant && field.IsLiteral) {
4067                         object val = null;
4068                         try {
4069                                 val   = field.Constant;
4070                         } catch {
4071                                 return buf;
4072                         }
4073                         if (val == null)
4074                                 buf.Append (" = ").Append ("null");
4075                         else if (val is Enum)
4076                                 buf.Append (" = ")
4077                                         .Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4078                                         .Append ('(')
4079                                         .Append (val.ToString ())
4080                                         .Append (')');
4081                         else if (val is IFormattable) {
4082                                 string value = ((IFormattable)val).ToString();
4083                                 buf.Append (" = ");
4084                                 if (val is string)
4085                                         buf.Append ("\"" + value + "\"");
4086                                 else
4087                                         buf.Append (GetBuiltinType (field.DeclaringType.GetUnderlyingType ().FullName))
4088                                                 .Append ('(')
4089                                                 .Append (value)
4090                                                 .Append (')');
4091                         }
4092                 }
4093                 return buf;
4094         }
4095
4096         protected override string GetEventDeclaration (EventDefinition e)
4097         {
4098                 StringBuilder buf = new StringBuilder ();
4099                 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4100                         return null;
4101                 }
4102
4103                 buf.Length = 0;
4104                 buf.Append (".event ")
4105                         .Append (GetName (e.EventType))
4106                         .Append (' ')
4107                         .Append (e.Name);
4108
4109                 return buf.ToString ();
4110         }
4111 }
4112
4113 class ILMemberFormatter : ILFullMemberFormatter {
4114         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4115         {
4116                 return buf;
4117         }
4118 }
4119
4120 class CSharpFullMemberFormatter : MemberFormatter {
4121
4122         public override string Language {
4123                 get {return "C#";}
4124         }
4125
4126         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4127         {
4128                 string ns = DocUtils.GetNamespace (type);
4129                 if (GetCSharpType (type.FullName) == null && ns != null && ns.Length > 0 && ns != "System")
4130                         buf.Append (ns).Append ('.');
4131                 return buf;
4132         }
4133
4134         private string GetCSharpType (string t)
4135         {
4136                 switch (t) {
4137                 case "System.Byte":    return "byte";
4138                 case "System.SByte":   return "sbyte";
4139                 case "System.Int16":   return "short";
4140                 case "System.Int32":   return "int";
4141                 case "System.Int64":   return "long";
4142
4143                 case "System.UInt16":  return "ushort";
4144                 case "System.UInt32":  return "uint";
4145                 case "System.UInt64":  return "ulong";
4146
4147                 case "System.Single":  return "float";
4148                 case "System.Double":  return "double";
4149                 case "System.Decimal": return "decimal";
4150                 case "System.Boolean": return "bool";
4151                 case "System.Char":    return "char";
4152                 case "System.Void":    return "void";
4153                 case "System.String":  return "string";
4154                 case "System.Object":  return "object";
4155                 }
4156                 return null;
4157         }
4158
4159         protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
4160         {
4161                 if (type is GenericParameter)
4162                         return AppendGenericParameterConstraints (buf, (GenericParameter) type).Append (type.Name);
4163                 string t = type.FullName;
4164                 if (!t.StartsWith ("System.")) {
4165                         return base.AppendTypeName (buf, type);
4166                 }
4167
4168                 string s = GetCSharpType (t);
4169                 if (s != null)
4170                         return buf.Append (s);
4171                 
4172                 return base.AppendTypeName (buf, type);
4173         }
4174
4175         private StringBuilder AppendGenericParameterConstraints (StringBuilder buf, GenericParameter type)
4176         {
4177                 if (MemberFormatterState != MemberFormatterState.WithinGenericTypeParameters)
4178                         return buf;
4179                 GenericParameterAttributes attrs = type.Attributes;
4180                 bool isout = (attrs & GenericParameterAttributes.Covariant) != 0;
4181                 bool isin  = (attrs & GenericParameterAttributes.Contravariant) != 0;
4182                 if (isin)
4183                         buf.Append ("in ");
4184                 else if (isout)
4185                         buf.Append ("out ");
4186                 return buf;
4187         }
4188
4189         protected override string GetTypeDeclaration (TypeDefinition type)
4190         {
4191                 string visibility = GetTypeVisibility (type.Attributes);
4192                 if (visibility == null)
4193                         return null;
4194
4195                 StringBuilder buf = new StringBuilder ();
4196                 
4197                 buf.Append (visibility);
4198                 buf.Append (" ");
4199
4200                 MemberFormatter full = new CSharpFullMemberFormatter ();
4201
4202                 if (DocUtils.IsDelegate (type)) {
4203                         buf.Append("delegate ");
4204                         MethodDefinition invoke = type.GetMethod ("Invoke");
4205                         buf.Append (full.GetName (invoke.ReturnType)).Append (" ");
4206                         buf.Append (GetName (type));
4207                         AppendParameters (buf, invoke, invoke.Parameters);
4208                         AppendGenericTypeConstraints (buf, type);
4209                         buf.Append (";");
4210
4211                         return buf.ToString();
4212                 }
4213                 
4214                 if (type.IsAbstract && !type.IsInterface)
4215                         buf.Append("abstract ");
4216                 if (type.IsSealed && !DocUtils.IsDelegate (type) && !type.IsValueType)
4217                         buf.Append("sealed ");
4218                 buf.Replace ("abstract sealed", "static");
4219
4220                 buf.Append (GetTypeKind (type));
4221                 buf.Append (" ");
4222                 buf.Append (GetCSharpType (type.FullName) == null 
4223                                 ? GetName (type) 
4224                                 : type.Name);
4225
4226                 if (!type.IsEnum) {
4227                         TypeReference basetype = type.BaseType;
4228                         if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType)       // FIXME
4229                                 basetype = null;
4230
4231                         List<string> interface_names = DocUtils.GetUserImplementedInterfaces (type)
4232                                         .Select (iface => full.GetName (iface))
4233                                         .OrderBy (s => s)
4234                                         .ToList ();
4235
4236                         if (basetype != null || interface_names.Count > 0)
4237                                 buf.Append (" : ");
4238                         
4239                         if (basetype != null) {
4240                                 buf.Append (full.GetName (basetype));
4241                                 if (interface_names.Count > 0)
4242                                         buf.Append (", ");
4243                         }
4244                         
4245                         for (int i = 0; i < interface_names.Count; i++){
4246                                 if (i != 0)
4247                                         buf.Append (", ");
4248                                 buf.Append (interface_names [i]);
4249                         }
4250                         AppendGenericTypeConstraints (buf, type);
4251                 }
4252
4253                 return buf.ToString ();
4254         }
4255
4256         static string GetTypeKind (TypeDefinition t)
4257         {
4258                 if (t.IsEnum)
4259                         return "enum";
4260                 if (t.IsValueType)
4261                         return "struct";
4262                 if (t.IsClass || t.FullName == "System.Enum")
4263                         return "class";
4264                 if (t.IsInterface)
4265                         return "interface";
4266                 throw new ArgumentException(t.FullName);
4267         }
4268
4269         static string GetTypeVisibility (TypeAttributes ta)
4270         {
4271                 switch (ta & TypeAttributes.VisibilityMask) {
4272                 case TypeAttributes.Public:
4273                 case TypeAttributes.NestedPublic:
4274                         return "public";
4275
4276                 case TypeAttributes.NestedFamily:
4277                 case TypeAttributes.NestedFamORAssem:
4278                         return "protected";
4279
4280                 default:
4281                         return null;
4282                 }
4283         }
4284
4285         protected override StringBuilder AppendGenericTypeConstraints (StringBuilder buf, TypeReference type)
4286         {
4287                 if (type.GenericParameters.Count == 0)
4288                         return buf;
4289                 return AppendConstraints (buf, type.GenericParameters);
4290         }
4291
4292         private StringBuilder AppendConstraints (StringBuilder buf, IList<GenericParameter> genArgs)
4293         {
4294                 foreach (GenericParameter genArg in genArgs) {
4295                         GenericParameterAttributes attrs = genArg.Attributes;
4296                         IList<TypeReference> constraints = genArg.Constraints;
4297                         if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0)
4298                                 continue;
4299
4300                         bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
4301                         bool isvt  = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
4302                         bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
4303                         bool comma = false;
4304
4305                         if (!isref && !isvt && !isnew && constraints.Count == 0)
4306                                 continue;
4307                         buf.Append (" where ").Append (genArg.Name).Append (" : ");
4308                         if (isref) {
4309                                 buf.Append ("class");
4310                                 comma = true;
4311                         }
4312                         else if (isvt) {
4313                                 buf.Append ("struct");
4314                                 comma = true;
4315                         }
4316                         if (constraints.Count > 0 && !isvt) {
4317                                 if (comma)
4318                                         buf.Append (", ");
4319                                 buf.Append (GetTypeName (constraints [0]));
4320                                 for (int i = 1; i < constraints.Count; ++i)
4321                                         buf.Append (", ").Append (GetTypeName (constraints [i]));
4322                         }
4323                         if (isnew && !isvt) {
4324                                 if (comma)
4325                                         buf.Append (", ");
4326                                 buf.Append ("new()");
4327                         }
4328                 }
4329                 return buf;
4330         }
4331
4332         protected override string GetConstructorDeclaration (MethodDefinition constructor)
4333         {
4334                 StringBuilder buf = new StringBuilder ();
4335                 AppendVisibility (buf, constructor);
4336                 if (buf.Length == 0)
4337                         return null;
4338
4339                 buf.Append (' ');
4340                 base.AppendTypeName (buf, constructor.DeclaringType.Name).Append (' ');
4341                 AppendParameters (buf, constructor, constructor.Parameters);
4342                 buf.Append (';');
4343
4344                 return buf.ToString ();
4345         }
4346         
4347         protected override string GetMethodDeclaration (MethodDefinition method)
4348         {
4349                 string decl = base.GetMethodDeclaration (method);
4350                 if (decl != null)
4351                         return decl + ";";
4352                 return null;
4353         }
4354
4355         protected override string GetTypeName (MethodReturnType returnType)
4356         {
4357                 return GetTypeName (returnType, () => returnType.ReturnType);
4358         }
4359
4360         string GetTypeName (ICustomAttributeProvider provider, Func<TypeReference> selector)
4361         {
4362                 string type = GetName (selector ());
4363                 if (type == "object" && provider.HasCustomAttributes &&
4364                                 provider.CustomAttributes.Cast<CustomAttribute>()
4365                                 .Any (ca => ca.GetDeclaringType() == "System.Runtime.CompilerServices.DynamicAttribute"))
4366                         return "dynamic";
4367                 return type;
4368         }
4369
4370         protected override StringBuilder AppendMethodName (StringBuilder buf, MethodDefinition method)
4371         {
4372                 if (DocUtils.IsExplicitlyImplemented (method)) {
4373                         TypeReference iface;
4374                         MethodReference ifaceMethod;
4375                         DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4376                         return buf.Append (new CSharpMemberFormatter ().GetName (iface))
4377                                 .Append ('.')
4378                                 .Append (ifaceMethod.Name);
4379                 }
4380                 return base.AppendMethodName (buf, method);
4381         }
4382
4383         protected override StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
4384         {
4385                 if (method.GenericParameters.Count == 0)
4386                         return buf;
4387                 return AppendConstraints (buf, method.GenericParameters);
4388         }
4389
4390         protected override string RefTypeModifier {
4391                 get {return "";}
4392         }
4393
4394         protected override string GetFinalizerName (MethodDefinition method)
4395         {
4396                 return "~" + method.DeclaringType.Name + " ()"; 
4397         }
4398
4399         protected override StringBuilder AppendVisibility (StringBuilder buf, MethodDefinition method)
4400         {
4401                 if (method == null)
4402                         return buf;
4403                 if (method.IsPublic)
4404                         return buf.Append ("public");
4405                 if (method.IsFamily || method.IsFamilyOrAssembly)
4406                         return buf.Append ("protected");
4407                 return buf;
4408         }
4409
4410         protected override StringBuilder AppendModifiers (StringBuilder buf, MethodDefinition method)
4411         {
4412                 string modifiers = String.Empty;
4413                 if (method.IsStatic) modifiers += " static";
4414                 if (method.IsVirtual && !method.IsAbstract) {
4415                         if ((method.Attributes & MethodAttributes.NewSlot) != 0) modifiers += " virtual";
4416                         else modifiers += " override";
4417                 }
4418                 TypeDefinition declType = (TypeDefinition) method.DeclaringType;
4419                 if (method.IsAbstract && !declType.IsInterface) modifiers += " abstract";
4420                 if (method.IsFinal) modifiers += " sealed";
4421                 if (modifiers == " virtual sealed") modifiers = "";
4422
4423                 return buf.Append (modifiers);
4424         }
4425
4426         protected override StringBuilder AppendGenericMethod (StringBuilder buf, MethodDefinition method)
4427         {
4428                 if (method.IsGenericMethod ()) {
4429                         IList<GenericParameter> args = method.GenericParameters;
4430                         if (args.Count > 0) {
4431                                 buf.Append ("<");
4432                                 buf.Append (args [0].Name);
4433                                 for (int i = 1; i < args.Count; ++i)
4434                                         buf.Append (",").Append (args [i].Name);
4435                                 buf.Append (">");
4436                         }
4437                 }
4438                 return buf;
4439         }
4440
4441         protected override StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters)
4442         {
4443                 return AppendParameters (buf, method, parameters, '(', ')');
4444         }
4445
4446         private StringBuilder AppendParameters (StringBuilder buf, MethodDefinition method, IList<ParameterDefinition> parameters, char begin, char end)
4447         {
4448                 buf.Append (begin);
4449
4450                 if (parameters.Count > 0) {
4451                         if (DocUtils.IsExtensionMethod (method))
4452                                 buf.Append ("this ");
4453                         AppendParameter (buf, parameters [0]);
4454                         for (int i = 1; i < parameters.Count; ++i) {
4455                                 buf.Append (", ");
4456                                 AppendParameter (buf, parameters [i]);
4457                         }
4458                 }
4459
4460                 return buf.Append (end);
4461         }
4462
4463         private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
4464         {
4465                 if (parameter.ParameterType is ByReferenceType) {
4466                         if (parameter.IsOut)
4467                                 buf.Append ("out ");
4468                         else
4469                                 buf.Append ("ref ");
4470                 }
4471                 buf.Append (GetTypeName (parameter, () => parameter.ParameterType)).Append (" ");
4472                 return buf.Append (parameter.Name);
4473         }
4474
4475         protected override string GetPropertyDeclaration (PropertyDefinition property)
4476         {
4477                 MethodDefinition method;
4478
4479                 string get_visible = null;
4480                 if ((method = property.GetMethod) != null && 
4481                                 (DocUtils.IsExplicitlyImplemented (method) || 
4482                                  (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4483                         get_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4484                 string set_visible = null;
4485                 if ((method = property.SetMethod) != null &&
4486                                 (DocUtils.IsExplicitlyImplemented (method) || 
4487                                  (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly)))
4488                         set_visible = AppendVisibility (new StringBuilder (), method).ToString ();
4489
4490                 if ((set_visible == null) && (get_visible == null))
4491                         return null;
4492
4493                 string visibility;
4494                 StringBuilder buf = new StringBuilder ();
4495                 if (get_visible != null && (set_visible == null || (set_visible != null && get_visible == set_visible)))
4496                         buf.Append (visibility = get_visible);
4497                 else if (set_visible != null && get_visible == null)
4498                         buf.Append (visibility = set_visible);
4499                 else
4500                         buf.Append (visibility = "public");
4501
4502                 // Pick an accessor to use for static/virtual/override/etc. checks.
4503                 method = property.SetMethod;
4504                 if (method == null)
4505                         method = property.GetMethod;
4506         
4507                 string modifiers = String.Empty;
4508                 if (method.IsStatic) modifiers += " static";
4509                 if (method.IsVirtual && !method.IsAbstract) {
4510                                 if ((method.Attributes & MethodAttributes.NewSlot) != 0)
4511                                         modifiers += " virtual";
4512                                 else
4513                                         modifiers += " override";
4514                 }
4515                 TypeDefinition declDef = (TypeDefinition) method.DeclaringType;
4516                 if (method.IsAbstract && !declDef.IsInterface)
4517                         modifiers += " abstract";
4518                 if (method.IsFinal)
4519                         modifiers += " sealed";
4520                 if (modifiers == " virtual sealed")
4521                         modifiers = "";
4522                 buf.Append (modifiers).Append (' ');
4523
4524                 buf.Append (GetName (property.PropertyType)).Append (' ');
4525
4526                 IEnumerable<MemberReference> defs = property.DeclaringType.GetDefaultMembers ();
4527                 string name = property.Name;
4528                 foreach (MemberReference mi in defs) {
4529                         if (mi == property) {
4530                                 name = "this";
4531                                 break;
4532                         }
4533                 }
4534                 buf.Append (name == "this" ? name : DocUtils.GetPropertyName (property));
4535         
4536                 if (property.Parameters.Count != 0) {
4537                         AppendParameters (buf, method, property.Parameters, '[', ']');
4538                 }
4539
4540                 buf.Append (" {");
4541                 if (get_visible != null) {
4542                         if (get_visible != visibility)
4543                                 buf.Append (' ').Append (get_visible);
4544                         buf.Append (" get;");
4545                 }
4546                 if (set_visible != null) {
4547                         if (set_visible != visibility)
4548                                 buf.Append (' ').Append (set_visible);
4549                         buf.Append (" set;");
4550                 }
4551                 buf.Append (" }");
4552         
4553                 return buf [0] != ' ' ? buf.ToString () : buf.ToString (1, buf.Length-1);
4554         }
4555
4556         protected override string GetFieldDeclaration (FieldDefinition field)
4557         {
4558                 TypeDefinition declType = (TypeDefinition) field.DeclaringType;
4559                 if (declType.IsEnum && field.Name == "value__")
4560                         return null; // This member of enums aren't documented.
4561
4562                 StringBuilder buf = new StringBuilder ();
4563                 AppendFieldVisibility (buf, field);
4564                 if (buf.Length == 0)
4565                         return null;
4566
4567                 if (declType.IsEnum)
4568                         return field.Name;
4569
4570                 if (field.IsStatic && !field.IsLiteral)
4571                         buf.Append (" static");
4572                 if (field.IsInitOnly)
4573                         buf.Append (" readonly");
4574                 if (field.IsLiteral)
4575                         buf.Append (" const");
4576
4577                 buf.Append (' ').Append (GetName (field.FieldType)).Append (' ');
4578                 buf.Append (field.Name);
4579                 AppendFieldValue (buf, field);
4580                 buf.Append (';');
4581
4582                 return buf.ToString ();
4583         }
4584
4585         static StringBuilder AppendFieldVisibility (StringBuilder buf, FieldDefinition field)
4586         {
4587                 if (field.IsPublic)
4588                         return buf.Append ("public");
4589                 if (field.IsFamily || field.IsFamilyOrAssembly)
4590                         return buf.Append ("protected");
4591                 return buf;
4592         }
4593
4594         static StringBuilder AppendFieldValue (StringBuilder buf, FieldDefinition field)
4595         {
4596                 // enums have a value__ field, which we ignore
4597                 if (((TypeDefinition ) field.DeclaringType).IsEnum || 
4598                                 field.DeclaringType.IsGenericType ())
4599                         return buf;
4600                 if (field.HasConstant && field.IsLiteral) {
4601                         object val = null;
4602                         try {
4603                                 val   = field.Constant;
4604                         } catch {
4605                                 return buf;
4606                         }
4607                         if (val == null)
4608                                 buf.Append (" = ").Append ("null");
4609                         else if (val is Enum)
4610                                 buf.Append (" = ").Append (val.ToString ());
4611                         else if (val is IFormattable) {
4612                                 string value = ((IFormattable)val).ToString();
4613                                 if (val is string)
4614                                         value = "\"" + value + "\"";
4615                                 buf.Append (" = ").Append (value);
4616                         }
4617                 }
4618                 return buf;
4619         }
4620
4621         protected override string GetEventDeclaration (EventDefinition e)
4622         {
4623                 StringBuilder buf = new StringBuilder ();
4624                 if (AppendVisibility (buf, e.AddMethod).Length == 0) {
4625                         return null;
4626                 }
4627
4628                 AppendModifiers (buf, e.AddMethod);
4629
4630                 buf.Append (" event ");
4631                 buf.Append (GetName (e.EventType)).Append (' ');
4632                 buf.Append (e.Name).Append (';');
4633
4634                 return buf.ToString ();
4635         }
4636 }
4637
4638 class CSharpMemberFormatter : CSharpFullMemberFormatter {
4639         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4640         {
4641                 return buf;
4642         }
4643 }
4644
4645 class DocTypeFullMemberFormatter : MemberFormatter {
4646         public static readonly MemberFormatter Default = new DocTypeFullMemberFormatter ();
4647
4648         protected override char NestedTypeSeparator {
4649                 get {return '+';}
4650         }
4651 }
4652
4653 class DocTypeMemberFormatter : DocTypeFullMemberFormatter {
4654         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4655         {
4656                 return buf;
4657         }
4658 }
4659
4660 class SlashDocMemberFormatter : MemberFormatter {
4661
4662         protected override char[] GenericTypeContainer {
4663                 get {return new char[]{'{', '}'};}
4664         }
4665
4666         private bool AddTypeCount = true;
4667
4668         private TypeReference genDeclType;
4669         private MethodReference genDeclMethod;
4670
4671         protected override StringBuilder AppendTypeName (StringBuilder buf, TypeReference type)
4672         {
4673                 if (type is GenericParameter) {
4674                         int l = buf.Length;
4675                         if (genDeclType != null) {
4676                                 IList<GenericParameter> genArgs = genDeclType.GenericParameters;
4677                                 for (int i = 0; i < genArgs.Count; ++i) {
4678                                         if (genArgs [i].Name == type.Name) {
4679                                                 buf.Append ('`').Append (i);
4680                                                 break;
4681                                         }
4682                                 }
4683                         }
4684                         if (genDeclMethod != null) {
4685                                 IList<GenericParameter> genArgs = null;
4686                                 if (genDeclMethod.IsGenericMethod ()) {
4687                                         genArgs = genDeclMethod.GenericParameters;
4688                                         for (int i = 0; i < genArgs.Count; ++i) {
4689                                                 if (genArgs [i].Name == type.Name) {
4690                                                         buf.Append ("``").Append (i);
4691                                                         break;
4692                                                 }
4693                                         }
4694                                 }
4695                         }
4696                         if (genDeclType == null && genDeclMethod == null) {
4697                                 // Probably from within an explicitly implemented interface member,
4698                                 // where CSC uses parameter names instead of indices (why?), e.g.
4699                                 // MyList`2.Mono#DocTest#Generic#IFoo{A}#Method``1(`0,``0) instead of
4700                                 // MyList`2.Mono#DocTest#Generic#IFoo{`0}#Method``1(`0,``0).
4701                                 buf.Append (type.Name);
4702                         }
4703                         if (buf.Length == l) {
4704                                 throw new Exception (string.Format (
4705                                                 "Unable to translate generic parameter {0}; genDeclType={1}, genDeclMethod={2}", 
4706                                                 type.Name, genDeclType, genDeclMethod));
4707                         }
4708                 }
4709                 else {
4710                         base.AppendTypeName (buf, type);
4711                         if (AddTypeCount) {
4712                                 int numArgs = type.GenericParameters.Count;
4713                                 if (type.DeclaringType != null)
4714                                         numArgs -= type.GenericParameters.Count;
4715                                 if (numArgs > 0) {
4716                                         buf.Append ('`').Append (numArgs);
4717                                 }
4718                         }
4719                 }
4720                 return buf;
4721         }
4722
4723         protected override StringBuilder AppendArrayModifiers (StringBuilder buf, ArrayType array)
4724         {
4725                 buf.Append (ArrayDelimeters [0]);
4726                 int rank = array.Rank;
4727                 if (rank > 1) {
4728                         buf.Append ("0:");
4729                         for (int i = 1; i < rank; ++i) {
4730                                 buf.Append (",0:");
4731                         }
4732                 }
4733                 return buf.Append (ArrayDelimeters [1]);
4734         }
4735
4736         protected override StringBuilder AppendGenericType (StringBuilder buf, TypeReference type)
4737         {
4738                 if (!AddTypeCount)
4739                         base.AppendGenericType (buf, type);
4740                 else
4741                         AppendType (buf, type);
4742                 return buf;
4743         }
4744
4745         private StringBuilder AppendType (StringBuilder buf, TypeReference type)
4746         {
4747                 List<TypeReference> decls = DocUtils.GetDeclaringTypes (type);
4748                 bool insertNested = false;
4749                 int prevParamCount = 0;
4750                 foreach (var decl in decls) {
4751                         if (insertNested)
4752                                 buf.Append (NestedTypeSeparator);
4753                         insertNested = true;
4754                         base.AppendTypeName (buf, decl);
4755                         int argCount = DocUtils.GetGenericArgumentCount (decl);
4756                         int numArgs = argCount - prevParamCount;
4757                         prevParamCount = argCount;
4758                         if (numArgs > 0)
4759                                 buf.Append ('`').Append (numArgs);
4760                 }
4761                 return buf;
4762         }
4763
4764         public override string GetDeclaration (MemberReference member)
4765         {
4766                 TypeReference r = member as TypeReference;
4767                 if (r != null) {
4768                         return "T:" + GetTypeName (r);
4769                 }
4770                 return base.GetDeclaration (member);
4771         }
4772
4773         protected override string GetConstructorName (MethodReference constructor)
4774         {
4775                 return GetMethodDefinitionName (constructor, "#ctor");
4776         }
4777
4778         protected override string GetMethodName (MethodReference method)
4779         {
4780                 string name = null;
4781                 MethodDefinition methodDef = method as MethodDefinition;
4782                 if (methodDef == null || !DocUtils.IsExplicitlyImplemented (methodDef))
4783                         name = method.Name;
4784                 else {
4785                         TypeReference iface;
4786                         MethodReference ifaceMethod;
4787                         DocUtils.GetInfoForExplicitlyImplementedMethod (methodDef, out iface, out ifaceMethod);
4788                         AddTypeCount = false;
4789                         name = GetTypeName (iface) + "." + ifaceMethod.Name;
4790                         AddTypeCount = true;
4791                 }
4792                 return GetMethodDefinitionName (method, name);
4793         }
4794
4795         private string GetMethodDefinitionName (MethodReference method, string name)
4796         {
4797                 StringBuilder buf = new StringBuilder ();
4798                 buf.Append (GetTypeName (method.DeclaringType));
4799                 buf.Append ('.');
4800                 buf.Append (name.Replace (".", "#"));
4801                 if (method.IsGenericMethod ()) {
4802                         IList<GenericParameter> genArgs = method.GenericParameters;
4803                         if (genArgs.Count > 0)
4804                                 buf.Append ("``").Append (genArgs.Count);
4805                 }
4806                 IList<ParameterDefinition> parameters = method.Parameters;
4807                 try {
4808                         genDeclType   = method.DeclaringType;
4809                         genDeclMethod = method;
4810                         AppendParameters (buf, method.DeclaringType.GenericParameters, parameters);
4811                 }
4812                 finally {
4813                         genDeclType   = null;
4814                         genDeclMethod = null;
4815                 }
4816                 return buf.ToString ();
4817         }
4818
4819         private StringBuilder AppendParameters (StringBuilder buf, IList<GenericParameter> genArgs, IList<ParameterDefinition> parameters)
4820         {
4821                 if (parameters.Count == 0)
4822                         return buf;
4823
4824                 buf.Append ('(');
4825
4826                 AppendParameter (buf, genArgs, parameters [0]);
4827                 for (int i = 1; i < parameters.Count; ++i) {
4828                         buf.Append (',');
4829                         AppendParameter (buf, genArgs, parameters [i]);
4830                 }
4831
4832                 return buf.Append (')');
4833         }
4834
4835         private StringBuilder AppendParameter (StringBuilder buf, IList<GenericParameter> genArgs, ParameterDefinition parameter)
4836         {
4837                 AddTypeCount = false;
4838                 buf.Append (GetTypeName (parameter.ParameterType));
4839                 AddTypeCount = true;
4840                 return buf;
4841         }
4842
4843         protected override string GetPropertyName (PropertyReference property)
4844         {
4845                 string name = null;
4846
4847                 PropertyDefinition propertyDef = property as PropertyDefinition;
4848                 MethodDefinition method = null;
4849                 if (propertyDef != null)
4850                         method = propertyDef.GetMethod ?? propertyDef.SetMethod;
4851                 if (method != null && !DocUtils.IsExplicitlyImplemented (method))
4852                         name = property.Name;
4853                 else {
4854                         TypeReference iface;
4855                         MethodReference ifaceMethod;
4856                         DocUtils.GetInfoForExplicitlyImplementedMethod (method, out iface, out ifaceMethod);
4857                         AddTypeCount = false;
4858                         name = string.Join ("#", new string[]{
4859                                         GetTypeName (iface).Replace (".", "#"),
4860                                         DocUtils.GetMember (property.Name)
4861                         });
4862                         AddTypeCount = true;
4863                 }
4864
4865                 StringBuilder buf = new StringBuilder ();
4866                 buf.Append (GetName (property.DeclaringType));
4867                 buf.Append ('.');
4868                 buf.Append (name);
4869                 IList<ParameterDefinition> parameters = property.Parameters;
4870                 if (parameters.Count > 0) {
4871                         genDeclType = property.DeclaringType;
4872                         buf.Append ('(');
4873                         IList<GenericParameter> genArgs = property.DeclaringType.GenericParameters;
4874                         AppendParameter (buf, genArgs, parameters [0]);
4875                         for (int i = 1; i < parameters.Count; ++i) {
4876                                  buf.Append (',');
4877                                  AppendParameter (buf, genArgs, parameters [i]);
4878                         }
4879                         buf.Append (')');
4880                         genDeclType = null;
4881                 }
4882                 return buf.ToString ();
4883         }
4884
4885         protected override string GetFieldName (FieldReference field)
4886         {
4887                 return string.Format ("{0}.{1}",
4888                         GetName (field.DeclaringType), field.Name);
4889         }
4890
4891         protected override string GetEventName (EventReference e)
4892         {
4893                 return string.Format ("{0}.{1}",
4894                         GetName (e.DeclaringType), e.Name);
4895         }
4896
4897         protected override string GetTypeDeclaration (TypeDefinition type)
4898         {
4899                 string name = GetName (type);
4900                 if (type == null)
4901                         return null;
4902                 return "T:" + name;
4903         }
4904
4905         protected override string GetConstructorDeclaration (MethodDefinition constructor)
4906         {
4907                 string name = GetName (constructor);
4908                 if (name == null)
4909                         return null;
4910                 return "M:" + name;
4911         }
4912
4913         protected override string GetMethodDeclaration (MethodDefinition method)
4914         {
4915                 string name = GetName (method);
4916                 if (name == null)
4917                         return null;
4918                 if (method.Name == "op_Implicit" || method.Name == "op_Explicit") {
4919                         genDeclType = method.DeclaringType;
4920                         genDeclMethod = method;
4921                         name += "~" + GetName (method.ReturnType);
4922                         genDeclType = null;
4923                         genDeclMethod = null;
4924                 }
4925                 return "M:" + name;
4926         }
4927
4928         protected override string GetPropertyDeclaration (PropertyDefinition property)
4929         {
4930                 string name = GetName (property);
4931                 if (name == null)
4932                         return null;
4933                 return "P:" + name;
4934         }
4935
4936         protected override string GetFieldDeclaration (FieldDefinition field)
4937         {
4938                 string name = GetName (field);
4939                 if (name == null)
4940                         return null;
4941                 return "F:" + name;
4942         }
4943
4944         protected override string GetEventDeclaration (EventDefinition e)
4945         {
4946                 string name = GetName (e);
4947                 if (name == null)
4948                         return null;
4949                 return "E:" + name;
4950         }
4951 }
4952
4953 class FileNameMemberFormatter : SlashDocMemberFormatter {
4954         protected override StringBuilder AppendNamespace (StringBuilder buf, TypeReference type)
4955         {
4956                 return buf;
4957         }
4958
4959         protected override char NestedTypeSeparator {
4960                 get {return '+';}
4961         }
4962 }
4963
4964 }