Merge pull request #1949 from lewurm/fixtype
[mono.git] / mcs / tools / mdoc / Mono.Documentation / monodocer.cs
index 75a6d5ca63e3306dfd27c5996a930a3118ebe725..7d54a28947b1f8da02de4832e9b490b9df27175a 100644 (file)
@@ -120,7 +120,7 @@ namespace Mono.Documentation {
                        string typename = t.FullName;
 
                        bool isInAssembly = MDocUpdater.IsInAssemblies (t.Module.Name);
-                       if (isInAssembly && MDocUpdater.HasDroppedNamespace () && !typename.StartsWith ("System")) {
+                       if (isInAssembly && !typename.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (t)) {
                                string nameWithDropped = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typename);
                                return nameWithDropped;
                        }
@@ -166,9 +166,35 @@ class MDocUpdater : MDocCommand
        HashSet<string> forwardedTypes = new HashSet<string> ();
 
        public static string droppedNamespace = string.Empty;
-       public static bool HasDroppedNamespace() {
+
+       public static bool HasDroppedNamespace(TypeDefinition forType) 
+       {
+               return HasDroppedNamespace(forType.Module);
+       }
+
+       public static bool HasDroppedNamespace(MemberReference forMember) 
+       {
+               return HasDroppedNamespace(forMember.Module);
+       }
+
+       public static bool HasDroppedNamespace(AssemblyDefinition forAssembly) 
+       {
+               return HasDroppedNamespace(forAssembly.MainModule);
+       }
+
+       public static bool HasDroppedNamespace(ModuleDefinition forModule) 
+       {
+               return !string.IsNullOrWhiteSpace (droppedNamespace) && droppedAssemblies.Any(da => da == forModule.Name);
+       }
+
+       public static bool HasDroppedAnyNamespace ()
+       {
                return !string.IsNullOrWhiteSpace (droppedNamespace);
        }
+
+       
+       static List<string> droppedAssemblies = new List<string>();
+
        public string PreserveTag { get; set; }
        public static MDocUpdater Instance { get; private set; }
        public static bool SwitchingToMagicTypes { get; private set; }
@@ -236,8 +262,16 @@ class MDocUpdater : MDocCommand
                          "Only update documentation for {TYPE}.",
                                v => types.Add (v) },
                        { "dropns=",
-                               "Instructs the update process that {NAMESPACE} has been dropped, so that types and members will match existing documentation nodes.",
-                               v => droppedNamespace = v },
+                         "When processing assembly {ASSEMBLY}, strip off leading namespace {PREFIX}:\n" +
+                         "  e.g. --dropns ASSEMBLY=PREFIX",
+                         v => {
+                           var parts = v.Split ('=');
+                           if (parts.Length != 2) { Console.Error.WriteLine ("Invalid dropns input"); return; }
+                           var assembly = Path.GetFileName (parts [0].Trim ());
+                           var prefix = parts [1].Trim();
+                           droppedAssemblies.Add (assembly);
+                           droppedNamespace = prefix;
+                       } },
                        { "ntypes",
                                "If the new assembly is switching to 'magic types', then this switch should be defined.",
                                v => SwitchingToMagicTypes = true },
@@ -415,8 +449,10 @@ class MDocUpdater : MDocCommand
                        }
                        if (r == null)
                                continue;
-                       c.Remove (n);
-                       c.InsertBefore (n, r);
+                       if (c [n.Name] != null) {
+                               c.RemoveNamedItem (n.Name);
+                               c.InsertBefore (n, r);
+                       }
                }
        }
        
@@ -580,7 +616,7 @@ class MDocUpdater : MDocCommand
                        nsname
                };
 
-               if (MDocUpdater.HasDroppedNamespace ()) {
+               if (MDocUpdater.HasDroppedNamespace (type)) {
                        // If dropping namespace, types may have moved into a couple of different places.
                        var newSearchLocations = searchLocations.Union (new string[] {
                                string.Format ("{0}.{1}", droppedNamespace, nsname),
@@ -642,12 +678,6 @@ class MDocUpdater : MDocCommand
                        XmlElement td = StubType(type, output);
                        if (td == null)
                                return null;
-                       
-                       System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace));
-                       if (!dir.Exists) {
-                               dir.Create();
-                               Console.WriteLine("Namespace Directory Created: " + type.Namespace);
-                       }
                }
                return reltypefile;
        }
@@ -847,10 +877,10 @@ class MDocUpdater : MDocCommand
                        
                        // Add namespace and type nodes into the index file as needed
                        AddIndexType (type, index_types);
-                               
+
                        // Ensure the namespace index file exists
                        string namespaceToUse = type.Namespace;
-                       if (HasDroppedNamespace()) {
+                       if (HasDroppedNamespace(assembly)) {
                                namespaceToUse = string.Format ("{0}.{1}", droppedNamespace, namespaceToUse);
                        }
                        string onsdoc = DocUtils.PathCombine (dest, namespaceToUse + ".xml");
@@ -951,7 +981,7 @@ class MDocUpdater : MDocCommand
                throw new ArgumentException ("Unknown kind for type: " + type.FullName);
        }
 
-       private static bool IsPublic (TypeDefinition type)
+       public static bool IsPublic (TypeDefinition type)
        {
                TypeDefinition decl = type;
                while (decl != null) {
@@ -974,18 +1004,66 @@ class MDocUpdater : MDocCommand
                                        XmlDocument doc = new XmlDocument ();
                                        doc.Load (typefile.FullName);
                                        XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
-                                       if (e != null && !no_assembly_versions && UpdateAssemblyVersions(e, GetAssemblyVersions(), false)) {
+                                       string assemblyName = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName").InnerText;
+                                       AssemblyDefinition assembly = assemblies.FirstOrDefault (a => a.Name.Name == assemblyName);
+
+                                       Action saveDoc = () => {
                                                using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate))
                                                        WriteXml(doc.DocumentElement, writer);
+                                       };
+
+                                       if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions (e, assembly, GetAssemblyVersions(assemblyName), false)) {
+                                               saveDoc ();
                                                goodfiles.Add (relTypeFile);
                                                continue;
                                        }
 
-                                       if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was no -preserve
+                                       Action actuallyDelete = () => {
                                                string newname = typefile.FullName + ".remove";
-                                               try { System.IO.File.Delete(newname); } catch (Exception) { }
-                                               try { typefile.MoveTo(newname); } catch (Exception) { }
-                                               Console.WriteLine("Class no longer present; file renamed: " + Path.Combine(nsdir.Name, typefile.Name));
+                                               try { System.IO.File.Delete (newname); } catch (Exception) { Warning ("Unable to delete existing file: {0}", newname); }
+                                               try { typefile.MoveTo (newname); } catch (Exception) { Warning ("Unable to rename to: {0}", newname); }
+                                               Console.WriteLine ("Class no longer present; file renamed: " + Path.Combine (nsdir.Name, typefile.Name));
+                                       };
+
+                                       if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was not a -preserve
+                                               saveDoc ();
+
+                                               var unifiedAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='unified']");
+                                               var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='classic']");
+                                               var unifiedMembers = doc.SelectNodes ("//Member[@apistyle='unified']|//Member/AssemblyInfo[@apistyle='unified']");
+                                               var classicMembers = doc.SelectNodes ("//Member[@apistyle='classic']|//Member/AssemblyInfo[@apistyle='classic']");
+                                               bool isUnifiedRun = HasDroppedAnyNamespace ();
+                                               bool isClassicOrNormalRun = !isUnifiedRun;
+
+                                               Action<XmlNode, ApiStyle> removeStyles = (x, style) => {
+                                                       var styledNodes = doc.SelectNodes("//*[@apistyle='"+ style.ToString ().ToLowerInvariant () +"']");
+                                                       if (styledNodes != null && styledNodes.Count > 0) {
+                                                               foreach(var node in styledNodes.Cast<XmlNode> ()) {
+                                                                       node.ParentNode.RemoveChild (node);
+                                                               }
+                                                       }
+                                                       saveDoc ();
+                                               };
+                                               if (isClassicOrNormalRun) {
+                                                       if (unifiedAssemblyNode != null || unifiedMembers.Count > 0) {
+                                                               Warning ("*** this type is marked as unified, not deleting during this run: {0}", typefile.FullName);
+                                                               // if truly removed from both assemblies, it will be removed fully during the unified run
+                                                               removeStyles (doc, ApiStyle.Classic);
+                                                               continue;
+                                                       } else {
+                                                               // we should be safe to delete here because it was not marked as a unified assembly
+                                                               actuallyDelete ();
+                                                       }
+                                               }
+                                               if (isUnifiedRun) {
+                                                       if (classicAssemblyNode != null || classicMembers.Count > 0) {
+                                                               Warning ("*** this type is marked as classic, not deleting {0}", typefile.FullName);
+                                                               continue; 
+                                                       } else {
+                                                               // safe to delete because it wasn't marked as a classic assembly, so the type is gone in both.
+                                                               actuallyDelete ();
+                                                       }
+                                               }
                                        }
                                }
                        }
@@ -1002,9 +1080,11 @@ class MDocUpdater : MDocCommand
                return w;
        }
 
-       private string[] GetAssemblyVersions ()
+       private string[] GetAssemblyVersions (string assemblyName)
        {
-               return (from a in assemblies select GetAssemblyVersion (a)).ToArray ();
+               return (from a in assemblies 
+                       where a.Name.Name == assemblyName 
+                       select GetAssemblyVersion (a)).ToArray ();
        }
 
        private static void CleanupIndexTypes (XmlElement index_types, HashSet<string> goodfiles)
@@ -1079,6 +1159,16 @@ class MDocUpdater : MDocCommand
                foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) {
                        XmlElement oldmember  = info.Node;
                        MemberReference oldmember2 = info.Member;
+
+                       if (info.Member != null &&  info.Node != null) {
+                               // Check for an error condition where the xml MemberName doesn't match the matched member
+                               var memberName = GetMemberName (info.Member);
+                               var memberAttribute = info.Node.Attributes ["MemberName"];
+                               if (memberAttribute == null || (memberAttribute.Value != memberName && memberAttribute.Value.Split (',').Length != memberName.Split (',').Length)) {
+                                       oldmember.SetAttribute ("MemberName", memberName);
+                               }
+                       }
+
                        string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null;
                        
                        // Interface implementations and overrides are deleted from the docs
@@ -1088,10 +1178,10 @@ class MDocUpdater : MDocCommand
                        
                        // Deleted (or signature changed)
                        if (oldmember2 == null) {
-                               if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
+                               if (!no_assembly_versions && UpdateAssemblyVersions (oldmember, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, false))
                                        continue;
 
-                               DeleteMember ("Member Removed", output, oldmember, todelete);
+                               DeleteMember ("Member Removed", output, oldmember, todelete, type);
                                continue;
                        }
                        
@@ -1101,7 +1191,7 @@ class MDocUpdater : MDocCommand
                                        // ignore, already seen
                                }
                                else if (DefaultMemberComparer.Compare (oldmember, seenmembers [sig]) == 0)
-                                       DeleteMember ("Duplicate Member Found", output, oldmember, todelete);
+                                       DeleteMember ("Duplicate Member Found", output, oldmember, todelete, type);
                                else
                                        Warning ("TODO: found a duplicate member '{0}', but it's not identical to the prior member found!", sig);
                                continue;
@@ -1131,6 +1221,27 @@ class MDocUpdater : MDocCommand
                                                string sig = memberFormatters [0].GetDeclaration (m);
                                                if (sig == null) return false;
                                                if (seenmembers.ContainsKey(sig)) return false;
+
+                                               // Verify that the member isn't an explicitly implemented 
+                                               // member of an internal interface, in which case we shouldn't return true.
+                                               MethodDefinition methdef = null;
+                                               if (m is MethodDefinition) 
+                                                       methdef = m as MethodDefinition;
+                                               else if (m is PropertyDefinition) {
+                                                       var prop = m as PropertyDefinition;
+                                                       methdef = prop.GetMethod ?? prop.SetMethod;
+                                               }
+
+                                               if (methdef != null) {
+                                                       TypeReference iface;
+                                                       MethodReference imethod;
+
+                                                       if (methdef.Overrides.Count == 1) {
+                                                               DocUtils.GetInfoForExplicitlyImplementedMethod (methdef, out iface, out imethod);
+                                                               if (!IsPublic (iface.Resolve ())) return false;
+                                                       }
+                                               }
+
                                                return true;
                                        })
                                        .ToArray();
@@ -1232,39 +1343,59 @@ class MDocUpdater : MDocCommand
                return null;
        }
 
-       void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete)
+       void DeleteMember (string reason, string output, XmlNode member, MyXmlNodeList todelete, TypeDefinition type)
        {
                string format = output != null
                        ? "{0}: File='{1}'; Signature='{4}'"
                        : "{0}: XPath='/Type[@FullName=\"{2}\"]/Members/Member[@MemberName=\"{3}\"]'; Signature='{4}'";
+               string signature = member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value;
                Warning (format,
                                reason, 
                                output,
                                member.OwnerDocument.DocumentElement.GetAttribute ("FullName"),
                                member.Attributes ["MemberName"].Value, 
-                               member.SelectSingleNode ("MemberSignature[@Language='C#']/@Value").Value);
-                       if (!delete && MemberDocsHaveUserContent (member)) {
-                               Warning ("Member deletions must be enabled with the --delete option.");
-                       } else if (HasDroppedNamespace ()) {
-                               // if we're dropping the namespace, add the "classic style"
-                               var existingAttribute = member.Attributes ["apistyle"];
-                               if (existingAttribute != null) {
-                                       existingAttribute.Value = "classic";
-                               } else {
-                                       // add the attribute and do not remove
-                                       XmlAttribute apistyleAttr = member.OwnerDocument.CreateAttribute ("apistyle");
+                               signature);
 
-                                       apistyleAttr.Value = "classic";
+               // Identify all of the different states that could affect our decision to delete the member
+               bool shouldPreserve = !string.IsNullOrWhiteSpace (PreserveTag);
+               bool hasContent = MemberDocsHaveUserContent (member);
+               bool shouldDelete = !shouldPreserve && (delete || !hasContent);
 
-                                       member.Attributes.Append (apistyleAttr);
-                               }
-                       } else if (!HasDroppedNamespace () && member.Attributes ["apistyle"] != null && member.Attributes ["apistyle"].Value == "unified") {
-                               // do nothing if there's an apistyle=new attribute and we haven't dropped the namespace
-                       } else if (!string.IsNullOrWhiteSpace (PreserveTag)) {
-                               // do nothing
-                       } else {
+               bool unifiedRun = HasDroppedNamespace (type);
+
+               var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='classic']");
+               bool nodeIsClassic = classicAssemblyInfo != null || member.HasApiStyle (ApiStyle.Classic);
+               var unifiedAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
+               bool nodeIsUnified = unifiedAssemblyInfo != null || member.HasApiStyle (ApiStyle.Unified);
+
+               Action actuallyDelete = () => {
                        todelete.Add (member);
                        deletions++;
+               };
+
+               if (!shouldDelete) {
+                       // explicitly not deleting
+                       string message = shouldPreserve ? 
+                                       "Not deleting '{0}' due to --preserve." :
+                                       "Not deleting '{0}'; must be enabled with the --delete option";
+                       Warning (message, signature);
+               } else if (unifiedRun && nodeIsClassic) {
+                       // this is a unified run, and the member doesn't exist, but is marked as being in the classic assembly.
+                       member.RemoveApiStyle (ApiStyle.Unified);
+                       Warning ("Not removing '{0}' since it's still in the classic assembly.", signature);
+               } else if (unifiedRun && !nodeIsClassic) {
+                       // unified run, and the node is not classic, which means it doesn't exist anywhere.
+                       actuallyDelete ();
+               } else { 
+                       if (!nodeIsClassic && !nodeIsUnified) { // regular codepath (ie. not classic/unified)
+                               actuallyDelete ();
+                       } else { // this is a classic run
+                               Warning ("Removing classic from '{0}' ... will be removed in the unified run if not present there.", signature);
+                               member.RemoveApiStyle (ApiStyle.Classic);
+                               if (classicAssemblyInfo != null) {
+                                       member.RemoveChild (classicAssemblyInfo);
+                               }
+                       }
                }
        }
 
@@ -1400,10 +1531,11 @@ class MDocUpdater : MDocCommand
                                        var node = WriteElementAttribute (root, element, "Language", f.Language, forceNewElement: true);
                                        var newnode = WriteElementAttribute (root, node, "Value", valueToUse);
                                        return newnode;
-                               });
+                               },
+                               type);
                }
                
-               string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace () ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
+               string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
 
                AddXmlNode(
                        root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().ToArray (),
@@ -1412,12 +1544,13 @@ class MDocUpdater : MDocCommand
                        () => {
                                XmlElement ass = WriteElement(root, "AssemblyInfo", forceNewElement:true);
                                
-                               if (MDocUpdater.HasDroppedNamespace ()) ass.SetAttribute ("apistyle", "unified");
+                               if (MDocUpdater.HasDroppedNamespace (type)) ass.SetAttribute ("apistyle", "unified");
 
                                
 
                                return ass;
-                       });
+                       },
+                       type);
 
                foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ())
                {
@@ -1448,7 +1581,7 @@ class MDocUpdater : MDocCommand
                }
                
                if (type.IsGenericType ()) {
-                               MakeTypeParameters (root, type.GenericParameters, MDocUpdater.HasDroppedNamespace());
+                               MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
                } else {
                        ClearElement(root, "TypeParameters");
                }
@@ -1505,9 +1638,10 @@ class MDocUpdater : MDocCommand
                        MakeAttributes (root, GetCustomAttributes (type), type);
                
                if (DocUtils.IsDelegate (type)) {
-                               MakeTypeParameters (root, type.GenericParameters, MDocUpdater.HasDroppedNamespace());
-                       MakeParameters(root, type.GetMethod("Invoke").Parameters);
-                       MakeReturnValue(root, type.GetMethod("Invoke"));
+                       MakeTypeParameters (root, type.GenericParameters, type, MDocUpdater.HasDroppedNamespace(type));
+                       var member = type.GetMethod ("Invoke");
+                       MakeParameters(root, member, member.Parameters);
+                       MakeReturnValue(root, member);
                }
                
                DocsNodeInfo typeInfo = new DocsNodeInfo (WriteElement(root, "Docs"), type);
@@ -1567,7 +1701,8 @@ class MDocUpdater : MDocCommand
                                        var node = WriteElementAttribute (me, element, "Language", f.Language, forceNewElement:true);
                                        var newNode = WriteElementAttribute (me, node, "Value", valueToUse);
                                        return newNode;
-                               });
+                               },
+                               mi);
 
                }
 
@@ -1582,13 +1717,13 @@ class MDocUpdater : MDocCommand
 
                MakeAttributes (me, GetCustomAttributes (mi), mi.DeclaringType);
 
-               MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace());
+               MakeReturnValue(me, mi, MDocUpdater.HasDroppedNamespace(mi));
                if (mi is MethodReference) {
                        MethodReference mb = (MethodReference) mi;
                        if (mb.IsGenericMethod ())
-                                       MakeTypeParameters (me, mb.GenericParameters, MDocUpdater.HasDroppedNamespace());
+                                       MakeTypeParameters (me, mb.GenericParameters, mi, MDocUpdater.HasDroppedNamespace(mi));
                }
-               MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace());
+               MakeParameters(me, mi, MDocUpdater.HasDroppedNamespace(mi));
                
                string fieldValue;
                if (mi is FieldDefinition && GetFieldConstValue ((FieldDefinition)mi, out fieldValue))
@@ -1600,14 +1735,22 @@ class MDocUpdater : MDocCommand
                UpdateExtensionMethods (me, info);
        }
 
+       static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, MemberReference member) {
+               AddXmlNode (relevant, valueMatches, setValue, makeNewNode, member.Module);
+       }
+
+       static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, TypeDefinition type) {
+               AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
+       }
+
        /// <summary>Adds an xml node, reusing the node if it's available</summary>
        /// <param name="relevant">The existing set of nodes</param>
        /// <param name="valueMatches">Checks to see if the node's value matches what you're trying to write.</param>
        /// <param name="setValue">Sets the node's value</param>
        /// <param name="makeNewNode">Creates a new node, if valueMatches returns false.</param>
-       static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode)
+       static void AddXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
        {
-               bool shouldDuplicate = MDocUpdater.HasDroppedNamespace ();
+               bool shouldDuplicate = MDocUpdater.HasDroppedNamespace (module);
                var styleToUse = shouldDuplicate ? ApiStyle.Unified : ApiStyle.Classic;
                var existing = relevant;
                bool done = false;
@@ -2164,7 +2307,7 @@ class MDocUpdater : MDocCommand
                TypeDefinition type = member as TypeDefinition;
                if (type == null)
                        type = member.DeclaringType as TypeDefinition;
-               return UpdateAssemblyVersions(root, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
+               return UpdateAssemblyVersions(root, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
        }
        
        private static string GetAssemblyVersion (AssemblyDefinition assembly)
@@ -2172,7 +2315,7 @@ class MDocUpdater : MDocCommand
                return assembly.Name.Version.ToString();
        }
        
-       private static bool UpdateAssemblyVersions(XmlElement root, string[] assemblyVersions, bool add)
+       private static bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
        {
                XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
                if (av != null) {
@@ -2182,14 +2325,14 @@ class MDocUpdater : MDocCommand
 
                string oldNodeFilter = "AssemblyInfo[not(@apistyle) or @apistyle='classic']";
                string newNodeFilter = "AssemblyInfo[@apistyle='unified']";
-               string thisNodeFilter = MDocUpdater.HasDroppedNamespace () ? newNodeFilter : oldNodeFilter;
-               string thatNodeFilter = MDocUpdater.HasDroppedNamespace () ? oldNodeFilter : newNodeFilter;
+               string thisNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? newNodeFilter : oldNodeFilter;
+               string thatNodeFilter = MDocUpdater.HasDroppedNamespace (assembly) ? oldNodeFilter : newNodeFilter;
 
                XmlElement e = (XmlElement) root.SelectSingleNode (thisNodeFilter);
                if (e == null) {
                        e = root.OwnerDocument.CreateElement("AssemblyInfo");
 
-                       if (MDocUpdater.HasDroppedNamespace ()) {
+                       if (MDocUpdater.HasDroppedNamespace (assembly)) {
                                e.SetAttribute ("apistyle", "unified");
                        }
 
@@ -2197,7 +2340,7 @@ class MDocUpdater : MDocCommand
                }
 
                var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
-               if (MDocUpdater.HasDroppedNamespace () && thatNode != null) {
+               if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
                        // there's a classic node, we should add apistyles
                        e.SetAttribute ("apistyle", "unified");
                        thatNode.SetAttribute ("apistyle", "classic");
@@ -2281,40 +2424,27 @@ class MDocUpdater : MDocCommand
 
        public static string MakeAttributesValueString (object v, TypeReference valueType)
        {
-               if (v == null)
-                       return "null";
-                       if (valueType.FullName == "System.Type") {
-                               var vTypeRef = v as TypeReference;
-                               if (vTypeRef != null)
-                                       return "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
-                               else
-                                       return "typeof(" + v.ToString () + ")";
+               var formatters = new [] { 
+                       new AttributeValueFormatter (), 
+                       new ApplePlatformEnumFormatter (), 
+                       new StandardFlagsEnumFormatter (), 
+                       new DefaultAttributeValueFormatter (),
+               };
+
+               ResolvedTypeInfo type = new ResolvedTypeInfo (valueType);
+               foreach (var formatter in formatters) {
+                       string formattedValue;
+                       if (formatter.TryFormatValue (v, type, out formattedValue)) {
+                               return formattedValue;
                        }
-               if (valueType.FullName == "System.String")
-                       return "\"" + v.ToString () + "\"";
-               if (valueType.FullName == "System.Char")
-                       return "'" + v.ToString () + "'";
-               if (v is Boolean)
-                       return (bool)v ? "true" : "false";
-               TypeDefinition valueDef = valueType.Resolve ();
-               if (valueDef == null || !valueDef.IsEnum)
-                       return v.ToString ();
-               string typename = GetDocTypeFullName (valueType);
-               var values = GetEnumerationValues (valueDef);
-               long c = ToInt64 (v);
-               if (values.ContainsKey (c))
-                       return typename + "." + values [c];
-               if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
-                       return string.Join (" | ",
-                                       (from i in values.Keys
-                                        where (c & i) != 0
-                                        select typename + "." + values [i])
-                                       .DefaultIfEmpty (v.ToString ()).ToArray ());
                }
-               return "(" + GetDocTypeFullName (valueType) + ") " + v.ToString ();
+
+               // this should never occur because the DefaultAttributeValueFormatter will always
+               // successfully format the value ... but this is needed to satisfy the compiler :)
+               throw new InvalidDataException (string.Format ("Unable to format attribute value ({0})", v.ToString ()));
        }
 
-       private static Dictionary<long, string> GetEnumerationValues (TypeDefinition type)
+       internal static IDictionary<long, string> GetEnumerationValues (TypeDefinition type)
        {
                var values = new Dictionary<long, string> ();
                foreach (var f in 
@@ -2326,14 +2456,14 @@ class MDocUpdater : MDocCommand
                return values;
        }
 
-       static long ToInt64 (object value)
+       internal static long ToInt64 (object value)
        {
                if (value is ulong)
                        return (long) (ulong) value;
                return Convert.ToInt64 (value);
        }
        
-       private void MakeParameters (XmlElement root, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
+       private void MakeParameters (XmlElement root, MemberReference member, IList<ParameterDefinition> parameters, bool shouldDuplicateWithNew=false)
        {
                XmlElement e = WriteElement(root, "Parameters");
 
@@ -2382,13 +2512,14 @@ class MDocUpdater : MDocCommand
 
                                        MakeAttributes (pe, GetCustomAttributes (p.CustomAttributes, ""));
                                        return pe;
-                               });
+                               },
+                               member);
 
                        i++;
                }
        }
        
-       private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, bool shouldDuplicateWithNew)
+       private void MakeTypeParameters (XmlElement root, IList<GenericParameter> typeParams, MemberReference member, bool shouldDuplicateWithNew)
        {
                if (typeParams == null || typeParams.Count == 0) {
                        XmlElement f = (XmlElement) root.SelectSingleNode ("TypeParameters");
@@ -2450,18 +2581,19 @@ class MDocUpdater : MDocCommand
                                                }
                                        
                                                return pe;
-                                       });
+                                       },
+                               member);
                }
        }
 
        private void MakeParameters (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew)
        {
                if (mi is MethodDefinition && ((MethodDefinition) mi).IsConstructor)
-                               MakeParameters (root, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
+                               MakeParameters (root, mi, ((MethodDefinition)mi).Parameters, shouldDuplicateWithNew);
                else if (mi is MethodDefinition) {
                        MethodDefinition mb = (MethodDefinition) mi;
                        IList<ParameterDefinition> parameters = mb.Parameters;
-                               MakeParameters(root, parameters, shouldDuplicateWithNew);
+                               MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
                        if (parameters.Count > 0 && DocUtils.IsExtensionMethod (mb)) {
                                XmlElement p = (XmlElement) root.SelectSingleNode ("Parameters/Parameter[position()=1]");
                                p.SetAttribute ("RefType", "this");
@@ -2470,7 +2602,7 @@ class MDocUpdater : MDocCommand
                else if (mi is PropertyDefinition) {
                        IList<ParameterDefinition> parameters = ((PropertyDefinition)mi).Parameters;
                        if (parameters.Count > 0)
-                                       MakeParameters(root, parameters, shouldDuplicateWithNew);
+                                       MakeParameters(root, mi, parameters, shouldDuplicateWithNew);
                        else
                                return;
                }
@@ -2498,7 +2630,8 @@ class MDocUpdater : MDocCommand
                                                MakeAttributes(e, GetCustomAttributes (attributes, ""), type);
 
                                        return newNode;
-                               });
+                               },
+                       type);
        }
        
        private void MakeReturnValue (XmlElement root, MemberReference mi, bool shouldDuplicateWithNew=false)
@@ -2826,21 +2959,41 @@ enum ApiStyle {
 static class DocUtils {
 
        public static bool DoesNotHaveApiStyle(this XmlElement element, ApiStyle style) {
-               string styleString = style.ToString ().ToLower ();
+               string styleString = style.ToString ().ToLowerInvariant ();
                        string apistylevalue = element.GetAttribute ("apistyle");
                        return apistylevalue != styleString || string.IsNullOrWhiteSpace(apistylevalue);
        }
        public static bool HasApiStyle(this XmlElement element, ApiStyle style) {
-               string styleString = style.ToString ().ToLower ();
+               string styleString = style.ToString ().ToLowerInvariant ();
                return element.GetAttribute ("apistyle") == styleString;
        }
+       public static bool HasApiStyle(this XmlNode node, ApiStyle style) 
+       {
+               var attribute = node.Attributes ["apistyle"];
+               return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant ();
+       }
        public static void AddApiStyle(this XmlElement element, ApiStyle style) {
-               string styleString = style.ToString ().ToLower ();
+               string styleString = style.ToString ().ToLowerInvariant ();
                var existingValue = element.GetAttribute ("apistyle");
                if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
                        element.SetAttribute ("apistyle", styleString);
                }
        }
+       public static void RemoveApiStyle (this XmlElement element, ApiStyle style) 
+       {
+               string styleString = style.ToString ().ToLowerInvariant ();
+               string existingValue = element.GetAttribute ("apistyle");
+               if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString) {
+                       element.RemoveAttribute ("apistyle");
+               }
+       }
+       public static void RemoveApiStyle (this XmlNode node, ApiStyle style) 
+       {
+               var styleAttribute = node.Attributes ["apistyle"];
+               if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ()) {
+                       node.Attributes.Remove (styleAttribute);
+               }
+       }
 
        public static bool IsExplicitlyImplemented (MethodDefinition method)
        {
@@ -2913,7 +3066,7 @@ static class DocUtils {
                        // first, make sure this isn't a type reference to another assembly/module
 
                        bool isInAssembly = MDocUpdater.IsInAssemblies(type.Module.Name);
-                       if (isInAssembly && MDocUpdater.HasDroppedNamespace () && !typeNS.StartsWith ("System")) {
+                       if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type)) {
                                typeNS = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, typeNS);
                        }
                        return typeNS;
@@ -2975,7 +3128,7 @@ static class DocUtils {
                        if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
                                userInterfaces.Add (iface);
                }
-               return userInterfaces;
+               return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
        }
 
        private static string GetQualifiedTypeName (TypeReference type)
@@ -3141,7 +3294,7 @@ class DocumentationEnumerator {
                
                string docName = member.MemberName;
 
-               string[] docTypeParams = GetTypeParameters (docName);
+               string[] docTypeParams = GetTypeParameters (docName, member.TypeParameters);
 
                // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching
                MemberReference likelyCandidate = null;
@@ -3161,16 +3314,21 @@ class DocumentationEnumerator {
                        if (mi is MethodDefinition) {
                                MethodDefinition mb = (MethodDefinition) mi;
                                pis = mb.Parameters;
-                               if (docTypeParams != null && mb.IsGenericMethod ()) {
+                               if (mb.IsGenericMethod ()) {
                                        IList<GenericParameter> args = mb.GenericParameters;
-                                       if (args.Count == docTypeParams.Length) {
-                                               typeParams = args.Select (p => p.Name).ToArray ();
-                                       }
+                                       typeParams = args.Select (p => p.Name).ToArray ();
                                }
                        }
                        else if (mi is PropertyDefinition)
                                pis = ((PropertyDefinition)mi).Parameters;
-                       
+                               
+                       // check type parameters
+                       int methodTcount = member.TypeParameters == null ? 0 : member.TypeParameters.Count;
+                       int reflectionTcount = typeParams == null ? 0 : typeParams.Length;
+                       if (methodTcount != reflectionTcount) 
+                               continue;
+
+                       // check member parameters
                        int mcount = member.Parameters == null ? 0 : member.Parameters.Count;
                        int pcount = pis == null ? 0 : pis.Count;
                        if (mcount != pcount)
@@ -3217,7 +3375,7 @@ class DocumentationEnumerator {
                                        // did not match ... if we're dropping the namespace, and the paramType has the dropped
                                        // namespace, we should see if it matches when added
                                        bool stillDoesntMatch = true;
-                                       if (MDocUpdater.HasDroppedNamespace() && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
+                                       if (MDocUpdater.HasDroppedNamespace(type) && paramType.StartsWith (MDocUpdater.droppedNamespace)) {
                                                string withDroppedNs = string.Format ("{0}.{1}", MDocUpdater.droppedNamespace, xmlMemberType);
 
                                                stillDoesntMatch = withDroppedNs != paramType;
@@ -3246,7 +3404,7 @@ class DocumentationEnumerator {
                return likelyCandidate;
        }
 
-       static string[] GetTypeParameters (string docName)
+       static string[] GetTypeParameters (string docName, IEnumerable<string> knownParameters)
        {
                if (docName [docName.Length-1] != '>')
                        return null;
@@ -3263,14 +3421,19 @@ class DocumentationEnumerator {
                } while (--i >= 0);
 
                types.Reverse ();
-               return types.ToArray ();
+               var arrayTypes = types.ToArray ();
+
+               if (knownParameters != null && knownParameters.Any () && arrayTypes.Length != knownParameters.Count ())
+                       return knownParameters.ToArray ();
+               else
+                       return arrayTypes;
        }
 
        protected static IEnumerable<MemberReference> GetReflectionMembers (TypeDefinition type, string docName)
        {
                // In case of dropping the namespace, we have to remove the dropped NS
                // so that docName will match what's in the assembly/type
-               if (MDocUpdater.HasDroppedNamespace () && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
+               if (MDocUpdater.HasDroppedNamespace (type) && docName.StartsWith(MDocUpdater.droppedNamespace + ".")) {
                        int droppedNsLength = MDocUpdater.droppedNamespace.Length;
                        docName = docName.Substring (droppedNsLength + 1, docName.Length - droppedNsLength - 1);
                }
@@ -3724,6 +3887,7 @@ class DocumentationMember {
        public StringToStringMap MemberSignatures = new StringToStringMap ();
        public string ReturnType;
        public StringList Parameters;
+       public StringList TypeParameters;
        public string MemberName;
        public string MemberType;
 
@@ -3733,6 +3897,7 @@ class DocumentationMember {
                int depth = reader.Depth;
                bool go = true;
                StringList p = new StringList ();
+               StringList tp = new StringList ();
                do {
                        if (reader.NodeType != XmlNodeType.Element)
                                continue;
@@ -3760,6 +3925,10 @@ class DocumentationMember {
                                        if (reader.Depth == depth + 2 && shouldUse)
                                                p.Add (reader.GetAttribute ("Type"));
                                        break;
+                               case "TypeParameter":
+                                       if (reader.Depth == depth + 2 && shouldUse)
+                                               tp.Add (reader.GetAttribute ("Name"));
+                                       break;
                                case "Docs":
                                        if (reader.Depth == depth + 1)
                                                go = false;
@@ -3769,6 +3938,11 @@ class DocumentationMember {
                if (p.Count > 0) {
                        Parameters = p;
                }
+               if (tp.Count > 0) {
+                       TypeParameters = tp;
+               } else {
+                       DiscernTypeParameters ();
+               }
        }
 
        public DocumentationMember (XmlNode node)
@@ -3792,6 +3966,27 @@ class DocumentationMember {
                        for (int i = 0; i < p.Count; ++i)
                                Parameters.Add (p [i].Attributes ["Type"].Value);
                }
+               XmlNodeList tp = node.SelectNodes ("TypeParameters/TypeParameter[not(@apistyle) or @apistyle='classic']");
+               if (tp.Count > 0) {
+                       TypeParameters = new StringList (tp.Count);
+                       for (int i = 0; i < tp.Count; ++i)
+                               TypeParameters.Add (tp [i].Attributes ["Name"].Value);
+               }
+               else {
+                       DiscernTypeParameters ();
+               }
+       }
+
+       void DiscernTypeParameters ()
+       {
+               // see if we can discern the param list from the name
+               if (MemberName.Contains ("<") && MemberName.EndsWith (">")) {
+                       var starti = MemberName.IndexOf ("<") + 1;
+                       var endi = MemberName.LastIndexOf (">");
+                       var paramlist = MemberName.Substring (starti, endi - starti);
+                       var tparams = paramlist.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
+                       TypeParameters = new StringList (tparams);
+               }
        }
 }
 
@@ -4303,7 +4498,7 @@ class ILFullMemberFormatter : MemberFormatter {
                                buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
                }
                bool first = true;
-               foreach (var name in type.Interfaces
+               foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()))
                                .Select (i => full.GetName (i))
                                .OrderBy (n => n)) {
                        if (first) {
@@ -5556,4 +5751,190 @@ class FileNameMemberFormatter : SlashDocMemberFormatter {
        }
 }
 
+class ResolvedTypeInfo {
+       TypeDefinition typeDef;
+
+       public ResolvedTypeInfo (TypeReference value) {
+               Reference = value;
+       }
+
+       public TypeReference Reference { get; private set; }
+
+       public TypeDefinition Definition {
+               get {
+                       if (typeDef == null) {
+                               typeDef = Reference.Resolve ();
+                       }
+                       return typeDef;
+               }
+       }
+}
+
+/// <summary>Formats attribute values. Should return true if it is able to format the value.</summary>
+class AttributeValueFormatter {
+       public virtual bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
+       {
+               TypeReference valueType = type.Reference;
+               if (v == null) {
+                       returnvalue = "null";
+                       return true;
+               }
+               if (valueType.FullName == "System.Type") {
+                       var vTypeRef = v as TypeReference;
+                       if (vTypeRef != null) 
+                               returnvalue = "typeof(" + NativeTypeManager.GetTranslatedName (vTypeRef) + ")"; // TODO: drop NS handling
+                       else
+                               returnvalue = "typeof(" + v.ToString () + ")";
+                       
+                       return true;
+               }
+               if (valueType.FullName == "System.String") {
+                       returnvalue = "\"" + v.ToString () + "\"";
+                       return true;
+               }
+               if (valueType.FullName == "System.Char") {
+                       returnvalue = "'" + v.ToString () + "'";
+                       return true;
+               }
+               if (v is Boolean) {
+                       returnvalue = (bool)v ? "true" : "false";
+                       return true;
+               }
+
+               TypeDefinition valueDef = type.Definition;
+               if (valueDef == null || !valueDef.IsEnum) {
+                       returnvalue = v.ToString ();
+                       return true;
+               }
+
+               string typename = MDocUpdater.GetDocTypeFullName (valueType);
+               var values = MDocUpdater.GetEnumerationValues (valueDef);
+               long c = MDocUpdater.ToInt64 (v);
+               if (values.ContainsKey (c)) {
+                       returnvalue = typename + "." + values [c];
+                       return true;
+               }
+
+               returnvalue = null;
+               return false;
+       }
+}
+
+/// <summary>The final value formatter in the pipeline ... if no other formatter formats the value,
+/// then this one will serve as the default implementation.</summary>
+class DefaultAttributeValueFormatter : AttributeValueFormatter {
+       public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
+       {
+               returnvalue = "(" + MDocUpdater.GetDocTypeFullName (type.Reference) + ") " + v.ToString ();
+               return true;
+       }
+}
+
+/// <summary>Flags enum formatter that assumes powers of two values.</summary>
+/// <remarks>As described here: https://msdn.microsoft.com/en-us/library/vstudio/ms229062(v=vs.100).aspx</remarks>
+class StandardFlagsEnumFormatter : AttributeValueFormatter {
+       public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
+       {
+               TypeReference valueType = type.Reference;
+               TypeDefinition valueDef = type.Definition;
+               if (valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
+
+                       string typename = MDocUpdater.GetDocTypeFullName (valueType);
+                       var values = MDocUpdater.GetEnumerationValues (valueDef);
+                       long c = MDocUpdater.ToInt64 (v);
+                       returnvalue = string.Join (" | ",
+                               (from i in values.Keys
+                                where (c & i) == i && i != 0
+                                select typename + "." + values [i])
+                               .DefaultIfEmpty (c.ToString ()).ToArray ());
+                       
+                       return true;
+               }
+
+               returnvalue = null;
+               return false;
+       }
+}
+
+/// <summary>A custom formatter for the ObjCRuntime.Platform enumeration.</summary>
+class ApplePlatformEnumFormatter : AttributeValueFormatter {
+       public override bool TryFormatValue (object v, ResolvedTypeInfo type, out string returnvalue)
+       {
+               TypeReference valueType = type.Reference;
+               string typename = MDocUpdater.GetDocTypeFullName (valueType);
+               TypeDefinition valueDef = type.Definition;
+               if (typename.Contains ("ObjCRuntime.Platform") && valueDef.CustomAttributes.Any (ca => ca.AttributeType.FullName == "System.FlagsAttribute")) {
+
+                       var values = MDocUpdater.GetEnumerationValues (valueDef);
+                       long c = MDocUpdater.ToInt64 (v);
+
+                       returnvalue = Format (c, values, typename);
+                       return true;
+               }
+
+               returnvalue = null;
+               return false;
+       }
+
+       string Format (long c, IDictionary<long, string> values, string typename)
+       {
+               int iosarch, iosmajor, iosminor, iossubminor;
+               int macarch, macmajor, macminor, macsubminor;
+               GetEncodingiOS (c, out iosarch, out iosmajor, out iosminor, out iossubminor);
+               GetEncodingMac ((ulong)c, out macarch, out macmajor, out macminor, out macsubminor);
+
+               if (iosmajor == 0 & iosminor == 0 && iossubminor == 0) {
+                       return FormatValues ("Mac", macarch, macmajor, macminor, macsubminor);
+               }
+
+               if (macmajor == 0 & macminor == 0 && macsubminor == 0) {
+                       return FormatValues ("iOS", iosarch, iosmajor, iosminor, iossubminor);
+               }
+
+               return string.Format ("(Platform){0}", c);
+       }
+
+       string FormatValues (string plat, int arch, int major, int minor, int subminor) 
+       {
+               string archstring = "";
+               switch (arch) {
+               case 1:
+                       archstring = "32";
+                       break;
+               case 2:
+                       archstring = "64";
+                       break;
+               }
+               return string.Format ("Platform.{4}_{0}_{1}{2} | Platform.{4}_Arch{3}",
+                       major,
+                       minor,
+                       subminor == 0 ? "" : "_" + subminor.ToString (),
+                       archstring,
+                       plat
+               );
+       }
+
+       void GetEncodingiOS (long entireLong, out int archindex, out int major, out int minor, out int subminor)
+       {
+               long lowerBits = entireLong & 0xffffffff; 
+               int lowerBitsAsInt = (int) lowerBits;
+               GetEncoding (lowerBitsAsInt, out archindex, out major, out minor, out subminor);
+       }
+
+       void GetEncodingMac (ulong entireLong, out int archindex, out int major, out int minor, out int subminor)
+       {
+               ulong higherBits = entireLong & 0xffffffff00000000; 
+               int higherBitsAsInt = (int) ((higherBits) >> 32);
+               GetEncoding (higherBitsAsInt, out archindex, out major, out minor, out subminor);
+       }
+
+       void GetEncoding (Int32 encodedBits, out int archindex, out int major, out int minor, out int subminor)
+       {
+               // format is AAJJNNSS
+               archindex = (int)((encodedBits & 0xFF000000) >> 24);
+               major = (int)((encodedBits & 0x00FF0000) >> 16);
+               minor = (int)((encodedBits & 0x0000FF00) >> 8);
+               subminor = (int)((encodedBits & 0x000000FF) >> 0);
+       }
+}
 }