Skip to content

Commit d3f0c5c

Browse files
authored
[generator] Fix parameter string in ref doc links (#935)
Fixes: #931 Fixes: #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 `<parameter/>` elements under all methods. The `<parameter/>` elements contain attributes with additional type data; for instance: <method jni-return="Landroid/animation/ObjectAnimator;" jni-signature="(Ljava/lang/Object;Landroid/util/Property;Landroid/util/Property;Landroid/graphics/Path;)Landroid/animation/ObjectAnimator;" name="ofFloat" return="android.animation.ObjectAnimator"> <parameter jni-type="Ljava/lang/Object;" name="target" type="T"/> <parameter jni-type="Landroid/util/Property;" name="xProperty" type="android.util.Property&lt;T, java.lang.Float&gt;"/> <parameter jni-type="Landroid/util/Property;" name="yProperty" type="android.util.Property&lt;T, java.lang.Float&gt;"/> <parameter jni-type="Landroid/graphics/Path;" name="path" type="android.graphics.Path"/> <javadoc> <![CDATA[Constructs and returns an ObjectAnimator that animates coordinates along a <code>Path</code> using two properties. A <code>Path</code></> animation moves in two dimensions, animating coordinates <code>(x, y)</code> together to follow the line. In this variation, the coordinates are floats that are set to separate properties, <code>xProperty</code> and <code>yProperty</code>. @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 <code>Path</code> to animate values along. @return An ObjectAnimator object that is set up to animate along <code>path</code>.]]> </javadoc> </method> 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.
1 parent d64087c commit d3f0c5c

File tree

1 file changed

+27
-99
lines changed
  • tools/generator/Java.Interop.Tools.Generator.ObjectModel

1 file changed

+27
-99
lines changed

tools/generator/Java.Interop.Tools.Generator.ObjectModel/JavadocInfo.cs

Lines changed: 27 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ public static JavadocInfo CreateInfo (XElement element, XmldocStyle style, bool
3939
var desc = GetMemberDescription (element);
4040
string declaringJniType = desc.DeclaringJniType;
4141
string declaringMemberName = desc.DeclaringMemberName;
42-
var declaringMemberJniSignature = desc.DeclaringMemberJniSignature;
42+
var declaringMemberParamString = desc.DeclaringMemberParameterString;
4343

44-
var extras = GetExtra (element, style, declaringJniType, declaringMemberName, declaringMemberJniSignature, appendCopyrightExtra);
44+
var extras = GetExtra (element, style, declaringJniType, declaringMemberName, declaringMemberParamString, appendCopyrightExtra);
4545
XElement[] extra = extras.Extras;
4646
XElement[] copyright = extras.Copyright;
4747

@@ -54,13 +54,13 @@ public static JavadocInfo CreateInfo (XElement element, XmldocStyle style, bool
5454
Javadoc = javadoc,
5555
MemberDescription = declaringMemberName == null
5656
? declaringJniType
57-
: $"{declaringJniType}.{declaringMemberName}.{declaringMemberJniSignature}",
57+
: $"{declaringJniType}.{declaringMemberName}{declaringMemberParamString}",
5858
XmldocStyle = style,
5959
};
6060
return info;
6161
}
6262

63-
static (string DeclaringJniType, string DeclaringMemberName, string DeclaringMemberJniSignature) GetMemberDescription (XElement element)
63+
static (string DeclaringJniType, string DeclaringMemberName, string DeclaringMemberParameterString) GetMemberDescription (XElement element)
6464
{
6565
bool isType = element.Name.LocalName == "class" ||
6666
element.Name.LocalName == "interface";
@@ -76,14 +76,26 @@ public static JavadocInfo CreateInfo (XElement element, XmldocStyle style, bool
7676
string declaringMemberName = isType
7777
? null
7878
: (string) element.Attribute ("name") ?? declaringJniType.Substring (declaringJniType.LastIndexOf ('/')+1);
79+
7980
string declaringMemberJniSignature = isType
8081
? null
8182
: (string) element.Attribute ("jni-signature");
8283

83-
return (declaringJniType, declaringMemberName, declaringMemberJniSignature);
84+
85+
string declaringMemberParameterString = null;
86+
if (!isType && (declaringMemberJniSignature?.StartsWith ("(", StringComparison.Ordinal) ?? false)) {
87+
var parameterTypes = element.Elements ("parameter")?.Select (e => e.Attribute ("type")?.Value)?.ToList ();
88+
if (parameterTypes?.Any () ?? false) {
89+
declaringMemberParameterString = $"({string.Join (", ", parameterTypes)})";
90+
} else {
91+
declaringMemberParameterString = "()";
92+
}
93+
}
94+
95+
return (declaringJniType, declaringMemberName, declaringMemberParameterString);
8496
}
8597

86-
static (XElement[] Extras, XElement[] Copyright) GetExtra (XElement element, XmldocStyle style, string declaringJniType, string declaringMemberName, string declaringMemberJniSignature, bool appendCopyrightExtra)
98+
static (XElement[] Extras, XElement[] Copyright) GetExtra (XElement element, XmldocStyle style, string declaringJniType, string declaringMemberName, string declaringMemberParameterString, bool appendCopyrightExtra)
8799
{
88100
if (!style.HasFlag (XmldocStyle.IntelliSenseAndExtraRemarks))
89101
return (null, null);
@@ -107,7 +119,7 @@ public static JavadocInfo CreateInfo (XElement element, XmldocStyle style, bool
107119

108120
XElement docLink = null;
109121
if (!string.IsNullOrEmpty (urlPrefix)) {
110-
docLink = CreateDocLinkUrl (kind, urlPrefix, declaringJniType, declaringMemberName, declaringMemberJniSignature);
122+
docLink = CreateDocLinkUrl (kind, urlPrefix, declaringJniType, declaringMemberName, declaringMemberParameterString);
111123
}
112124
extra = new List<XElement> ();
113125
extra.Add (docLink);
@@ -217,26 +229,26 @@ static List<string> GetLines (string text)
217229
[ApiLinkStyle.DeveloperAndroidComReference_2020Nov] = CreateAndroidDocLinkUri,
218230
};
219231

220-
static XElement CreateDocLinkUrl (ApiLinkStyle style, string prefix, string declaringJniType, string declaringMemberName, string declaringMemberJniSignature)
232+
static XElement CreateDocLinkUrl (ApiLinkStyle style, string prefix, string declaringJniType, string declaringMemberName, string declaringMemberParameterString)
221233
{
222-
;
223234
if (style == ApiLinkStyle.None || prefix == null || declaringJniType == null)
224235
return null;
225236
if (UrlCreators.TryGetValue (style, out var creator)) {
226-
return creator (prefix, declaringJniType, declaringMemberName, declaringMemberJniSignature);
237+
return creator (prefix, declaringJniType, declaringMemberName, declaringMemberParameterString);
227238
}
228239
return null;
229240
}
230241

231-
static XElement CreateAndroidDocLinkUri (string prefix, string declaringJniType, string declaringMemberName, string declaringMemberJniSignature)
242+
static XElement CreateAndroidDocLinkUri (string prefix, string declaringJniType, string declaringMemberName, string declaringMemberParameterString)
232243
{
233244
// URL is:
234245
// * {prefix}
235246
// * declaring type in JNI format
236247
// * when `declaringJniMemberName` != null, `#{declaringJniMemberName}`
237248
// * for methods & constructors, a `(`, the arguments in *Java* syntax -- separated by `, ` -- and `)`
238249
//
239-
// Example: https://developer.android.com/reference/android/app/Application#registerOnProvideAssistDataListener(android.app.Application.OnProvideAssistDataListener)
250+
// Example: "https://developer.android.com/reference/android/app/Application#registerOnProvideAssistDataListener(android.app.Application.OnProvideAssistDataListener)"
251+
// Example: "https://developer.android.com/reference/android/animation/ObjectAnimator#ofFloat(T,%20android.util.Property%3CT,%20java.lang.Float%3E,%20float...)"
240252

241253
var java = new StringBuilder (declaringJniType)
242254
.Replace ("/", ".")
@@ -250,13 +262,9 @@ static XElement CreateAndroidDocLinkUri (string prefix, string declaringJniType,
250262
if (declaringMemberName != null) {
251263
java.Append (".").Append (declaringMemberName);
252264
url.Append ("#").Append (declaringMemberName);
253-
if (declaringMemberJniSignature?.StartsWith ("(", StringComparison.Ordinal) ?? false) {
254-
java.Append ("(");
255-
url.Append ("(");
256-
AppendJavaParameterTypes (java, declaringMemberJniSignature);
257-
AppendJavaParameterTypes (url, declaringMemberJniSignature);
258-
java.Append (")");
259-
url.Append (")");
265+
if (declaringMemberParameterString != null) {
266+
java.Append (declaringMemberParameterString);
267+
url.Append (declaringMemberParameterString);
260268
}
261269
}
262270
var format = new XElement ("format",
@@ -270,85 +278,5 @@ static XElement CreateAndroidDocLinkUri (string prefix, string declaringJniType,
270278
return new XElement ("para", format);
271279
}
272280

273-
static StringBuilder AppendJavaParameterTypes (StringBuilder builder, string declaringMemberJniSignature)
274-
{
275-
if (string.IsNullOrEmpty (declaringMemberJniSignature) || declaringMemberJniSignature [0] != '(')
276-
return builder;
277-
278-
int startLen = builder.Length;
279-
280-
for (int i = 1; i < declaringMemberJniSignature.Length; ++i) {
281-
if (declaringMemberJniSignature [i] == ')')
282-
break;
283-
AppendComma ();
284-
AppendJavaParameterType (builder, declaringMemberJniSignature, ref i);
285-
}
286-
287-
return builder;
288-
289-
void AppendComma ()
290-
{
291-
if (startLen == builder.Length)
292-
return;
293-
builder.Append (", ");
294-
}
295-
}
296-
297-
static void AppendJavaParameterType (StringBuilder builder, string declaringMemberJniSignature, ref int i)
298-
{
299-
switch (declaringMemberJniSignature [i]) {
300-
case '[': {
301-
++i;
302-
AppendJavaParameterType (builder, declaringMemberJniSignature, ref i);
303-
builder.Append ("[]");
304-
break;
305-
}
306-
case 'B': {
307-
builder.Append ("byte");
308-
break;
309-
}
310-
case 'C': {
311-
builder.Append ("char");
312-
break;
313-
}
314-
case 'D': {
315-
builder.Append ("double");
316-
break;
317-
}
318-
case 'F': {
319-
builder.Append ("float");
320-
break;
321-
}
322-
case 'I': {
323-
builder.Append ("int");
324-
break;
325-
}
326-
case 'J': {
327-
builder.Append ("long");
328-
break;
329-
}
330-
case 'L': {
331-
int end = declaringMemberJniSignature.IndexOf (';', i);
332-
if (end < 0)
333-
throw new InvalidOperationException ($"INTERNAL ERROR: Invalid JNI signature '{declaringMemberJniSignature}': no ';' to end 'L' at index {i}!");
334-
var type = declaringMemberJniSignature.Substring (i+1, end - i - 1)
335-
.Replace ('/', '.')
336-
.Replace ('$', '.');
337-
builder.Append (type);
338-
i = end;
339-
break;
340-
}
341-
case 'S': {
342-
builder.Append ("short");
343-
break;
344-
}
345-
case 'Z': {
346-
builder.Append ("boolean");
347-
break;
348-
}
349-
default:
350-
throw new NotSupportedException ($"INTERNAL ERROR: Don't know what to do with '{declaringMemberJniSignature [i]}' in '{declaringMemberJniSignature}'!");
351-
}
352-
}
353281
}
354282
}

0 commit comments

Comments
 (0)