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