List<AssemblyDefinition> assemblies;
readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
+ bool multiassembly;
bool delete;
bool show_exceptions;
bool no_assembly_versions, ignore_missing_types;
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>();
{ "preserve",
"Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
v => PreserveTag = "true" },
+ { "multiassembly",
+ "Allow types to be in multiple assemblies.",
+ v => multiassembly = true },
};
var assemblies = Parse (p, args, "update",
"[OPTIONS]+ ASSEMBLIES",
}
if (r == null)
continue;
- c.Remove (n);
- c.InsertBefore (n, r);
+ if (c [n.Name] != null) {
+ c.RemoveNamedItem (n.Name);
+ c.InsertBefore (n, r);
+ }
}
}
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;
}
private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
{
- XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly");
+ XmlElement index_assembly = null;
+ if (multiassembly)
+ index_assembly = (XmlElement)parent.SelectSingleNode ("Assembly[@Name='"+ assembly.Name.Name +"']");
+
+ if (index_assembly == null)
+ index_assembly = parent.OwnerDocument.CreateElement ("Assembly");
+
index_assembly.SetAttribute ("Name", assembly.Name.Name);
index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString());
XmlElement index_types = WriteElement(index.DocumentElement, "Types");
XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
- index_assemblies.RemoveAll ();
+ if (!multiassembly)
+ index_assemblies.RemoveAll ();
HashSet<string> goodfiles = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
XmlElement e = doc.SelectSingleNode("/Type") as XmlElement;
string assemblyName = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName").InnerText;
AssemblyDefinition assembly = assemblies.FirstOrDefault (a => a.Name.Name == assemblyName);
- if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions(e, assembly, GetAssemblyVersions(assemblyName), false)) {
+
+ 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 ();
+ }
+ }
}
}
}
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
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 (type)) {
- // 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 (type) && 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);
+ }
+ }
}
}
type);
}
- string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
-
- AddXmlNode(
- root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().ToArray (),
- x => x.SelectSingleNode("AssemblyName").InnerText == type.Module.Assembly.Name.Name,
- x => WriteElementText(x, "AssemblyName", type.Module.Assembly.Name.Name),
- () => {
- XmlElement ass = WriteElement(root, "AssemblyInfo", forceNewElement:true);
-
- if (MDocUpdater.HasDroppedNamespace (type)) ass.SetAttribute ("apistyle", "unified");
+ AddAssemblyNameToNode (root, type);
-
-
- return ass;
- },
- type);
-
- foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ())
+ string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']";
+ Func<XmlElement, bool> assemblyFilter = x => x.SelectSingleNode ("AssemblyName").InnerText == type.Module.Assembly.Name.Name;
+ foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast<XmlElement> ().Where (assemblyFilter))
{
WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name);
if (!no_assembly_versions) {
- UpdateAssemblyVersions (root, type, true);
+ UpdateAssemblyVersions (ass, type, true);
}
else {
var versions = ass.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().ToList ();
NormalizeWhitespace(root);
}
+ /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
+ /// <returns>The assembly that was either added, or was already present</returns>
+ static XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type)
+ {
+ return AddAssemblyNameToNode (root, type.Module);
+ }
+
+ /// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
+ /// <returns>The assembly that was either added, or was already present</returns>
+ static XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module)
+ {
+ Func<XmlElement, bool> assemblyFilter = x => {
+ var existingName = x.SelectSingleNode ("AssemblyName");
+ return existingName != null && existingName.InnerText == module.Assembly.Name.Name;
+ };
+
+ return AddAssemblyXmlNode (
+ root.SelectNodes ("AssemblyInfo").Cast<XmlElement> ().ToArray (),
+ assemblyFilter, x => WriteElementText (x, "AssemblyName", module.Assembly.Name.Name),
+ () => {
+ XmlElement ass = WriteElement (root, "AssemblyInfo", forceNewElement: true);
+ if (MDocUpdater.HasDroppedNamespace (module))
+ ass.SetAttribute ("apistyle", "unified");
+ return ass;
+ }, module);
+ }
+
static readonly string[] TypeNodeOrder = {
"TypeSignature",
"MemberOfLibrary",
WriteElementText(me, "MemberType", GetMemberType(mi));
if (!no_assembly_versions) {
- UpdateAssemblyVersions (me, mi, true);
+ if (!multiassembly)
+ UpdateAssemblyVersions (me, mi, true);
+ else {
+ var node = AddAssemblyNameToNode (me, mi.Module);
+
+ UpdateAssemblyVersionForAssemblyInfo (node, me, new[] { GetAssemblyVersion (mi.Module.Assembly) }, add: true);
+ }
}
else {
ClearElement (me, "AssemblyInfo");
AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module);
}
+ static XmlElement AddAssemblyXmlNode (XmlElement[] relevant, Func<XmlElement, bool> valueMatches, Action<XmlElement> setValue, Func<XmlElement> makeNewNode, ModuleDefinition module)
+ {
+ bool isUnified = MDocUpdater.HasDroppedNamespace (module);
+ XmlElement thisAssemblyNode = relevant.FirstOrDefault (valueMatches);
+ if (thisAssemblyNode == null) {
+ thisAssemblyNode = makeNewNode ();
+ setValue (thisAssemblyNode);
+ }
+
+ if (isUnified) {
+ thisAssemblyNode.AddApiStyle (ApiStyle.Unified);
+
+ foreach (var otherNodes in relevant.Where (n => n != thisAssemblyNode && n.DoesNotHaveApiStyle (ApiStyle.Unified))) {
+ otherNodes.AddApiStyle (ApiStyle.Classic);
+ }
+ }
+ return thisAssemblyNode;
+ }
+
/// <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>
TypeDefinition type = member as TypeDefinition;
if (type == null)
type = member.DeclaringType as TypeDefinition;
- return UpdateAssemblyVersions(root, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add);
+
+ var versions = new string[] { GetAssemblyVersion (type.Module.Assembly) };
+
+ if (root.LocalName == "AssemblyInfo")
+ return UpdateAssemblyVersionForAssemblyInfo (root, root.ParentNode as XmlElement, versions, add: true);
+ else
+ return UpdateAssemblyVersions (root, type.Module.Assembly, versions, add);
}
private static string GetAssemblyVersion (AssemblyDefinition assembly)
thatNode.SetAttribute ("apistyle", "classic");
}
- List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode>()
- .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0)
- .ToList ();
+ return UpdateAssemblyVersionForAssemblyInfo (e, root, assemblyVersions, add);
+ }
+
+ static bool UpdateAssemblyVersionForAssemblyInfo (XmlElement e, XmlElement root, string[] assemblyVersions, bool add)
+ {
+ List<XmlNode> matches = e.SelectNodes ("AssemblyVersion").Cast<XmlNode> ().Where (v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0).ToList ();
// matches.Count > 0 && add: ignore -- already present
if (matches.Count > 0 && !add) {
foreach (XmlNode c in matches)
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
return values;
}
- static long ToInt64 (object value)
+ internal static long ToInt64 (object value)
{
if (value is ulong)
return (long) (ulong) value;
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)
{
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;
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)
return likelyCandidate;
}
- static string[] GetTypeParameters (string docName)
+ static string[] GetTypeParameters (string docName, IEnumerable<string> knownParameters)
{
if (docName [docName.Length-1] != '>')
return null;
} 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)
public StringToStringMap MemberSignatures = new StringToStringMap ();
public string ReturnType;
public StringList Parameters;
+ public StringList TypeParameters;
public string MemberName;
public string MemberType;
int depth = reader.Depth;
bool go = true;
StringList p = new StringList ();
+ StringList tp = new StringList ();
do {
if (reader.NodeType != XmlNodeType.Element)
continue;
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;
if (p.Count > 0) {
Parameters = p;
}
+ if (tp.Count > 0) {
+ TypeParameters = tp;
+ } else {
+ DiscernTypeParameters ();
+ }
}
public DocumentationMember (XmlNode node)
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);
+ }
}
}
}
}
+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);
+ }
+}
}