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