Serialization enum coder generation
This CL adds code generation for enum coders to SchemaProcessor. It
includes unit and integration tests for the code generation and an
initial pass at a data model for the code generation environment as well
as some JavaPoet extensions to enable idomatic Kotlin.
Test: ./gradlew :serialization:serialiation-compiler:test
Bug: 144724751
Change-Id: I32286139096ee644e98fdab4e0a8701450ccc11a
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/SchemaProcessor.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/SchemaProcessor.kt
index 45c481e..f0ed964 100644
--- a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/SchemaProcessor.kt
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/SchemaProcessor.kt
@@ -16,7 +16,8 @@
package androidx.serialization.compiler
-import androidx.serialization.compiler.processing.steps.EnumCompilationStep
+import androidx.serialization.compiler.codegen.CodeGenEnvironment
+import androidx.serialization.compiler.processing.steps.EnumProcessingStep
import com.google.auto.common.BasicAnnotationProcessor
import com.google.auto.service.AutoService
import com.google.common.collect.ImmutableList
@@ -31,9 +32,12 @@
@AutoService(Processor::class)
@IncrementalAnnotationProcessor(ISOLATING)
class SchemaProcessor : BasicAnnotationProcessor() {
- override fun initSteps(): List<ProcessingStep> = ImmutableList.of(
- EnumCompilationStep(processingEnv)
- )
+ override fun initSteps(): List<ProcessingStep> {
+ val codeGenEnv = CodeGenEnvironment(processingEnv, this::class.qualifiedName)
+ return ImmutableList.of(
+ EnumProcessingStep(processingEnv, codeGenEnv)
+ )
+ }
override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latest()
}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/CodeGenEnvironment.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/CodeGenEnvironment.kt
new file mode 100644
index 0000000..d60d2d4
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/CodeGenEnvironment.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen
+
+import androidx.serialization.compiler.nullability.NULLABILITY_ANNOTATIONS
+import androidx.serialization.compiler.nullability.Nullability
+import androidx.serialization.compiler.processing.isClassPresent
+import androidx.serialization.compiler.processing.packageElement
+import com.google.auto.common.GeneratedAnnotations
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.TypeElement
+
+/**
+ * Details about the code generation environment, including the generating class and the
+ * availability of annotations.
+ */
+internal data class CodeGenEnvironment(
+ val generatingClassName: String? = null,
+ val generated: Generated? = null,
+ val nullability: Nullability? = null
+) {
+ constructor(
+ processingEnv: ProcessingEnvironment,
+ generatingClass: String? = null
+ ) : this(
+ generatingClass,
+
+ generated = GeneratedAnnotations
+ .generatedAnnotation(processingEnv.elementUtils, processingEnv.sourceVersion)
+ .map { Generated(it) }
+ .orElse(null),
+
+ nullability = NULLABILITY_ANNOTATIONS.find { nullability ->
+ processingEnv.isClassPresent(nullability.qualifiedNonNullName) &&
+ processingEnv.isClassPresent(nullability.qualifiedNullableName)
+ }
+ )
+
+ data class Generated(
+ val packageName: String,
+ val simpleName: String = "Generated"
+ ) {
+ constructor(typeElement: TypeElement) : this(
+ packageName = typeElement.packageElement.qualifiedName.toString(),
+ simpleName = typeElement.simpleName.toString()
+ )
+ }
+}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/ComplexTypeExt.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/ComplexTypeExt.kt
new file mode 100644
index 0000000..f883ac2
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/ComplexTypeExt.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen
+
+import androidx.serialization.compiler.schema.ProcessingType
+import androidx.serialization.schema.ComplexType
+import javax.lang.model.element.TypeElement
+
+/**
+ * The originating type element if present or null.
+ *
+ * This makes it easier for code generators to work with the base interface types for testing in
+ * isolation.
+ */
+internal val ComplexType.originatingElement: TypeElement?
+ get() = when (this) {
+ is ProcessingType -> this.element
+ else -> null
+ }
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/StringExt.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/StringExt.kt
new file mode 100644
index 0000000..54236f4
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/StringExt.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen
+
+import java.util.Locale
+
+/** Convert a string to lower camel case, treating '_' as a delimiter. */
+internal fun String.toLowerCamelCase(): String {
+ return if (contains('_')) {
+ split('_').joinToString(separator = "") { it.safeCapitalize() }.safeDecapitalize()
+ } else {
+ safeDecapitalize()
+ }
+}
+
+/** Replacement for [String.capitalize] that does not use experimental APIs. */
+private fun String.safeCapitalize(): String {
+ return when (length) {
+ 0 -> this
+ 1 -> toUpperCase(Locale.ENGLISH)
+ else -> substring(0, 1).toLowerCase(Locale.ENGLISH) + substring(1)
+ }
+}
+
+/** Replacement for [String.decapitalize] that does not use experimental APIs. */
+private fun String.safeDecapitalize(): String {
+ return when (length) {
+ 0 -> this
+ 1 -> toLowerCase(Locale.ENGLISH)
+ else -> substring(0, 1).toLowerCase(Locale.ENGLISH) + substring(1)
+ }
+}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/EnumCoder.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/EnumCoder.kt
new file mode 100644
index 0000000..ac59842
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/EnumCoder.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen.java
+
+import androidx.serialization.EnumValue
+import androidx.serialization.compiler.codegen.originatingElement
+import androidx.serialization.compiler.codegen.toLowerCamelCase
+import androidx.serialization.schema.Enum
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.Modifier.FINAL
+import javax.lang.model.element.Modifier.PRIVATE
+import javax.lang.model.element.Modifier.PUBLIC
+import javax.lang.model.element.Modifier.STATIC
+
+/** Get the name used for an enum coder for the supplied class name. */
+internal fun enumCoderName(enumName: ClassName): ClassName {
+ return ClassName.get(
+ enumName.packageName(),
+ enumName.simpleNames().joinToString(
+ prefix = "\$Serialization",
+ separator = "_",
+ postfix = "EnumCoder"
+ )
+ )
+}
+
+/** Generate the Java source file for an enum coder. */
+internal fun generateEnumCoder(enum: Enum, javaGenEnv: JavaGenEnvironment): JavaFile {
+ val enumClass = enum.name.toClassName()
+ val variableName = nameAllocatorOf("encode", "decode")
+ .newName(enum.name.simpleName.toLowerCamelCase())
+
+ val default = enum.values.first { it.id == EnumValue.DEFAULT }
+ val values = enum.values.filter { it.id != EnumValue.DEFAULT }.sortedBy { it.id }
+
+ return buildClass(enumCoderName(enumClass), javaGenEnv, enum.originatingElement) {
+ addModifiers(PUBLIC, FINAL)
+ addJavadoc("Serialization of enum {@link $T}.\n", enumClass)
+
+ constructor { addModifiers(PRIVATE) }
+
+ method("encode") {
+ addModifiers(PUBLIC, STATIC)
+ parameter(variableName, enumClass, javaGenEnv.nullable)
+ returns(TypeName.INT)
+
+ controlFlow("if ($N != null)", variableName) {
+ controlFlow("switch ($N)", variableName) {
+ for (value in values) {
+ switchCase("\$N", value.name) {
+ addStatement("return $L", value.id)
+ }
+ }
+ }
+ }
+
+ addCode("return $L; // $N\n", EnumValue.DEFAULT, default.name)
+ }
+
+ method("decode") {
+ addModifiers(PUBLIC, STATIC)
+ parameter("value", TypeName.INT)
+ returns(enumClass, javaGenEnv.nonNull)
+
+ controlFlow("switch (value)") {
+ for (value in values) {
+ switchCase("\$L", value.id) {
+ addStatement("return $T.$N", enumClass, value.name)
+ }
+ }
+
+ switchDefault {
+ addStatement("return $T.$N", enumClass, default.name)
+ }
+ }
+ }
+ }
+}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/JavaGenEnvironment.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/JavaGenEnvironment.kt
new file mode 100644
index 0000000..1527353
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/JavaGenEnvironment.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen.java
+
+import androidx.serialization.compiler.codegen.CodeGenEnvironment
+import com.squareup.javapoet.AnnotationSpec
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeSpec
+
+/** Code generation environment with [AnnotationSpec] wrappers. */
+internal data class JavaGenEnvironment(
+ val generatingClassName: String? = null,
+ val generated: AnnotationSpec? = null,
+ val nonNull: AnnotationSpec? = null,
+ val nullable: AnnotationSpec? = null
+) {
+ fun applyGenerated(typeSpec: TypeSpec.Builder) {
+ when {
+ generated != null -> typeSpec.addAnnotation(generated)
+ generatingClassName != null -> {
+ typeSpec.addJavadoc("\nGenerated by \$L. Do not modify.\n", generatingClassName)
+ }
+ }
+ }
+
+ constructor(codeGenEnv: CodeGenEnvironment) : this(
+ generatingClassName = codeGenEnv.generatingClassName,
+
+ generated = codeGenEnv.generated?.let { generated ->
+ codeGenEnv.generatingClassName?.let { className ->
+ AnnotationSpec.builder(ClassName.get(generated.packageName, generated.simpleName))
+ .addMember("value", "\$S", className)
+ .build()
+ }
+ },
+
+ nonNull = codeGenEnv.nullability?.let {
+ AnnotationSpec.builder(ClassName.get(it.packageName, it.nonNull)).build()
+ },
+
+ nullable = codeGenEnv.nullability?.let {
+ AnnotationSpec.builder(ClassName.get(it.packageName, it.nullable)).build()
+ }
+ )
+}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/JavaPoetExt.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/JavaPoetExt.kt
new file mode 100644
index 0000000..0a2f2f3
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/JavaPoetExt.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen.java
+
+import com.squareup.javapoet.AnnotationSpec
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.NameAllocator
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Element
+
+internal const val L = "\$L"
+internal const val N = "\$N"
+internal const val S = "\$S"
+internal const val T = "\$T"
+
+internal fun nameAllocatorOf(vararg names: String): NameAllocator {
+ return NameAllocator().apply { names.forEach { newName(it, it) } }
+}
+
+internal inline fun buildClass(
+ className: ClassName,
+ javaGenEnv: JavaGenEnvironment,
+ vararg originatingElements: Element?,
+ init: TypeSpec.Builder.() -> Unit
+): JavaFile {
+ return TypeSpec.classBuilder(className).apply {
+ init()
+ afterInit(javaGenEnv, originatingElements)
+ }.toJavaFile(className)
+}
+
+internal fun TypeSpec.Builder.toJavaFile(className: ClassName): JavaFile {
+ return JavaFile.builder(className.packageName(), build()).indent(" ").build()
+}
+
+internal inline fun TypeSpec.Builder.constructor(init: MethodSpec.Builder.() -> Unit) {
+ addMethod(MethodSpec.constructorBuilder().apply(init).build())
+}
+
+internal inline fun TypeSpec.Builder.method(name: String, init: MethodSpec.Builder.() -> Unit) {
+ addMethod(MethodSpec.methodBuilder(name).apply(init).build())
+}
+
+internal fun MethodSpec.Builder.parameter(
+ name: String,
+ type: TypeName,
+ vararg annotations: AnnotationSpec?
+) {
+ addParameter(ParameterSpec.builder(type, name).run {
+ annotations.forEach { if (it != null) addAnnotation(it) }
+ build()
+ })
+}
+
+internal fun MethodSpec.Builder.returns(
+ type: TypeName,
+ vararg annotations: AnnotationSpec?
+) {
+ returns(type)
+ annotations.forEach { if (it != null) addAnnotation(it) }
+}
+
+internal inline fun MethodSpec.Builder.controlFlow(
+ format: String,
+ vararg args: Any,
+ body: MethodSpec.Builder.() -> Unit
+) {
+ beginControlFlow(format, *args)
+ body()
+ endControlFlow()
+}
+
+internal inline fun MethodSpec.Builder.switchCase(
+ format: String,
+ vararg args: Any,
+ body: MethodSpec.Builder.() -> Unit
+) {
+ addCode("case $format:\n$>", *args)
+ body()
+ addCode("$<")
+}
+
+internal inline fun MethodSpec.Builder.switchDefault(
+ body: MethodSpec.Builder.() -> Unit
+) {
+ addCode("default:\n$>")
+ body()
+ addCode("$<")
+}
+
+internal fun TypeSpec.Builder.afterInit(
+ javaGenEnv: JavaGenEnvironment,
+ originatingElements: Array<out Element?>
+) {
+ javaGenEnv.applyGenerated(this)
+ originatingElements.forEach { if (it != null) addOriginatingElement(it) }
+}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/TypeNameExt.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/TypeNameExt.kt
new file mode 100644
index 0000000..387bedb
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/codegen/java/TypeNameExt.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen.java
+
+import androidx.serialization.schema.TypeName
+import com.squareup.javapoet.ClassName
+
+/** Convert a type name to a JavaPoet class name */
+internal fun TypeName.toClassName(): ClassName {
+ return when (names.size) {
+ 1 -> ClassName.get(packageName.orEmpty(), names.single())
+ else -> ClassName.get(
+ packageName.orEmpty(),
+ names.first(),
+ *names.drop(1).toTypedArray()
+ )
+ }
+}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/nullability/KnownAnnotations.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/nullability/KnownAnnotations.kt
new file mode 100644
index 0000000..00f798a
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/nullability/KnownAnnotations.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 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.serialization.compiler.nullability
+
+/**
+ * A list of [Nullability] annotations ordered by preference.
+ *
+ * This is used for code generation, as `androidx.annotation` may not be present in the
+ * compile-time class path and the code generator may fall back to other annotation packages.
+ * These are derived from the set of annotations recognized by the Kotlin compiler.
+ *
+ * These will also be used for determining nullability of nested messages.
+ */
+internal val NULLABILITY_ANNOTATIONS = listOf(
+ Nullability("androidx.annotation"),
+ Nullability("org.jetbrains.annotations", nonNull = "NotNull"),
+ Nullability("javax.annotation", nonNull = "Nonnull"),
+ Nullability("android.support.annotation"),
+ Nullability("android.annotation"),
+ Nullability("com.android.annotations"),
+ Nullability("org.eclipse.jdt.annotation"),
+ Nullability("org.checkerframework.checker.nullness.qual"),
+ Nullability("io.reactivex.annotations")
+)
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/nullability/Nullability.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/nullability/Nullability.kt
new file mode 100644
index 0000000..8bbe9a8
--- /dev/null
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/nullability/Nullability.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020 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.serialization.compiler.nullability
+
+/** A pair of non-null and nullable annotations */
+internal data class Nullability(
+ val packageName: String,
+ val nonNull: String = "NonNull",
+ val nullable: String = "Nullable"
+) {
+ val qualifiedNonNullName = "$packageName.$nonNull"
+ val qualifiedNullableName = "$packageName.$nullable"
+}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/ElementExt.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/ElementExt.kt
index e0ffbe2..643661f 100644
--- a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/ElementExt.kt
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/ElementExt.kt
@@ -20,8 +20,10 @@
import javax.lang.model.element.AnnotationMirror
import javax.lang.model.element.Element
import javax.lang.model.element.Modifier
+import javax.lang.model.element.PackageElement
import javax.lang.model.element.TypeElement
import javax.lang.model.element.VariableElement
+import javax.lang.model.util.SimpleElementVisitor6
import kotlin.reflect.KClass
/** Casts this element to a [TypeElement] using [MoreElements.asType]. */
@@ -39,6 +41,33 @@
return Modifier.PRIVATE in modifiers
}
+/**
+ * Determines if this element is visible to its own package.
+ *
+ * A private element or an element enclosed within a private element is not visible to its package.
+ */
+internal fun Element.isVisibleToPackage(): Boolean {
+ return accept(IsVisibleToPackageVisitor, null)
+}
+
+private object IsVisibleToPackageVisitor : SimpleElementVisitor6<Boolean, Nothing?>() {
+ override fun visitPackage(e: PackageElement, p: Nothing?): Boolean {
+ return true
+ }
+
+ override fun defaultAction(e: Element, p: Nothing?): Boolean {
+ return if (e.isPrivate()) {
+ false
+ } else {
+ e.enclosingElement.accept(this, null)
+ }
+ }
+}
+
+/** Gets the enclosing package element using [MoreElements.getPackage]. */
+internal val Element.packageElement: PackageElement
+ get() = MoreElements.getPackage(this)
+
/** Get an annotation mirror on this element, throwing if it is not directly present. */
internal operator fun Element.get(annotationClass: KClass<out Annotation>): AnnotationMirror {
return requireNotNull(getAnnotationMirror(annotationClass)) {
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/MessagerExt.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/ProcessingExt.kt
similarity index 86%
rename from serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/MessagerExt.kt
rename to serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/ProcessingExt.kt
index 2418978..6c5a561 100644
--- a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/MessagerExt.kt
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/ProcessingExt.kt
@@ -17,6 +17,7 @@
package androidx.serialization.compiler.processing
import javax.annotation.processing.Messager
+import javax.annotation.processing.ProcessingEnvironment
import javax.lang.model.element.AnnotationMirror
import javax.lang.model.element.AnnotationValue
import javax.lang.model.element.Element
@@ -24,6 +25,11 @@
import javax.tools.Diagnostic.Kind.WARNING
import kotlin.reflect.KClass
+/** Determine if a qualified class name is present in the processing environment. */
+internal fun ProcessingEnvironment.isClassPresent(qualifiedName: String): Boolean {
+ return elementUtils.getTypeElement(qualifiedName) != null
+}
+
/** Print [message] as a warning with optional positional information. */
internal inline fun Messager.warn(
element: Element? = null,
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/TypeMirrorExt.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/TypeMirrorExt.kt
deleted file mode 100644
index 92173a5..0000000
--- a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/TypeMirrorExt.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2020 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.serialization.compiler.processing
-
-import com.google.auto.common.MoreTypes
-import javax.lang.model.element.Element
-import javax.lang.model.element.TypeElement
-import javax.lang.model.type.TypeMirror
-
-/** Get the element represented by this type using [MoreTypes.asElement]. */
-internal fun TypeMirror.asElement(): Element {
- return MoreTypes.asElement(this)
-}
-
-/** Convenience wrapper for [asElement] that casts to type element. */
-internal fun TypeMirror.asTypeElement(): TypeElement {
- return asElement().asTypeElement()
-}
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/AbstractStep.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/AbstractProcessingStep.kt
similarity index 97%
rename from serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/AbstractStep.kt
rename to serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/AbstractProcessingStep.kt
index 6e50b3d..09be6e4 100644
--- a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/AbstractStep.kt
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/AbstractProcessingStep.kt
@@ -24,7 +24,7 @@
import kotlin.reflect.KClass
/** Wrapper for [ProcessingStep] for more idiomatic Kotlin. */
-internal abstract class AbstractStep(
+internal abstract class AbstractProcessingStep(
vararg annotations: KClass<out Annotation>
) : ProcessingStep {
private val javaAnnotations = ImmutableSet
diff --git a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/EnumCompilationStep.kt b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/EnumProcessingStep.kt
similarity index 71%
rename from serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/EnumCompilationStep.kt
rename to serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/EnumProcessingStep.kt
index 7153645..d8dd73b 100644
--- a/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/EnumCompilationStep.kt
+++ b/serialization/serialization-compiler/src/main/kotlin/androidx/serialization/compiler/processing/steps/EnumProcessingStep.kt
@@ -18,11 +18,15 @@
import androidx.serialization.EnumValue
import androidx.serialization.Reserved
+import androidx.serialization.compiler.codegen.CodeGenEnvironment
+import androidx.serialization.compiler.codegen.java.JavaGenEnvironment
+import androidx.serialization.compiler.codegen.java.generateEnumCoder
import androidx.serialization.compiler.processing.asTypeElement
import androidx.serialization.compiler.processing.asVariableElement
import androidx.serialization.compiler.processing.error
import androidx.serialization.compiler.processing.get
import androidx.serialization.compiler.processing.getAnnotationMirror
+import androidx.serialization.compiler.processing.isVisibleToPackage
import androidx.serialization.compiler.processing.isPrivate
import androidx.serialization.compiler.processing.processReserved
import androidx.serialization.compiler.schema.Enum
@@ -35,16 +39,18 @@
import kotlin.reflect.KClass
/** Processing step that parses and validates enums, and generates enum coders. */
-internal class EnumCompilationStep(
+internal class EnumProcessingStep(
private val processingEnv: ProcessingEnvironment,
+ private val codeGenEnv: CodeGenEnvironment,
private val onEnum: ((Enum) -> Unit)? = null
-) : AbstractStep(EnumValue::class, Reserved::class) {
+) : AbstractProcessingStep(EnumValue::class, Reserved::class) {
+ private val javaGenEnv = JavaGenEnvironment(codeGenEnv)
private val messager: Messager = processingEnv.messager
override fun process(elementsByAnnotation: Map<KClass<out Annotation>, Set<Element>>) {
elementsByAnnotation[EnumValue::class]
?.let(::processEnumValues)
- ?.forEach(::processEnumType)
+ ?.forEach(::processEnumClass)
}
/**
@@ -57,11 +63,11 @@
private fun processEnumValues(elements: Set<Element>): Set<TypeElement> {
if (elements.isEmpty()) return emptySet()
- val types = mutableSetOf<TypeElement>()
+ val enumClasses = mutableSetOf<TypeElement>()
for (element in elements) {
if (element.kind == ENUM_CONSTANT) {
- types += element.enclosingElement.asTypeElement()
+ enumClasses += element.enclosingElement.asTypeElement()
} else {
messager.error(element, EnumValue::class) {
"@${EnumValue::class.simpleName} must annotate an enum constant"
@@ -69,7 +75,7 @@
}
}
- return types
+ return enumClasses
}
/**
@@ -80,23 +86,31 @@
* reads [EnumValue.id] and constructs an [Enum] and dispatches it to [onEnum]. It fills
* [Enum.reserved] using [processReserved].
*/
- private fun processEnumType(typeElement: TypeElement) {
- check(typeElement.kind == ElementKind.ENUM) {
- "Expected $typeElement to be an enum class"
+ private fun processEnumClass(enumClass: TypeElement) {
+ check(enumClass.kind == ElementKind.ENUM) {
+ "Expected $enumClass to be an enum class"
}
var hasError = false
- if (typeElement.isPrivate()) {
- messager.error(typeElement) {
- "Enum ${typeElement.qualifiedName} is private and cannot be serialized"
+ if (!enumClass.isVisibleToPackage()) {
+ if (enumClass.isPrivate()) {
+ messager.error(enumClass) {
+ "Enum ${enumClass.qualifiedName} is private and cannot be serialized"
+ }
+ } else {
+ messager.error(enumClass) {
+ "Enum ${enumClass.qualifiedName} is not visible to its package and cannot " +
+ "be serialized"
+ }
}
+
hasError = true
}
val values = mutableSetOf<Enum.Value>()
- for (element in typeElement.enclosedElements) {
+ for (element in enumClass.enclosedElements) {
if (element.kind == ENUM_CONSTANT) {
val annotation = element.getAnnotationMirror(EnumValue::class)
@@ -112,6 +126,10 @@
}
}
- if (!hasError) onEnum?.invoke(Enum(typeElement, values, processReserved(typeElement)))
+ if (!hasError) {
+ val enum = Enum(enumClass, values, processReserved(enumClass))
+ generateEnumCoder(enum, javaGenEnv).writeTo(processingEnv.filer)
+ onEnum?.invoke(enum)
+ }
}
}
diff --git a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/SchemaProcessorTest.kt b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/SchemaProcessorTest.kt
index d602752..4ba5dc8 100644
--- a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/SchemaProcessorTest.kt
+++ b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/SchemaProcessorTest.kt
@@ -23,10 +23,10 @@
import org.junit.Test
import javax.tools.JavaFileObject
-/** Integration tests for [SchemaProcessor]. */
+/** Unit tests for [SchemaProcessor]. */
class SchemaProcessorTest {
@Test
- fun testSucceedsWithoutWarningsa() {
+ fun testSucceedsWithoutWarnings() {
val testClass = JavaFileObjects.forSourceString("Test", "public class Test {}")
assertThat(compile(testClass)).succeededWithoutWarnings()
}
diff --git a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/TestEnum.kt b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/TestEnum.kt
new file mode 100644
index 0000000..aa0c509
--- /dev/null
+++ b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/TestEnum.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 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.serialization.compiler
+
+import androidx.serialization.schema.Enum
+import androidx.serialization.schema.Reserved
+import androidx.serialization.schema.TypeName
+
+/** Customizable [Enum] implementation with default values scaffolding. */
+internal data class TestEnum(
+ override val name: TypeName = TypeName("com.example", "TestEnum"),
+ override val values: Set<Value> = setOf(
+ Value(0, "DEFAULT"),
+ Value(1, "ONE"),
+ Value(2, "TWO")
+ ),
+ override val reserved: Reserved = Reserved.empty()
+) : Enum {
+ data class Value(
+ override val id: Int,
+ override val name: String
+ ) : Enum.Value
+}
diff --git a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/codegen/Environment.kt b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/codegen/Environment.kt
new file mode 100644
index 0000000..b3a875c
--- /dev/null
+++ b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/codegen/Environment.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen
+
+import androidx.serialization.compiler.codegen.CodeGenEnvironment.Generated
+import androidx.serialization.compiler.codegen.java.JavaGenEnvironment
+import androidx.serialization.compiler.nullability.Nullability
+import kotlin.reflect.KClass
+
+internal fun codeGenEnv(
+ testClass: KClass<*>,
+ generated: Generated? = DEFAULT_GENERATED,
+ nullability: Nullability? = DEFAULT_NULLABILITY
+): CodeGenEnvironment {
+ return CodeGenEnvironment(testClass.qualifiedName, generated, nullability)
+}
+
+internal fun javaGenEnv(
+ testClass: KClass<*>,
+ generated: Generated? = DEFAULT_GENERATED,
+ nullability: Nullability? = DEFAULT_NULLABILITY
+): JavaGenEnvironment {
+ return JavaGenEnvironment(codeGenEnv(testClass, generated, nullability))
+}
+
+internal val DEFAULT_GENERATED = Generated(packageName = "javax.annotation")
+internal val DEFAULT_NULLABILITY = Nullability("androidx.annotation")
diff --git a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/codegen/java/EnumCoderTest.kt b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/codegen/java/EnumCoderTest.kt
new file mode 100644
index 0000000..57c12f8
--- /dev/null
+++ b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/codegen/java/EnumCoderTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2020 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.serialization.compiler.codegen.java
+
+import androidx.serialization.compiler.TestEnum
+import androidx.serialization.compiler.codegen.javaGenEnv
+import com.google.common.truth.Truth.assertThat
+import com.squareup.javapoet.ClassName
+import org.junit.Test
+
+/** Unit tests for [generateEnumCoder] and [enumCoderName]. */
+class EnumCoderTest {
+ @Test
+ fun testEnumCoderName() {
+ assertThat(enumCoderName(ClassName.get("com.example", "Test")))
+ .isEqualTo(ClassName.get("com.example", "\$SerializationTestEnumCoder"))
+
+ assertThat(enumCoderName(ClassName.get("", "Outer", "Inner", "TestEnum")))
+ .isEqualTo(ClassName.get("", "\$SerializationOuter_Inner_TestEnumEnumCoder"))
+ }
+
+ @Test
+ fun testGenerateEnumCoder() {
+ assertThat(generateEnumCoder(TestEnum(), javaGenEnv(this::class)).toString()).contains("""
+ package com.example;
+
+ import androidx.annotation.NonNull;
+ import androidx.annotation.Nullable;
+ import javax.annotation.Generated;
+
+ /**
+ * Serialization of enum {@link TestEnum}.
+ */
+ @Generated("androidx.serialization.compiler.codegen.java.EnumCoderTest")
+ public final class ${'$'}SerializationTestEnumEnumCoder {
+ private ${'$'}SerializationTestEnumEnumCoder() {
+ }
+
+ public static int encode(@Nullable TestEnum testEnum) {
+ if (testEnum != null) {
+ switch (testEnum) {
+ case ONE:
+ return 1;
+ case TWO:
+ return 2;
+ }
+ }
+ return 0; // DEFAULT
+ }
+
+ @NonNull
+ public static TestEnum decode(int value) {
+ switch (value) {
+ case 1:
+ return TestEnum.ONE;
+ case 2:
+ return TestEnum.TWO;
+ default:
+ return TestEnum.DEFAULT;
+ }
+ }
+ }
+ """.trimIndent())
+ }
+}
diff --git a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/ProcessReservedTest.kt b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/ProcessReservedTest.kt
index 02dcb83..a6c517d 100644
--- a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/ProcessReservedTest.kt
+++ b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/ProcessReservedTest.kt
@@ -16,7 +16,7 @@
package androidx.serialization.compiler.processing
-import androidx.serialization.compiler.processing.steps.AbstractStep
+import androidx.serialization.compiler.processing.steps.AbstractProcessingStep
import androidx.serialization.schema.Reserved
import com.google.auto.common.BasicAnnotationProcessor
import com.google.common.truth.Truth.assertThat
@@ -76,9 +76,9 @@
return processor.reserved
}
- private class ReservedStep(
+ private class ReservedProcessingStep(
private val onReserved: (Reserved) -> Unit
- ) : AbstractStep(androidx.serialization.Reserved::class) {
+ ) : AbstractProcessingStep(androidx.serialization.Reserved::class) {
override fun process(elementsByAnnotation: Map<KClass<out Annotation>, Set<Element>>) {
elementsByAnnotation[androidx.serialization.Reserved::class]?.forEach {
onReserved(processReserved(it.asTypeElement()))
@@ -90,7 +90,7 @@
lateinit var reserved: Reserved
override fun initSteps(): List<ProcessingStep> = listOf(
- ReservedStep { reserved = it }
+ ReservedProcessingStep { reserved = it }
)
override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latest()
diff --git a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/steps/EnumCompilationStepTest.kt b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/steps/EnumProcessingStepTest.kt
similarity index 70%
rename from serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/steps/EnumCompilationStepTest.kt
rename to serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/steps/EnumProcessingStepTest.kt
index d0b7919..d83a3ee 100644
--- a/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/steps/EnumCompilationStepTest.kt
+++ b/serialization/serialization-compiler/src/test/kotlin/androidx/serialization/compiler/processing/steps/EnumProcessingStepTest.kt
@@ -16,6 +16,7 @@
package androidx.serialization.compiler.processing.steps
+import androidx.serialization.compiler.codegen.CodeGenEnvironment
import androidx.serialization.compiler.schema.Enum
import androidx.serialization.schema.Reserved
import com.google.auto.common.BasicAnnotationProcessor
@@ -29,15 +30,15 @@
import javax.lang.model.SourceVersion
import javax.tools.JavaFileObject
-/** Unit tests for [EnumCompilationStep]. */
-class EnumCompilationStepTest {
+/** Unit tests for [EnumProcessingStep]. */
+class EnumProcessingStepTest {
private val enumValueCorrespondence = Correspondence.from({
actual: Enum.Value?, expected: Pair<Int, String>? ->
actual?.id == expected?.first && actual?.name == expected?.second
}, "has ID and name")
@Test
- fun testEnum() {
+ fun testParsing() {
val enum = compileEnum(JavaFileObjects.forSourceString("TestEnum", """
import androidx.serialization.EnumValue;
@@ -70,8 +71,25 @@
""".trimIndent())
assertThat(compile(testEnum)).hadErrorContaining(
- "Enum com.example.PrivateEnumTest.PrivateEnum is private and cannot be serialized"
- )
+ "Enum com.example.PrivateEnumTest.PrivateEnum is private and cannot be serialized")
+ }
+
+ @Test
+ fun testInvalidPrivateNestedEnum() {
+ val testEnum = JavaFileObjects.forSourceString("PrivateNestedEnumTest", """
+ import androidx.serialization.EnumValue;
+
+ public class PrivateNestedEnumTest {
+ private static class NestedClass {
+ public enum NestedEnum {
+ @EnumValue(EnumValue.DEFAULT) TEST
+ }
+ }
+ }
+ """.trimIndent())
+
+ assertThat(compile(testEnum)).hadErrorContaining(
+ "Enum PrivateNestedEnumTest.NestedClass.NestedEnum is not visible to its package")
}
@Test
@@ -106,6 +124,26 @@
"annotated with @EnumValue")
}
+ @Test
+ fun testCoderGeneration() {
+ val testEnum = JavaFileObjects.forSourceString("com.example.Test", """
+ package com.example;
+ import androidx.serialization.EnumValue;
+
+ public enum Test {
+ @EnumValue(EnumValue.DEFAULT)
+ DEFAULT,
+ @EnumValue(1)
+ ONE,
+ @EnumValue(2)
+ TWO
+ }
+ """.trimIndent())
+
+ assertThat(compile(testEnum))
+ .generatedSourceFile("com.example.\$SerializationTestEnumCoder")
+ }
+
private fun compile(vararg sources: JavaFileObject): Compilation {
return javac().withProcessors(SchemaCompilationProcessor()).compile(*sources)
}
@@ -115,18 +153,16 @@
assertThat(javac().withProcessors(processor).compile(source))
.succeededWithoutWarnings()
- val enums = processor.enums
- assertThat(enums).hasSize(1)
-
- return enums.single()
+ return processor.enum
}
private class SchemaCompilationProcessor : BasicAnnotationProcessor() {
- val enums = mutableSetOf<Enum>()
+ lateinit var enum: Enum
- override fun initSteps(): List<ProcessingStep> = listOf(
- EnumCompilationStep(processingEnv) { enums += it }
- )
+ override fun initSteps(): List<ProcessingStep> {
+ val codeGenEnv = CodeGenEnvironment(EnumProcessingStepTest::class.qualifiedName)
+ return listOf(EnumProcessingStep(processingEnv, codeGenEnv) { enum = it })
+ }
override fun getSupportedSourceVersion(): SourceVersion {
return SourceVersion.latest()