diff --git a/src/Xamarin.Android.Tools.Bytecode/ClassPath.cs b/src/Xamarin.Android.Tools.Bytecode/ClassPath.cs index 8b7303cd6..79a4f042e 100644 --- a/src/Xamarin.Android.Tools.Bytecode/ClassPath.cs +++ b/src/Xamarin.Android.Tools.Bytecode/ClassPath.cs @@ -17,7 +17,8 @@ public enum JavaDocletType { Java6, Java7, Java8, - _ApiXml + _ApiXml, + JavaApiParameterNamesXml, } public class ClassPath { @@ -231,15 +232,16 @@ void FixupParametersFromDocs (XElement api) } } - IAndroidDocScraper CreateDocScraper (string src) + IJavaMethodParameterNameProvider CreateDocScraper (string src) { - switch (AndroidDocScraper.GetDocletType (src)) { + switch (JavaMethodParameterNameProvider.GetDocletType (src)) { default: return new DroidDoc2Scraper (src); case JavaDocletType.DroidDoc: return new DroidDocScraper (src); case JavaDocletType.Java6: return new JavaDocScraper (src); case JavaDocletType.Java7: return new Java7DocScraper (src); case JavaDocletType.Java8: return new Java8DocScraper (src); case JavaDocletType._ApiXml: return new ApiXmlDocScraper (src); + case JavaDocletType.JavaApiParameterNamesXml: return new JavaParameterNamesLoader (src); } } diff --git a/src/Xamarin.Android.Tools.Bytecode/JavaDocumentScraper.cs b/src/Xamarin.Android.Tools.Bytecode/JavaDocumentScraper.cs index b210fe14a..48369081b 100644 --- a/src/Xamarin.Android.Tools.Bytecode/JavaDocumentScraper.cs +++ b/src/Xamarin.Android.Tools.Bytecode/JavaDocumentScraper.cs @@ -129,7 +129,7 @@ protected override string StripTagsFromParameters (string value) } } - public abstract class AndroidDocScraper : IAndroidDocScraper + public abstract class AndroidDocScraper : IJavaMethodParameterNameProvider { readonly String pattern_head; readonly String reset_pattern_head; @@ -290,6 +290,14 @@ public static void LoadXml (String filename) Log.Error ("Annotations parser error: " + ex); } } + } + + public interface IJavaMethodParameterNameProvider + { + String[] GetParameterNames (string package, string type, string method, string[] ptypes, bool isVarArgs); + } + + public static class JavaMethodParameterNameProvider { public static JavaDocletType GetDocletType (string path) { @@ -322,18 +330,17 @@ public static JavaDocletType GetDocletType (string path) } if (rawXML.Contains ("") && rawXML.Contains (" packages; + + public JavaParameterNamesLoader(string path) + { + packages = this.LoadParameterFixupDescription(path); + } + + class Parameter + { + public string Type { get; set; } + public string Name { get; set; } + } + + class Method + { + public string Name { get; set; } + public List Parameters { get; set; } + } + + class Type + { + public string Name { get; set; } + public List Methods { get; set; } + } + + class Package + { + public string Name { get; set; } + public List Types { get; set; } + } + + // from https://github.com/atsushieno/xamarin-android-docimporter-ng/blob/master/Xamarin.Android.Tools.JavaStubImporter/JavaApiParameterNamesXmlExporter.cs#L78 + /* + * The Text Format is: + * + * package {packagename} + * #--------------------------------------- + * interface {interfacename}{optional_type_parameters} -or- + * class {classname}{optional_type_parameters} + * {optional_type_parameters}{methodname}({parameters}) + * + * Anything after # is treated as comment. + * + * optional_type_parameters: "" -or- "" (no constraints allowed) + * parameters: type1 p0, type2 p1 (pairs of {type} {name}, joined by ", ") + * + * It is with strict indentations. two spaces for types, four spaces for methods. + * + * Constructors are named as "#ctor". + * + * Commas are used by both parameter types and parameter separators, + * but only parameter separators can be followed by a whitespace. + * It is useful when writing text parsers for this format. + * + * Type names may contain whitespaces in case it is with generic constraints (e.g. "? extends FooBar"), + * so when parsing a parameter type-name pair, the only trustworthy whitespace for tokenizing name is the *last* one. + * + */ + List LoadParameterFixupDescription (string path) + { + var fixup = new List (); + string package = null; + var types = new List (); + string type = null; + var methods = new List (); + foreach (var l in File.ReadAllLines (path)) { + var line = l.IndexOf ('#') >= 0 ? l.Substring (0, l.IndexOf ('#')) : l; + if (line.Trim ().Length == 0) + continue; + if (line.StartsWith ("package ", StringComparison.Ordinal)) { + package = line.Substring ("package ".Length); + types = new List (); + fixup.Add (new Package { Name = package, Types = types }); + continue; + } else if (line.StartsWith (" ", StringComparison.Ordinal)) { + int open = line.IndexOf ('('); + string parameters = line.Substring (open + 1).TrimEnd (')'); + string name = line.Substring (4, open - 4); + if (name.FirstOrDefault () == '<') // generic method can begin with type parameters. + name = name.Substring (name.IndexOf (' ') + 1); + methods.Add (new Method { + Name = name, + Parameters = parameters.Replace (", ", "\0").Split ('\0') + .Select (s => s.Split (' ')) + .Select (a => new Parameter { Type = string.Join (" ", a.Take (a.Length - 1)), Name = a.Last () }).ToList () + }); + } else { + type = line.Substring (line.IndexOf (' ', 2) + 1); + // To match type name from class-parse, we need to strip off generic arguments here (generics are erased). + if (type.IndexOf ('<') > 0) + type = type.Substring (0, type.IndexOf ('<')); + methods = new List (); + types.Add (new Type { Name = type, Methods = methods }); + } + } + return fixup; + } + + public string[] GetParameterNames (string package, string type, string method, string[] ptypes, bool isVarArgs) + { + var methods = this.packages + .Where(p => p.Name == package) + .SelectMany(p => p.Types) + .Where(t => t.Name == type) + .SelectMany(t => t.Methods) + .Where(m => m.Name == method); + var namedMethod = methods.FirstOrDefault (m => ParametersEqual (m.Parameters, ptypes)); + if (namedMethod == null) + return null; + return namedMethod.Parameters.Select (p => p.Name).ToArray (); + } + + static bool ParametersEqual (List methodParameters, string[] ptypes) + { + if (methodParameters.Count != ptypes.Length) + return false; + for (int i = 0; i < ptypes.Length; ++i) { + if (methodParameters[i].Type != ptypes [i]) + return false; + } + return true; + } + } +} diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/ParameterDescription.txt b/src/Xamarin.Android.Tools.Bytecode/Tests/ParameterDescription.txt new file mode 100644 index 000000000..6737ff3f7 --- /dev/null +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/ParameterDescription.txt @@ -0,0 +1,3 @@ +package java.util + class Collection + add(E e) diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/ParameterFixupTests.cs b/src/Xamarin.Android.Tools.Bytecode/Tests/ParameterFixupTests.cs index d8ff131ad..4faac0120 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/ParameterFixupTests.cs +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/ParameterFixupTests.cs @@ -63,7 +63,7 @@ public void DocletType_ShouldDetectApiXml () try { tempFile = LoadToTempFile ("ParameterFixupApiXmlDocs.xml"); - Assert.AreEqual (JavaDocletType._ApiXml, AndroidDocScraper.GetDocletType (tempFile)); + Assert.AreEqual (JavaDocletType._ApiXml, JavaMethodParameterNameProvider.GetDocletType (tempFile)); } finally { if (File.Exists (tempFile)) File.Delete (tempFile); @@ -85,7 +85,23 @@ public void DocletType_ShouldDetectDroidDocs () if (!Directory.Exists (droidDocsPath)) Assert.Fail("The Android SDK Documentation path `{0}` was not found.", droidDocsPath); - Assert.AreEqual(JavaDocletType.DroidDoc2, AndroidDocScraper.GetDocletType(droidDocsPath)); + Assert.AreEqual(JavaDocletType.DroidDoc2, JavaMethodParameterNameProvider.GetDocletType(droidDocsPath)); + } + + [Test] + public void XmlDeclaration_FixedUpFromParameterDescription () + { + string tempFile = null; + + try { + tempFile = LoadToTempFile ("ParameterDescription.txt"); + + AssertXmlDeclaration (new string [] { "Collection.class" }, "ParameterFixupFromDocs.xml", tempFile); + } + finally { + if (File.Exists (tempFile)) + File.Delete (tempFile); + } } } } diff --git a/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj b/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj index ffbbc7b0d..0cd24b389 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj +++ b/src/Xamarin.Android.Tools.Bytecode/Tests/Xamarin.Android.Tools.Bytecode-Tests.csproj @@ -162,7 +162,9 @@ ParameterFixupApiXmlDocs.xml - + + ParameterDescription.txt + diff --git a/src/Xamarin.Android.Tools.Bytecode/Xamarin.Android.Tools.Bytecode.csproj b/src/Xamarin.Android.Tools.Bytecode/Xamarin.Android.Tools.Bytecode.csproj index 9a0d7e3c5..c2379706f 100644 --- a/src/Xamarin.Android.Tools.Bytecode/Xamarin.Android.Tools.Bytecode.csproj +++ b/src/Xamarin.Android.Tools.Bytecode/Xamarin.Android.Tools.Bytecode.csproj @@ -49,6 +49,7 @@ + diff --git a/tools/class-parse/Program.cs b/tools/class-parse/Program.cs index d3123f77e..472b75a5b 100644 --- a/tools/class-parse/Program.cs +++ b/tools/class-parse/Program.cs @@ -38,6 +38,9 @@ public static void Main (string[] args) { "docspath=", "Documentation {PATH} for parameter fixup", doc => docsPaths.Add (doc) }, + { "parameter-names=", + "{PATH} for Java method parameter name information", + doc => docsPaths.Add (doc) }, { "docstype=", "OBSOLETE: Previously used to specify a doc type (now auto detected).", t => docsType = t != null },