Merge "Add converter APIs in XProcessing" into androidx-main
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
index 9293a8b..90fe22c 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
@@ -108,6 +108,12 @@
fun isCompanionObject(): Boolean
/**
+ * Fields declared in this type
+ * includes all instance/static fields in this
+ */
+ fun getDeclaredFields(): List<XFieldElement>
+
+ /**
* All fields, including private supers.
* Room only ever reads fields this way.
*/
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
new file mode 100644
index 0000000..4d5e1ee
--- /dev/null
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.compat
+
+import androidx.room.compiler.processing.XElement
+import androidx.room.compiler.processing.XExecutableElement
+import androidx.room.compiler.processing.XProcessingEnv
+import androidx.room.compiler.processing.XType
+import androidx.room.compiler.processing.XTypeElement
+import androidx.room.compiler.processing.XVariableElement
+import androidx.room.compiler.processing.javac.JavacElement
+import androidx.room.compiler.processing.javac.JavacExecutableElement
+import androidx.room.compiler.processing.javac.JavacProcessingEnv
+import androidx.room.compiler.processing.javac.JavacType
+import androidx.room.compiler.processing.javac.JavacTypeElement
+import androidx.room.compiler.processing.javac.JavacVariableElement
+import javax.lang.model.element.Element
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.TypeElement
+import javax.lang.model.element.VariableElement
+import javax.lang.model.type.TypeMirror
+
+// Migration APIs for converting between Javac and XProcessing types.
+object XConverters {
+
+ @JvmStatic
+ fun XElement.toJavac(): Element = (this as JavacElement).element
+
+ @JvmStatic
+ fun XTypeElement.toJavac(): TypeElement = (this as JavacTypeElement).element
+
+ @JvmStatic
+ fun XExecutableElement.toJavac(): ExecutableElement = (this as JavacExecutableElement).element
+
+ @JvmStatic
+ fun XVariableElement.toJavac(): VariableElement = (this as JavacVariableElement).element
+
+ @JvmStatic
+ fun XType.toJavac(): TypeMirror = (this as JavacType).typeMirror
+
+ @JvmStatic
+ fun Element.toXProcessing(env: XProcessingEnv): XElement {
+ return when (this) {
+ is TypeElement -> this.toXProcessing(env)
+ is ExecutableElement -> this.toXProcessing(env)
+ is VariableElement -> this.toXProcessing(env)
+ else -> error(
+ "Don't know how to convert element of type '${this::class}' to a XElement"
+ )
+ }
+ }
+
+ @JvmStatic
+ fun TypeElement.toXProcessing(env: XProcessingEnv): XTypeElement =
+ (env as JavacProcessingEnv).wrapTypeElement(this)
+
+ @JvmStatic
+ fun ExecutableElement.toXProcessing(env: XProcessingEnv): XExecutableElement =
+ (env as JavacProcessingEnv).wrapExecutableElement(this)
+
+ @JvmStatic
+ fun VariableElement.toXProcessing(env: XProcessingEnv): XVariableElement =
+ (env as JavacProcessingEnv).wrapVariableElement(this)
+
+ // TODO: TypeMirror to XType, this will be more complicated since location context is lost...
+}
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
index fa5ad96..3aca458 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
@@ -57,6 +57,20 @@
element.enclosingType(env)
}
+ private val _declaredFields by lazy {
+ ElementFilter.fieldsIn(element.enclosedElements).map {
+ JavacFieldElement(
+ env = env,
+ element = it,
+ containing = this
+ )
+ }
+ }
+
+ override fun getDeclaredFields(): List<XFieldElement> {
+ return _declaredFields
+ }
+
private val _allFieldsIncludingPrivateSupers by lazy {
element.getAllFieldsIncludingPrivateSupers(
env.elementUtils
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
index 1d21c53..24dffb3 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
@@ -134,13 +134,7 @@
private val _declaredFieldsIncludingSupers by lazy {
// Read all properties from all supers and select the ones that are not overridden.
- val myPropertyFields = if (declaration.classKind == ClassKind.INTERFACE) {
- _declaredProperties.filter {
- it.isStatic()
- }
- } else {
- _declaredProperties.filter { !it.isAbstract() }
- }
+ val myPropertyFields = getDeclaredFields()
val selectedNames = myPropertyFields.mapTo(mutableSetOf()) {
it.name
}
@@ -268,6 +262,18 @@
return !isInterface() && !declaration.isOpen()
}
+ private val _declaredFields by lazy {
+ if (declaration.classKind == ClassKind.INTERFACE) {
+ _declaredProperties.filter { it.isStatic() }
+ } else {
+ _declaredProperties.filter { !it.isAbstract() }
+ }
+ }
+
+ override fun getDeclaredFields(): List<XFieldElement> {
+ return _declaredFields
+ }
+
override fun getAllFieldsIncludingPrivateSupers(): List<XFieldElement> {
return _declaredFieldsIncludingSupers
}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
index e6fc8e1..342c4ad 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
@@ -336,8 +336,12 @@
runProcessorTest(sources = listOf(src)) { invocation ->
val baseClass = invocation.processingEnv.requireTypeElement("BaseClass")
assertThat(baseClass.getAllFieldNames()).containsExactly("genericProp")
+ assertThat(baseClass.getDeclaredFields().map { it.name })
+ .containsExactly("genericProp")
val subClass = invocation.processingEnv.requireTypeElement("SubClass")
assertThat(subClass.getAllFieldNames()).containsExactly("genericProp", "subClassProp")
+ assertThat(subClass.getDeclaredFields().map { it.name })
+ .containsExactly("subClassProp")
val baseMethod = baseClass.getMethod("baseMethod")
baseMethod.asMemberOf(subClass.type).let { methodType ->
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt
new file mode 100644
index 0000000..3c4b793
--- /dev/null
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/compat/XConvertersTest.kt
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.compat
+
+import androidx.room.compiler.processing.compat.XConverters.toJavac
+import androidx.room.compiler.processing.compat.XConverters.toXProcessing
+import androidx.room.compiler.processing.javac.JavacProcessingEnv
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.XTestInvocation
+import androidx.room.compiler.processing.util.getDeclaredField
+import androidx.room.compiler.processing.util.getDeclaredMethod
+import androidx.room.compiler.processing.util.runKaptTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import javax.lang.model.util.ElementFilter
+
+class XConvertersTest {
+
+ val kotlinSrc = Source.kotlin(
+ "KotlinClass.kt",
+ """
+ class KotlinClass {
+ var field = 1
+ fun foo(param: Int) {
+ }
+ }
+ """.trimIndent()
+ )
+ val javaSrc = Source.java(
+ "JavaClass",
+ """
+ public class JavaClass {
+ public int field = 1;
+ public void foo(int param) {
+ }
+ }
+ """.trimIndent()
+ )
+
+ @Test
+ fun typeElement() {
+ runKaptTest(
+ sources = listOf(kotlinSrc, javaSrc)
+ ) { invocation ->
+ val kotlinClass = invocation.processingEnv.requireTypeElement("KotlinClass")
+ val javaClass = invocation.processingEnv.requireTypeElement("JavaClass")
+
+ assertThat(kotlinClass.toJavac())
+ .isEqualTo(invocation.getJavacTypeElement("KotlinClass"))
+ assertThat(javaClass.toJavac())
+ .isEqualTo(invocation.getJavacTypeElement("JavaClass"))
+
+ assertThat(
+ invocation.getJavacTypeElement("KotlinClass")
+ .toXProcessing(invocation.processingEnv)
+ ).isEqualTo(kotlinClass)
+ assertThat(
+ invocation.getJavacTypeElement("JavaClass")
+ .toXProcessing(invocation.processingEnv)
+ ).isEqualTo(javaClass)
+ }
+ }
+
+ @Test
+ fun executableElement() {
+ runKaptTest(
+ sources = listOf(kotlinSrc, javaSrc)
+ ) { invocation ->
+ val kotlinClass = invocation.processingEnv.requireTypeElement("KotlinClass")
+ val javaClass = invocation.processingEnv.requireTypeElement("JavaClass")
+
+ assertThat(
+ kotlinClass.getDeclaredMethods().map { it.toJavac() }
+ ).containsExactlyElementsIn(
+ ElementFilter.methodsIn(
+ invocation.getJavacTypeElement("KotlinClass").enclosedElements
+ )
+ )
+ assertThat(
+ javaClass.getDeclaredMethods().map { it.toJavac() }
+ ).containsExactlyElementsIn(
+ ElementFilter.methodsIn(
+ invocation.getJavacTypeElement("JavaClass").enclosedElements
+ )
+ )
+
+ val kotlinFoo = ElementFilter.methodsIn(
+ invocation.getJavacTypeElement("KotlinClass").enclosedElements
+ ).first { it.simpleName.toString() == "foo" }
+ assertThat(kotlinFoo.toXProcessing(invocation.processingEnv))
+ .isEqualTo(kotlinClass.getDeclaredMethod("foo"))
+ val javaFoo = ElementFilter.methodsIn(
+ invocation.getJavacTypeElement("JavaClass").enclosedElements
+ ).first { it.simpleName.toString() == "foo" }
+ assertThat(javaFoo.toXProcessing(invocation.processingEnv))
+ .isEqualTo(javaClass.getDeclaredMethod("foo"))
+ }
+ }
+
+ @Test
+ fun variableElement_field() {
+ runKaptTest(
+ sources = listOf(kotlinSrc, javaSrc)
+ ) { invocation ->
+ val kotlinClass = invocation.processingEnv.requireTypeElement("KotlinClass")
+ val javaClass = invocation.processingEnv.requireTypeElement("JavaClass")
+
+ assertThat(
+ kotlinClass.getDeclaredFields().map { it.toJavac() }
+ ).containsExactlyElementsIn(
+ ElementFilter.fieldsIn(
+ invocation.getJavacTypeElement("KotlinClass").enclosedElements
+ )
+ )
+ assertThat(
+ javaClass.getDeclaredFields().map { it.toJavac() }
+ ).containsExactlyElementsIn(
+ ElementFilter.fieldsIn(
+ invocation.getJavacTypeElement("JavaClass").enclosedElements
+ )
+ )
+
+ val kotlinField = ElementFilter.fieldsIn(
+ invocation.getJavacTypeElement("KotlinClass").enclosedElements
+ ).first { it.simpleName.toString() == "field" }
+ assertThat(kotlinField.toXProcessing(invocation.processingEnv))
+ .isEqualTo(kotlinClass.getDeclaredField("field"))
+ val javaField = ElementFilter.fieldsIn(
+ invocation.getJavacTypeElement("JavaClass").enclosedElements
+ ).first { it.simpleName.toString() == "field" }
+ assertThat(javaField.toXProcessing(invocation.processingEnv))
+ .isEqualTo(javaClass.getDeclaredField("field"))
+ }
+ }
+
+ @Test
+ fun variableElement_parameter() {
+ runKaptTest(
+ sources = listOf(kotlinSrc, javaSrc)
+ ) { invocation ->
+ val kotlinClass = invocation.processingEnv.requireTypeElement("KotlinClass")
+ val javaClass = invocation.processingEnv.requireTypeElement("JavaClass")
+
+ assertThat(
+ kotlinClass.getDeclaredMethod("foo").parameters.map { it.toJavac() }
+ ).containsExactlyElementsIn(
+ ElementFilter.methodsIn(
+ invocation.getJavacTypeElement("KotlinClass").enclosedElements
+ ).first { it.simpleName.toString() == "foo" }.parameters
+ )
+ assertThat(
+ javaClass.getDeclaredMethod("foo").parameters.map { it.toJavac() }
+ ).containsExactlyElementsIn(
+ ElementFilter.methodsIn(
+ invocation.getJavacTypeElement("JavaClass").enclosedElements
+ ).first { it.simpleName.toString() == "foo" }.parameters
+ )
+
+ val kotlinParam = ElementFilter.methodsIn(
+ invocation.getJavacTypeElement("KotlinClass").enclosedElements
+ ).first { it.simpleName.toString() == "foo" }.parameters.first()
+ assertThat(kotlinParam.toXProcessing(invocation.processingEnv))
+ .isEqualTo(kotlinClass.getDeclaredMethod("foo").parameters.first())
+ val javaParam = ElementFilter.methodsIn(
+ invocation.getJavacTypeElement("JavaClass").enclosedElements
+ ).first { it.simpleName.toString() == "foo" }.parameters.first()
+ assertThat(javaParam.toXProcessing(invocation.processingEnv))
+ .isEqualTo(javaClass.getDeclaredMethod("foo").parameters.first())
+ }
+ }
+
+ private fun XTestInvocation.getJavacTypeElement(fqn: String) =
+ (this.processingEnv as JavacProcessingEnv).delegate.elementUtils.getTypeElement(fqn)
+}
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/util/TestExtensions.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/util/TestExtensions.kt
index 0e8f322..59ceb02 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/util/TestExtensions.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/util/TestExtensions.kt
@@ -23,6 +23,10 @@
it.name
}
+fun XTypeElement.getDeclaredField(name: String) = getDeclaredFields().first {
+ it.name == name
+}
+
fun XTypeElement.getField(name: String) = getAllFieldsIncludingPrivateSupers().first {
it.name == name
}