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