diff --git a/src/main/java/com/android/tools/metalava/JDiffXmlWriter.kt b/src/main/java/com/android/tools/metalava/JDiffXmlWriter.kt index d4dbe0b4eb3a935c543a8ab6b8a3c7134aeb64b1..8087c3f06d58716079559ae762a6dc60afb5b59f 100644 --- a/src/main/java/com/android/tools/metalava/JDiffXmlWriter.kt +++ b/src/main/java/com/android/tools/metalava/JDiffXmlWriter.kt @@ -201,12 +201,10 @@ class JDiffXmlWriter( } } else null - val fullTypeName = escapeAttributeValue(field.type().toTypeString()) - writer.print("<field name=\"") writer.print(field.name()) writer.print("\"\n type=\"") - writer.print(fullTypeName) + writer.print(escapeAttributeValue(formatType(field.type()))) writer.print("\"\n transient=\"") writer.print(modifiers.isTransient()) writer.print("\"\n volatile=\"") @@ -295,9 +293,11 @@ class JDiffXmlWriter( return } escapeAttributeValue( - superClass.toTypeString( - erased = compatibility.omitTypeParametersInInterfaces, - context = superClass.asClass() + formatType( + superClass.toTypeString( + erased = compatibility.omitTypeParametersInInterfaces, + context = superClass.asClass() + ) ) ) } @@ -325,8 +325,7 @@ class JDiffXmlWriter( interfaces.sortedWith(TypeItem.comparator).forEach { item -> writer.print("<implements name=\"") val type = item.toTypeString(erased = compatibility.omitTypeParametersInInterfaces, context = cls) - val escapedType = escapeAttributeValue(type) - writer.print(escapedType) + writer.print(escapeAttributeValue(formatType(type))) writer.println("\">\n</implements>") } } @@ -343,13 +342,12 @@ class JDiffXmlWriter( } } - private fun formatType(type: TypeItem): String { - val typeString = type.toTypeString() - return if (compatibility.spaceAfterCommaInTypes) { - typeString.replace(",", ", ").replace(", ", ", ") - } else { - typeString - } + private fun formatType(type: TypeItem): String = formatType(type.toTypeString()) + + private fun formatType(typeString: String): String { + // In JDiff we always want to include spaces after commas; the API signature tests depend + // on this. + return typeString.replace(",", ", ").replace(", ", ", ") } private fun writeThrowsList(method: MethodItem) { diff --git a/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java b/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java index b006d3cb17bab7f1bb6cb090f85d0ee712a159e7..00f3eb3126cb9c21107824d7d5c82ca1a67614a9 100644 --- a/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java +++ b/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java @@ -42,7 +42,9 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static com.android.tools.metalava.ConstantsKt.ANDROIDX_NONNULL; import static com.android.tools.metalava.ConstantsKt.ANDROIDX_NULLABLE; diff --git a/src/test/java/com/android/tools/metalava/JDiffXmlTest.kt b/src/test/java/com/android/tools/metalava/JDiffXmlTest.kt index c92458d5979b3875ea9b6c15f3036074f5e04916..1a190c2d9190c30edb14fc70cda1657d8d924851 100644 --- a/src/test/java/com/android/tools/metalava/JDiffXmlTest.kt +++ b/src/test/java/com/android/tools/metalava/JDiffXmlTest.kt @@ -711,7 +711,7 @@ class JDiffXmlTest : DriverTest() { <package name="test.pkg" > <interface name="AbstractList" - extends="test.pkg.List<A,B,C>" + extends="test.pkg.List<A, B, C>" abstract="true" static="false" final="false" @@ -720,7 +720,7 @@ class JDiffXmlTest : DriverTest() { > </interface> <interface name="ConcreteList" - extends="test.pkg.AbstractList<D,E,F>" + extends="test.pkg.AbstractList<D, E, F>" abstract="true" static="false" final="false" @@ -1041,4 +1041,90 @@ class JDiffXmlTest : DriverTest() { """ ) } + + @Test + fun `Spaces in type argument lists`() { + // JDiff expects spaces in type argument lists + // Regression test for 123140708 + check( + compatibilityMode = false, + checkDoclava1 = true, + format = FileFormat.V2, + signatureSource = """ + // Signature format: 2.0 + package org.apache.http.impl.conn.tsccm { + @Deprecated public class ConnPoolByRoute extends org.apache.http.impl.conn.tsccm.AbstractConnPool { + field @Deprecated protected final java.util.Map<org.apache.http.conn.routing.HttpRoute,org.apache.http.impl.conn.tsccm.RouteSpecificPool> routeToPool; + field @Deprecated protected java.util.Queue<org.apache.http.impl.conn.tsccm.WaitingThread> waitingThreads; + } + } + package test.pkg { + public abstract class MyClass extends HashMap<String,String> implements Map<String,String> { + field public Map<String,String> map; + } + } + """, + apiXml = + """ + <api> + <package name="org.apache.http.impl.conn.tsccm" + > + <class name="ConnPoolByRoute" + extends="org.apache.http.impl.conn.tsccm.AbstractConnPool" + abstract="false" + static="false" + final="false" + deprecated="deprecated" + visibility="public" + > + <field name="routeToPool" + type="java.util.Map<org.apache.http.conn.routing.HttpRoute, org.apache.http.impl.conn.tsccm.RouteSpecificPool>" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="deprecated" + visibility="protected" + > + </field> + <field name="waitingThreads" + type="java.util.Queue<org.apache.http.impl.conn.tsccm.WaitingThread>" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="deprecated" + visibility="protected" + > + </field> + </class> + </package> + <package name="test.pkg" + > + <class name="MyClass" + extends="java.lang.HashMap<String, String>" + abstract="true" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" + > + <implements name="java.lang.Map<String, String>"> + </implements> + <field name="map" + type="java.lang.Map<String, String>" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" + > + </field> + </class> + </package> + </api> + """ + ) + } } \ No newline at end of file