From d3f0c5c6f634380b6156c98f57e98463adf0de52 Mon Sep 17 00:00:00 2001 From: Peter Collins Date: Wed, 15 Dec 2021 08:09:06 -0500 Subject: [PATCH] [generator] Fix parameter string in ref doc links (#935) Fixes: https://github.com/xamarin/java.interop/issues/931 Fixes: https://github.com/xamarin/java.interop/issues/932 We have a handful of issues in the https://developer.android.com/ links that are generated for various type members with complex parameters. Collections, varargs, and generics are some examples of items that are not translated correctly when generating the parameter portion of the reference documentation URL. The XML generated by `java-source-utils` when processing `android-stubs-src.jar` contains `` elements under all methods. The `` elements contain attributes with additional type data; for instance: Path using two properties. A Path animation moves in two dimensions, animating coordinates (x, y) together to follow the line. In this variation, the coordinates are floats that are set to separate properties, xProperty and yProperty. @param target The object whose properties are to be animated. @param xProperty The property for the x coordinate being animated. @param yProperty The property for the y coordinate being animated. @param path The Path to animate values along. @return An ObjectAnimator object that is set up to animate along path.]]> Rather than processing the `//method/@jni-signature` attribute of the method, use the `//method/parameter/@type` attribute values to create more reliable type information for our reference documentation links. With these changes in place, instead of e.g. https://developer.android.com/reference/android/accounts/AccountManager#addAccount(java.lang.String,%20java.lang.String,%20java.lang.String[],%20android.os.Bundle,%20android.app.Activity,%20android.accounts.AccountManagerCallback,%20android.os.Handler) which doesn't anchor to the intended method documentation, we now emit https://developer.android.com/reference/android/accounts/AccountManager#addAccount(java.lang.String,%20java.lang.String,%20java.lang.String[],%20android.os.Bundle,%20android.app.Activity,%20android.accounts.AccountManagerCallback%3Candroid.os.Bundle%3E,%20android.os.Handler) which *does* anchor to the intended method documentation. --- .../JavadocInfo.cs | 126 ++++-------------- 1 file changed, 27 insertions(+), 99 deletions(-) diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/JavadocInfo.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/JavadocInfo.cs index cc105825f..8ee02b159 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/JavadocInfo.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/JavadocInfo.cs @@ -39,9 +39,9 @@ public static JavadocInfo CreateInfo (XElement element, XmldocStyle style, bool var desc = GetMemberDescription (element); string declaringJniType = desc.DeclaringJniType; string declaringMemberName = desc.DeclaringMemberName; - var declaringMemberJniSignature = desc.DeclaringMemberJniSignature; + var declaringMemberParamString = desc.DeclaringMemberParameterString; - var extras = GetExtra (element, style, declaringJniType, declaringMemberName, declaringMemberJniSignature, appendCopyrightExtra); + var extras = GetExtra (element, style, declaringJniType, declaringMemberName, declaringMemberParamString, appendCopyrightExtra); XElement[] extra = extras.Extras; XElement[] copyright = extras.Copyright; @@ -54,13 +54,13 @@ public static JavadocInfo CreateInfo (XElement element, XmldocStyle style, bool Javadoc = javadoc, MemberDescription = declaringMemberName == null ? declaringJniType - : $"{declaringJniType}.{declaringMemberName}.{declaringMemberJniSignature}", + : $"{declaringJniType}.{declaringMemberName}{declaringMemberParamString}", XmldocStyle = style, }; return info; } - static (string DeclaringJniType, string DeclaringMemberName, string DeclaringMemberJniSignature) GetMemberDescription (XElement element) + static (string DeclaringJniType, string DeclaringMemberName, string DeclaringMemberParameterString) GetMemberDescription (XElement element) { bool isType = element.Name.LocalName == "class" || element.Name.LocalName == "interface"; @@ -76,14 +76,26 @@ public static JavadocInfo CreateInfo (XElement element, XmldocStyle style, bool string declaringMemberName = isType ? null : (string) element.Attribute ("name") ?? declaringJniType.Substring (declaringJniType.LastIndexOf ('/')+1); + string declaringMemberJniSignature = isType ? null : (string) element.Attribute ("jni-signature"); - return (declaringJniType, declaringMemberName, declaringMemberJniSignature); + + string declaringMemberParameterString = null; + if (!isType && (declaringMemberJniSignature?.StartsWith ("(", StringComparison.Ordinal) ?? false)) { + var parameterTypes = element.Elements ("parameter")?.Select (e => e.Attribute ("type")?.Value)?.ToList (); + if (parameterTypes?.Any () ?? false) { + declaringMemberParameterString = $"({string.Join (", ", parameterTypes)})"; + } else { + declaringMemberParameterString = "()"; + } + } + + return (declaringJniType, declaringMemberName, declaringMemberParameterString); } - static (XElement[] Extras, XElement[] Copyright) GetExtra (XElement element, XmldocStyle style, string declaringJniType, string declaringMemberName, string declaringMemberJniSignature, bool appendCopyrightExtra) + static (XElement[] Extras, XElement[] Copyright) GetExtra (XElement element, XmldocStyle style, string declaringJniType, string declaringMemberName, string declaringMemberParameterString, bool appendCopyrightExtra) { if (!style.HasFlag (XmldocStyle.IntelliSenseAndExtraRemarks)) return (null, null); @@ -107,7 +119,7 @@ public static JavadocInfo CreateInfo (XElement element, XmldocStyle style, bool XElement docLink = null; if (!string.IsNullOrEmpty (urlPrefix)) { - docLink = CreateDocLinkUrl (kind, urlPrefix, declaringJniType, declaringMemberName, declaringMemberJniSignature); + docLink = CreateDocLinkUrl (kind, urlPrefix, declaringJniType, declaringMemberName, declaringMemberParameterString); } extra = new List (); extra.Add (docLink); @@ -217,18 +229,17 @@ static List GetLines (string text) [ApiLinkStyle.DeveloperAndroidComReference_2020Nov] = CreateAndroidDocLinkUri, }; - static XElement CreateDocLinkUrl (ApiLinkStyle style, string prefix, string declaringJniType, string declaringMemberName, string declaringMemberJniSignature) + static XElement CreateDocLinkUrl (ApiLinkStyle style, string prefix, string declaringJniType, string declaringMemberName, string declaringMemberParameterString) { - ; if (style == ApiLinkStyle.None || prefix == null || declaringJniType == null) return null; if (UrlCreators.TryGetValue (style, out var creator)) { - return creator (prefix, declaringJniType, declaringMemberName, declaringMemberJniSignature); + return creator (prefix, declaringJniType, declaringMemberName, declaringMemberParameterString); } return null; } - static XElement CreateAndroidDocLinkUri (string prefix, string declaringJniType, string declaringMemberName, string declaringMemberJniSignature) + static XElement CreateAndroidDocLinkUri (string prefix, string declaringJniType, string declaringMemberName, string declaringMemberParameterString) { // URL is: // * {prefix} @@ -236,7 +247,8 @@ static XElement CreateAndroidDocLinkUri (string prefix, string declaringJniType, // * when `declaringJniMemberName` != null, `#{declaringJniMemberName}` // * for methods & constructors, a `(`, the arguments in *Java* syntax -- separated by `, ` -- and `)` // - // Example: https://developer.android.com/reference/android/app/Application#registerOnProvideAssistDataListener(android.app.Application.OnProvideAssistDataListener) + // Example: "https://developer.android.com/reference/android/app/Application#registerOnProvideAssistDataListener(android.app.Application.OnProvideAssistDataListener)" + // Example: "https://developer.android.com/reference/android/animation/ObjectAnimator#ofFloat(T,%20android.util.Property%3CT,%20java.lang.Float%3E,%20float...)" var java = new StringBuilder (declaringJniType) .Replace ("/", ".") @@ -250,13 +262,9 @@ static XElement CreateAndroidDocLinkUri (string prefix, string declaringJniType, if (declaringMemberName != null) { java.Append (".").Append (declaringMemberName); url.Append ("#").Append (declaringMemberName); - if (declaringMemberJniSignature?.StartsWith ("(", StringComparison.Ordinal) ?? false) { - java.Append ("("); - url.Append ("("); - AppendJavaParameterTypes (java, declaringMemberJniSignature); - AppendJavaParameterTypes (url, declaringMemberJniSignature); - java.Append (")"); - url.Append (")"); + if (declaringMemberParameterString != null) { + java.Append (declaringMemberParameterString); + url.Append (declaringMemberParameterString); } } var format = new XElement ("format", @@ -270,85 +278,5 @@ static XElement CreateAndroidDocLinkUri (string prefix, string declaringJniType, return new XElement ("para", format); } - static StringBuilder AppendJavaParameterTypes (StringBuilder builder, string declaringMemberJniSignature) - { - if (string.IsNullOrEmpty (declaringMemberJniSignature) || declaringMemberJniSignature [0] != '(') - return builder; - - int startLen = builder.Length; - - for (int i = 1; i < declaringMemberJniSignature.Length; ++i) { - if (declaringMemberJniSignature [i] == ')') - break; - AppendComma (); - AppendJavaParameterType (builder, declaringMemberJniSignature, ref i); - } - - return builder; - - void AppendComma () - { - if (startLen == builder.Length) - return; - builder.Append (", "); - } - } - - static void AppendJavaParameterType (StringBuilder builder, string declaringMemberJniSignature, ref int i) - { - switch (declaringMemberJniSignature [i]) { - case '[': { - ++i; - AppendJavaParameterType (builder, declaringMemberJniSignature, ref i); - builder.Append ("[]"); - break; - } - case 'B': { - builder.Append ("byte"); - break; - } - case 'C': { - builder.Append ("char"); - break; - } - case 'D': { - builder.Append ("double"); - break; - } - case 'F': { - builder.Append ("float"); - break; - } - case 'I': { - builder.Append ("int"); - break; - } - case 'J': { - builder.Append ("long"); - break; - } - case 'L': { - int end = declaringMemberJniSignature.IndexOf (';', i); - if (end < 0) - throw new InvalidOperationException ($"INTERNAL ERROR: Invalid JNI signature '{declaringMemberJniSignature}': no ';' to end 'L' at index {i}!"); - var type = declaringMemberJniSignature.Substring (i+1, end - i - 1) - .Replace ('/', '.') - .Replace ('$', '.'); - builder.Append (type); - i = end; - break; - } - case 'S': { - builder.Append ("short"); - break; - } - case 'Z': { - builder.Append ("boolean"); - break; - } - default: - throw new NotSupportedException ($"INTERNAL ERROR: Don't know what to do with '{declaringMemberJniSignature [i]}' in '{declaringMemberJniSignature}'!"); - } - } } }