diff --git a/kotlin/codegen/pom.xml b/kotlin/codegen/pom.xml
index 21bda8e..81d75ee 100644
--- a/kotlin/codegen/pom.xml
+++ b/kotlin/codegen/pom.xml
@@ -27,6 +27,26 @@
kotlinpoet
${kotlinpoet.version}
+
+ org.jetbrains.kotlinx
+ kotlinx-metadata-jvm
+ 0.1.0
+
+
+ com.squareup
+ kotlinpoet-metadata
+ ${kotlinpoet.version}
+
+
+ com.squareup
+ kotlinpoet-metadata-specs
+ ${kotlinpoet.version}
+
+
+ com.squareup
+ kotlinpoet-classinspector-elements
+ ${kotlinpoet.version}
+
net.ltgt.gradle.incap
incap
@@ -34,11 +54,6 @@
provided
true
-
- com.google.auto
- auto-common
- ${auto-common.version}
-
com.google.auto.service
auto-service-annotations
@@ -67,10 +82,6 @@
The Kotlin compiler usage must be near the end of the list because its .jar file includes an
obsolete version of Guava!
-->
-
- me.eugeniomarletti.kotlin.metadata
- kotlin-metadata
-
com.github.tschuchortdev
kotlin-compile-testing
@@ -135,6 +146,12 @@
+
+
+
+ -Xuse-experimental=com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview
+
+
org.apache.maven.plugins
diff --git a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/AppliedType.kt b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/AppliedType.kt
index a3e27e9..91d9de2 100644
--- a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/AppliedType.kt
+++ b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/AppliedType.kt
@@ -15,10 +15,6 @@
*/
package com.squareup.moshi.kotlin.codegen
-import com.squareup.kotlinpoet.TypeName
-import com.squareup.kotlinpoet.TypeVariableName
-import com.squareup.kotlinpoet.asTypeName
-import com.squareup.moshi.kotlin.codegen.api.TypeResolver
import javax.lang.model.element.TypeElement
import javax.lang.model.type.DeclaredType
import javax.lang.model.util.Types
@@ -29,7 +25,6 @@ import javax.lang.model.util.Types
*/
internal class AppliedType private constructor(
val element: TypeElement,
- val resolver: TypeResolver,
private val mirror: DeclaredType
) {
/** Returns all supertypes of this, recursively. Includes both interface and class supertypes. */
@@ -41,32 +36,17 @@ internal class AppliedType private constructor(
for (supertype in types.directSupertypes(mirror)) {
val supertypeDeclaredType = supertype as DeclaredType
val supertypeElement = supertypeDeclaredType.asElement() as TypeElement
- val appliedSupertype = AppliedType(supertypeElement,
- resolver(supertypeElement, supertypeDeclaredType), supertypeDeclaredType)
+ val appliedSupertype = AppliedType(supertypeElement, supertypeDeclaredType)
appliedSupertype.supertypes(types, result)
}
return result
}
- /** Returns a resolver that uses `element` and `mirror` to resolve type parameters. */
- private fun resolver(element: TypeElement, mirror: DeclaredType): TypeResolver {
- return object : TypeResolver() {
- override fun resolveTypeVariable(typeVariable: TypeVariableName): TypeName {
- val index = element.typeParameters.indexOfFirst {
- it.simpleName.toString() == typeVariable.name
- }
- check(index != -1) { "Unexpected type variable $typeVariable in $mirror" }
- val argument = mirror.typeArguments[index]
- return argument.asTypeName()
- }
- }
- }
-
override fun toString() = mirror.toString()
companion object {
fun get(typeElement: TypeElement): AppliedType {
- return AppliedType(typeElement, TypeResolver(), typeElement.asType() as DeclaredType)
+ return AppliedType(typeElement, typeElement.asType() as DeclaredType)
}
}
-}
\ No newline at end of file
+}
diff --git a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessor.kt b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessor.kt
index 7517696..a5e19f8 100644
--- a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessor.kt
+++ b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessor.kt
@@ -16,22 +16,25 @@
package com.squareup.moshi.kotlin.codegen
import com.google.auto.service.AutoService
+import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.asClassName
+import com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview
import com.squareup.moshi.JsonClass
import com.squareup.moshi.kotlin.codegen.api.AdapterGenerator
import com.squareup.moshi.kotlin.codegen.api.PropertyGenerator
-import me.eugeniomarletti.kotlin.metadata.KotlinMetadataUtils
-import me.eugeniomarletti.kotlin.processing.KotlinAbstractProcessor
import net.ltgt.gradle.incap.IncrementalAnnotationProcessor
import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING
+import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.Filer
+import javax.annotation.processing.Messager
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.Processor
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.SourceVersion
-import javax.lang.model.element.Element
import javax.lang.model.element.TypeElement
-import javax.lang.model.element.VariableElement
-import javax.tools.Diagnostic.Kind.ERROR
+import javax.lang.model.util.Elements
+import javax.lang.model.util.Types
+import javax.tools.Diagnostic
/**
* An annotation processor that reads Kotlin data classes and generates Moshi JsonAdapters for them.
@@ -40,13 +43,11 @@ import javax.tools.Diagnostic.Kind.ERROR
*
* The generated class will match the visibility of the given data class (i.e. if it's internal, the
* adapter will also be internal).
- *
- * If you define a companion object, a jsonAdapter() extension function will be generated onto it.
- * If you don't want this though, you can use the runtime [JsonClass] factory implementation.
*/
+@KotlinPoetMetadataPreview
@AutoService(Processor::class)
@IncrementalAnnotationProcessor(ISOLATING)
-class JsonClassCodegenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils {
+class JsonClassCodegenProcessor : AbstractProcessor() {
companion object {
/**
@@ -65,6 +66,10 @@ class JsonClassCodegenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils
)
}
+ private lateinit var types: Types
+ private lateinit var elements: Elements
+ private lateinit var filer: Filer
+ private lateinit var messager: Messager
private val annotation = JsonClass::class.java
private var generatedType: TypeElement? = null
@@ -83,16 +88,37 @@ class JsonClassCodegenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils
}
processingEnv.elementUtils.getTypeElement(it)
}
+ this.types = processingEnv.typeUtils
+ this.elements = processingEnv.elementUtils
+ this.filer = processingEnv.filer
+ this.messager = processingEnv.messager
}
override fun process(annotations: Set, roundEnv: RoundEnvironment): Boolean {
for (type in roundEnv.getElementsAnnotatedWith(annotation)) {
+ if (type !is TypeElement) {
+ messager.printMessage(
+ Diagnostic.Kind.ERROR, "@JsonClass can't be applied to $type: must be a Kotlin class",
+ type)
+ continue
+ }
val jsonClass = type.getAnnotation(annotation)
if (jsonClass.generateAdapter && jsonClass.generator.isEmpty()) {
val generator = adapterGenerator(type) ?: continue
generator
- .generateFile(generatedType?.asClassName()) {
+ .generateFile {
it.toBuilder()
+ .apply {
+ generatedType?.asClassName()?.let { generatedClassName ->
+ addAnnotation(
+ AnnotationSpec.builder(generatedClassName)
+ .addMember("value = [%S]",
+ JsonClassCodegenProcessor::class.java.canonicalName)
+ .addMember("comments = %S", "https://github.com/square/moshi")
+ .build()
+ )
+ }
+ }
.addOriginatingElement(type)
.build()
}
@@ -103,12 +129,12 @@ class JsonClassCodegenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils
return false
}
- private fun adapterGenerator(element: Element): AdapterGenerator? {
- val type = targetType(messager, elementUtils, typeUtils, element) ?: return null
+ private fun adapterGenerator(element: TypeElement): AdapterGenerator? {
+ val type = targetType(messager, elements, types, element) ?: return null
val properties = mutableMapOf()
for (property in type.properties.values) {
- val generator = property.generator(messager)
+ val generator = property.generator(messager, element, elements)
if (generator != null) {
properties[property.name] = generator
}
@@ -117,7 +143,9 @@ class JsonClassCodegenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils
for ((name, parameter) in type.constructor.parameters) {
if (type.properties[parameter.name] == null && !parameter.hasDefault) {
messager.printMessage(
- ERROR, "No property for required constructor parameter $name", parameter.tag())
+ Diagnostic.Kind.ERROR,
+ "No property for required constructor parameter $name",
+ element)
return null
}
}
diff --git a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/AdapterGenerator.kt b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/AdapterGenerator.kt
index 90b884f..3ea13ed 100644
--- a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/AdapterGenerator.kt
+++ b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/AdapterGenerator.kt
@@ -17,7 +17,6 @@ package com.squareup.moshi.kotlin.codegen.api
import com.squareup.kotlinpoet.ARRAY
import com.squareup.kotlinpoet.AnnotationSpec
-import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
@@ -36,7 +35,6 @@ import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter
import com.squareup.moshi.Moshi
import com.squareup.moshi.internal.Util
-import com.squareup.moshi.kotlin.codegen.JsonClassCodegenProcessor
import java.lang.reflect.Constructor
import java.lang.reflect.Type
@@ -83,7 +81,8 @@ internal class AdapterGenerator(
nameAllocator.newName("options"), JsonReader.Options::class.asTypeName(),
KModifier.PRIVATE)
.initializer("%T.of(${nonTransientProperties.joinToString(", ") {
- CodeBlock.of("%S", it.jsonName).toString()
+ // We manually put in quotes because we know the jsonName is already escaped
+ CodeBlock.of("\"%L\"", it.jsonName).toString()
}})", JsonReader.Options::class.asTypeName())
.build()
@@ -96,27 +95,20 @@ internal class AdapterGenerator(
.initializer("null")
.build()
- fun generateFile(generatedOption: ClassName?, typeHook: (TypeSpec) -> TypeSpec = { it }): FileSpec {
+ fun generateFile(typeHook: (TypeSpec) -> TypeSpec = { it }): FileSpec {
for (property in nonTransientProperties) {
property.allocateNames(nameAllocator)
}
val result = FileSpec.builder(className.packageName, adapterName)
result.addComment("Code generated by moshi-kotlin-codegen. Do not edit.")
- result.addType(generateType(generatedOption).let(typeHook))
+ result.addType(generateType().let(typeHook))
return result.build()
}
- private fun generateType(generatedOption: ClassName?): TypeSpec {
+ private fun generateType(): TypeSpec {
val result = TypeSpec.classBuilder(adapterName)
- generatedOption?.let {
- result.addAnnotation(AnnotationSpec.builder(it)
- .addMember("value = [%S]", JsonClassCodegenProcessor::class.java.canonicalName)
- .addMember("comments = %S", "https://github.com/square/moshi")
- .build())
- }
-
result.superclass(jsonAdapterTypeName)
if (typeVariables.isNotEmpty()) {
@@ -366,7 +358,8 @@ internal class AdapterGenerator(
result.addStatement("%N.beginObject()", writerParam)
nonTransientProperties.forEach { property ->
- result.addStatement("%N.name(%S)", writerParam, property.jsonName)
+ // We manually put in quotes because we know the jsonName is already escaped
+ result.addStatement("%N.name(\"%L\")", writerParam, property.jsonName)
result.addStatement("%N.toJson(%N, %N.%N)",
nameAllocator[property.delegateKey], writerParam, valueParam, property.name)
}
diff --git a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/TargetParameter.kt b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/TargetParameter.kt
index 9c07707..b9e10bd 100644
--- a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/TargetParameter.kt
+++ b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/TargetParameter.kt
@@ -16,7 +16,6 @@
package com.squareup.moshi.kotlin.codegen.api
import com.squareup.kotlinpoet.AnnotationSpec
-import kotlin.reflect.KClass
/** A parameter in user code that should be populated by generated code. */
internal data class TargetParameter(
@@ -24,15 +23,5 @@ internal data class TargetParameter(
val index: Int,
val hasDefault: Boolean,
val jsonName: String = name,
- val qualifiers: Set? = null,
- private val tags: Map, Any>
-) {
- /** Returns the tag attached with [type] as a key, or null if no tag is attached with that key. */
- fun tag(type: KClass): T? {
- @Suppress("UNCHECKED_CAST")
- return tags[type] as T?
- }
-
- /** Returns the tag attached with [T] as a key, or null if no tag is attached with that key. */
- inline fun tag(): T? = tag(T::class)
-}
+ val qualifiers: Set? = null
+)
diff --git a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/TargetProperty.kt b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/TargetProperty.kt
index 20c5958..1c141d4 100644
--- a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/TargetProperty.kt
+++ b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/TargetProperty.kt
@@ -15,23 +15,19 @@
*/
package com.squareup.moshi.kotlin.codegen.api
-import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeName
/** A property in user code that maps to JSON. */
internal data class TargetProperty(
- val name: String,
- val type: TypeName,
+ val propertySpec: PropertySpec,
val parameter: TargetParameter?,
- val annotationHolder: FunSpec?,
- val field: PropertySpec?,
- val setter: FunSpec?,
- val getter: FunSpec?,
val visibility: KModifier,
val jsonName: String
) {
+ val name: String get() = propertySpec.name
+ val type: TypeName get() = propertySpec.type
val parameterIndex get() = parameter?.index ?: -1
val hasDefault get() = parameter?.hasDefault ?: true
diff --git a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/TypeResolver.kt b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/TypeResolver.kt
deleted file mode 100644
index c956702..0000000
--- a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/api/TypeResolver.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2018 Square, Inc.
- *
- * 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 com.squareup.moshi.kotlin.codegen.api
-
-import com.squareup.kotlinpoet.ClassName
-import com.squareup.kotlinpoet.ParameterizedTypeName
-import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
-import com.squareup.kotlinpoet.TypeName
-import com.squareup.kotlinpoet.TypeVariableName
-import com.squareup.kotlinpoet.WildcardTypeName
-
-/**
- * Resolves type parameters against a type declaration. Use this to fill in type variables with
- * their actual type parameters.
- */
-open class TypeResolver {
- open fun resolveTypeVariable(typeVariable: TypeVariableName): TypeName = typeVariable
-
- fun resolve(typeName: TypeName): TypeName {
- return when (typeName) {
- is ClassName -> typeName
-
- is ParameterizedTypeName -> {
- typeName.rawType.parameterizedBy(*(typeName.typeArguments.map { resolve(it) }.toTypedArray()))
- .copy(nullable = typeName.isNullable)
- }
-
- is WildcardTypeName -> {
- when {
- typeName.inTypes.size == 1 -> {
- WildcardTypeName.consumerOf(resolve(typeName.inTypes[0]))
- .copy(nullable = typeName.isNullable)
- }
- typeName.outTypes.size == 1 -> {
- WildcardTypeName.producerOf(resolve(typeName.outTypes[0]))
- .copy(nullable = typeName.isNullable)
- }
- else -> {
- throw IllegalArgumentException(
- "Unrepresentable wildcard type. Cannot have more than one bound: $typeName")
- }
- }
- }
-
- is TypeVariableName -> resolveTypeVariable(typeName)
-
- else -> throw IllegalArgumentException("Unrepresentable type: $typeName")
- }
- }
-}
diff --git a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/metadata.kt b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/metadata.kt
index 6d87af5..ef1b832 100644
--- a/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/metadata.kt
+++ b/kotlin/codegen/src/main/java/com/squareup/moshi/kotlin/codegen/metadata.kt
@@ -15,26 +15,23 @@
*/
package com.squareup.moshi.kotlin.codegen
-import com.google.auto.common.AnnotationMirrors
-import com.google.auto.common.MoreTypes
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.ClassName
-import com.squareup.kotlinpoet.CodeBlock
-import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
-import com.squareup.kotlinpoet.KModifier.PUBLIC
-import com.squareup.kotlinpoet.KModifier.VARARG
-import com.squareup.kotlinpoet.ParameterSpec
-import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
-import com.squareup.kotlinpoet.PropertySpec
-import com.squareup.kotlinpoet.STAR
-import com.squareup.kotlinpoet.TypeName
-import com.squareup.kotlinpoet.TypeVariableName
-import com.squareup.kotlinpoet.WildcardTypeName
+import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asClassName
import com.squareup.kotlinpoet.asTypeName
-import com.squareup.kotlinpoet.asTypeVariableName
-import com.squareup.kotlinpoet.tag
+import com.squareup.kotlinpoet.classinspector.elements.ElementsClassInspector
+import com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview
+import com.squareup.kotlinpoet.metadata.isAbstract
+import com.squareup.kotlinpoet.metadata.isClass
+import com.squareup.kotlinpoet.metadata.isEnum
+import com.squareup.kotlinpoet.metadata.isInner
+import com.squareup.kotlinpoet.metadata.isLocal
+import com.squareup.kotlinpoet.metadata.isSealed
+import com.squareup.kotlinpoet.metadata.specs.ClassInspector
+import com.squareup.kotlinpoet.metadata.specs.toTypeSpec
+import com.squareup.kotlinpoet.metadata.toImmutableKmClass
import com.squareup.moshi.Json
import com.squareup.moshi.JsonQualifier
import com.squareup.moshi.kotlin.codegen.api.DelegateKey
@@ -43,264 +40,125 @@ import com.squareup.moshi.kotlin.codegen.api.TargetConstructor
import com.squareup.moshi.kotlin.codegen.api.TargetParameter
import com.squareup.moshi.kotlin.codegen.api.TargetProperty
import com.squareup.moshi.kotlin.codegen.api.TargetType
-import com.squareup.moshi.kotlin.codegen.api.TypeResolver
-import me.eugeniomarletti.kotlin.metadata.KotlinClassMetadata
-import me.eugeniomarletti.kotlin.metadata.KotlinMetadata
-import me.eugeniomarletti.kotlin.metadata.classKind
-import me.eugeniomarletti.kotlin.metadata.declaresDefaultValue
-import me.eugeniomarletti.kotlin.metadata.getPropertyOrNull
-import me.eugeniomarletti.kotlin.metadata.isDataClass
-import me.eugeniomarletti.kotlin.metadata.isInnerClass
-import me.eugeniomarletti.kotlin.metadata.isPrimary
-import me.eugeniomarletti.kotlin.metadata.jvm.getJvmConstructorSignature
-import me.eugeniomarletti.kotlin.metadata.kotlinMetadata
-import me.eugeniomarletti.kotlin.metadata.modality
-import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Class
-import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Modality.ABSTRACT
-import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Modality.SEALED
-import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Type
-import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.TypeParameter
-import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.TypeParameter.Variance
-import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Visibility
-import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Visibility.INTERNAL
-import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Visibility.LOCAL
-import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Visibility.PRIVATE
-import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Visibility.PRIVATE_TO_THIS
-import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Visibility.PROTECTED
-import me.eugeniomarletti.kotlin.metadata.shadow.metadata.deserialization.NameResolver
-import me.eugeniomarletti.kotlin.metadata.shadow.util.capitalizeDecapitalize.decapitalizeAsciiOnly
-import me.eugeniomarletti.kotlin.metadata.visibility
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
import javax.annotation.processing.Messager
-import javax.lang.model.element.AnnotationMirror
-import javax.lang.model.element.Element
import javax.lang.model.element.ElementKind
-import javax.lang.model.element.ExecutableElement
-import javax.lang.model.element.Modifier
-import javax.lang.model.element.Modifier.DEFAULT
-import javax.lang.model.element.Modifier.FINAL
-import javax.lang.model.element.Modifier.NATIVE
-import javax.lang.model.element.Modifier.STATIC
-import javax.lang.model.element.Modifier.SYNCHRONIZED
-import javax.lang.model.element.Modifier.TRANSIENT
-import javax.lang.model.element.Modifier.VOLATILE
import javax.lang.model.element.TypeElement
-import javax.lang.model.element.VariableElement
-import javax.lang.model.type.TypeVariable
import javax.lang.model.util.Elements
import javax.lang.model.util.Types
import javax.tools.Diagnostic
-import javax.tools.Diagnostic.Kind.ERROR
-import kotlin.reflect.KClass
-private fun TypeParameter.asTypeName(
- nameResolver: NameResolver,
- getTypeParameter: (index: Int) -> TypeParameter,
- resolveAliases: Boolean = false
-): TypeVariableName {
- val possibleBounds = upperBoundList.map {
- it.asTypeName(nameResolver, getTypeParameter, resolveAliases)
- }
- return if (possibleBounds.isEmpty()) {
- TypeVariableName(
- name = nameResolver.getString(name),
- variance = variance.asKModifier())
- } else {
- TypeVariableName(
- name = nameResolver.getString(name),
- bounds = *possibleBounds.toTypedArray(),
- variance = variance.asKModifier())
- }
+private val JSON_QUALIFIER = JsonQualifier::class.java
+private val JSON = Json::class.asClassName()
+private val OBJECT_CLASS = ClassName("java.lang", "Object")
+private val VISIBILITY_MODIFIERS = setOf(
+ KModifier.INTERNAL,
+ KModifier.PRIVATE,
+ KModifier.PROTECTED,
+ KModifier.PUBLIC
+)
+
+private fun Collection.visibility(): KModifier {
+ return find { it in VISIBILITY_MODIFIERS } ?: KModifier.PUBLIC
}
-private fun TypeParameter.Variance.asKModifier(): KModifier? {
- return when (this) {
- Variance.IN -> KModifier.IN
- Variance.OUT -> KModifier.OUT
- Variance.INV -> null
- }
-}
-
-private fun Visibility?.asKModifier(): KModifier {
- return when (this) {
- INTERNAL -> KModifier.INTERNAL
- PRIVATE -> KModifier.PRIVATE
- PROTECTED -> KModifier.PROTECTED
- Visibility.PUBLIC -> KModifier.PUBLIC
- PRIVATE_TO_THIS -> KModifier.PRIVATE
- LOCAL -> KModifier.PRIVATE
- else -> PUBLIC
- }
-}
-
-/**
- * Returns the TypeName of this type as it would be seen in the source code, including nullability
- * and generic type parameters.
- *
- * @param [nameResolver] a [NameResolver] instance from the source proto
- * @param [getTypeParameter] a function that returns the type parameter for the given index. **Only
- * called if [ProtoBuf.Type.hasTypeParameter] is true!**
- */
-private fun Type.asTypeName(
- nameResolver: NameResolver,
- getTypeParameter: (index: Int) -> TypeParameter,
- useAbbreviatedType: Boolean = true
-): TypeName {
-
- val argumentList = when {
- useAbbreviatedType && hasAbbreviatedType() -> abbreviatedType.argumentList
- else -> argumentList
- }
-
- if (hasFlexibleUpperBound()) {
- return WildcardTypeName.producerOf(
- flexibleUpperBound.asTypeName(nameResolver, getTypeParameter, useAbbreviatedType))
- .copy(nullable = nullable)
- } else if (hasOuterType()) {
- return WildcardTypeName.consumerOf(
- outerType.asTypeName(nameResolver, getTypeParameter, useAbbreviatedType))
- .copy(nullable = nullable)
- }
-
- val realType = when {
- hasTypeParameter() -> return getTypeParameter(typeParameter)
- .asTypeName(nameResolver, getTypeParameter, useAbbreviatedType)
- .copy(nullable = nullable)
- hasTypeParameterName() -> typeParameterName
- useAbbreviatedType && hasAbbreviatedType() -> abbreviatedType.typeAliasName
- else -> className
- }
-
- var typeName: TypeName =
- ClassName.bestGuess(nameResolver.getString(realType)
- .replace("/", "."))
-
- if (argumentList.isNotEmpty()) {
- val remappedArgs: Array = argumentList.map { argumentType ->
- val nullableProjection = if (argumentType.hasProjection()) {
- argumentType.projection
- } else null
- if (argumentType.hasType()) {
- argumentType.type.asTypeName(nameResolver, getTypeParameter, useAbbreviatedType)
- .let { argumentTypeName ->
- nullableProjection?.let { projection ->
- when (projection) {
- Type.Argument.Projection.IN -> WildcardTypeName.consumerOf(argumentTypeName)
- Type.Argument.Projection.OUT -> WildcardTypeName.producerOf(argumentTypeName)
- Type.Argument.Projection.STAR -> STAR
- Type.Argument.Projection.INV -> TODO("INV projection is unsupported")
- }
- } ?: argumentTypeName
- }
- } else {
- STAR
- }
- }.toTypedArray()
- typeName = (typeName as ClassName).parameterizedBy(*remappedArgs)
- }
-
- return typeName.copy(nullable = nullable)
-}
-
-internal fun primaryConstructor(metadata: KotlinClassMetadata,
- elements: Elements): TargetConstructor {
- val (nameResolver, classProto) = metadata.data
-
- // TODO allow custom constructor
- val proto = classProto.constructorList
- .single { it.isPrimary }
- val constructorJvmSignature = proto.getJvmConstructorSignature(
- nameResolver, classProto.typeTable)
- val element = classProto.fqName
- .let(nameResolver::getString)
- .replace('/', '.')
- .let(elements::getTypeElement)
- .enclosedElements
- .mapNotNull {
- it.takeIf { it.kind == ElementKind.CONSTRUCTOR }?.let { it as ExecutableElement }
- }
- .first()
- // TODO Temporary until JVM method signature matching is better
- // .single { it.jvmMethodSignature == constructorJvmSignature }
+@KotlinPoetMetadataPreview
+internal fun primaryConstructor(kotlinApi: TypeSpec, elements: Elements): TargetConstructor? {
+ val primaryConstructor = kotlinApi.primaryConstructor ?: return null
val parameters = mutableMapOf()
- for (parameter in proto.valueParameterList) {
- val name = nameResolver.getString(parameter.name)
- val index = proto.valueParameterList.indexOf(parameter)
- val paramElement = element.parameters[index]
+ for ((index, parameter) in primaryConstructor.parameters.withIndex()) {
+ val name = parameter.name
parameters[name] = TargetParameter(
name = name,
index = index,
- hasDefault = parameter.declaresDefaultValue,
- qualifiers = paramElement.qualifiers.mapTo(mutableSetOf(), AnnotationMirror::asAnnotationSpec),
- jsonName = paramElement.jsonName ?: name,
- tags = mapOf(VariableElement::class to paramElement)
+ hasDefault = parameter.defaultValue != null,
+ qualifiers = parameter.annotations.qualifiers(elements),
+ jsonName = parameter.annotations.jsonName() ?: name.escapeDollarSigns()
)
}
- return TargetConstructor(parameters,
- proto.visibility.asKModifier())
+ return TargetConstructor(parameters, primaryConstructor.modifiers.visibility())
}
-private val OBJECT_CLASS = ClassName("java.lang", "Object")
-
/** Returns a target type for `element`, or null if it cannot be used with code gen. */
+@KotlinPoetMetadataPreview
internal fun targetType(messager: Messager,
elements: Elements,
types: Types,
- element: Element): TargetType? {
- val typeMetadata: KotlinMetadata? = element.kotlinMetadata
- if (element !is TypeElement || typeMetadata !is KotlinClassMetadata) {
+ element: TypeElement): TargetType? {
+ val typeMetadata = element.getAnnotation(Metadata::class.java)
+ if (typeMetadata == null) {
messager.printMessage(
- ERROR, "@JsonClass can't be applied to $element: must be a Kotlin class", element)
+ Diagnostic.Kind.ERROR, "@JsonClass can't be applied to $element: must be a Kotlin class",
+ element)
+ return null
+ }
+
+ val kmClass = try {
+ typeMetadata.toImmutableKmClass()
+ } catch (e: UnsupportedOperationException) {
+ messager.printMessage(
+ Diagnostic.Kind.ERROR, "@JsonClass can't be applied to $element: must be a Class type",
+ element)
return null
}
- val proto = typeMetadata.data.classProto
when {
- proto.classKind == Class.Kind.ENUM_CLASS -> {
+ kmClass.isEnum -> {
messager.printMessage(
- ERROR,
+ Diagnostic.Kind.ERROR,
"@JsonClass with 'generateAdapter = \"true\"' can't be applied to $element: code gen for enums is not supported or necessary",
element)
return null
}
- proto.classKind != Class.Kind.CLASS -> {
+ !kmClass.isClass -> {
messager.printMessage(
- ERROR, "@JsonClass can't be applied to $element: must be a Kotlin class", element)
+ Diagnostic.Kind.ERROR, "@JsonClass can't be applied to $element: must be a Kotlin class",
+ element)
return null
}
- proto.isInnerClass -> {
+ kmClass.isInner -> {
messager.printMessage(
- ERROR, "@JsonClass can't be applied to $element: must not be an inner class", element)
+ Diagnostic.Kind.ERROR,
+ "@JsonClass can't be applied to $element: must not be an inner class", element)
return null
}
- proto.modality == SEALED -> {
+ kmClass.isSealed -> {
messager.printMessage(
- ERROR, "@JsonClass can't be applied to $element: must not be sealed", element)
+ Diagnostic.Kind.ERROR, "@JsonClass can't be applied to $element: must not be sealed", element)
return null
}
- proto.modality == ABSTRACT -> {
+ kmClass.isAbstract -> {
messager.printMessage(
- ERROR, "@JsonClass can't be applied to $element: must not be abstract", element)
+ Diagnostic.Kind.ERROR, "@JsonClass can't be applied to $element: must not be abstract",
+ element)
return null
}
- proto.visibility == LOCAL -> {
+ kmClass.isLocal -> {
messager.printMessage(
- ERROR, "@JsonClass can't be applied to $element: must not be local", element)
+ Diagnostic.Kind.ERROR, "@JsonClass can't be applied to $element: must not be local",
+ element)
return null
}
}
- val typeVariables = genericTypeNames(proto, typeMetadata.data.nameResolver)
+ val elementHandler = ElementsClassInspector.create(elements, types)
+ val kotlinApi = kmClass.toTypeSpec(elementHandler)
+ val typeVariables = kotlinApi.typeVariables
val appliedType = AppliedType.get(element)
- val constructor = primaryConstructor(typeMetadata, elements)
+ val constructor = primaryConstructor(kotlinApi, elements)
+ if (constructor == null) {
+ messager.printMessage(Diagnostic.Kind.ERROR, "No primary constructor found on $element",
+ element)
+ return null
+ }
if (constructor.visibility != KModifier.INTERNAL && constructor.visibility != KModifier.PUBLIC) {
- messager.printMessage(ERROR, "@JsonClass can't be applied to $element: " +
+ messager.printMessage(Diagnostic.Kind.ERROR, "@JsonClass can't be applied to $element: " +
"primary constructor is not internal or public", element)
return null
}
@@ -313,14 +171,19 @@ internal fun targetType(messager: Messager,
if (supertype.element.kind != ElementKind.CLASS) {
continue // Don't load properties for interface types.
}
- if (supertype.element.kotlinMetadata == null) {
- messager.printMessage(ERROR,
+ if (supertype.element.getAnnotation(Metadata::class.java) == null) {
+ messager.printMessage(Diagnostic.Kind.ERROR,
"@JsonClass can't be applied to $element: supertype $supertype is not a Kotlin type",
element)
return null
}
- val supertypeProperties = declaredProperties(
- supertype.element, supertype.resolver, constructor)
+ val supertypeProperties = if (supertype.element == element) {
+ // We've already parsed this api above, reuse it
+ declaredProperties(supertype.element, constructor, elementHandler, kotlinApi)
+ } else {
+ declaredProperties(
+ supertype.element, constructor, elementHandler)
+ }
for ((name, property) in supertypeProperties) {
properties.putIfAbsent(name, property)
}
@@ -330,224 +193,37 @@ internal fun targetType(messager: Messager,
constructor = constructor,
properties = properties,
typeVariables = typeVariables,
- isDataClass = proto.isDataClass,
- visibility = proto.visibility.asKModifier())
+ isDataClass = KModifier.DATA in kotlinApi.modifiers,
+ visibility = kotlinApi.modifiers.visibility())
}
/** Returns the properties declared by `typeElement`. */
+@KotlinPoetMetadataPreview
private fun declaredProperties(
typeElement: TypeElement,
- typeResolver: TypeResolver,
- constructor: TargetConstructor
+ constructor: TargetConstructor,
+ elementHandler: ClassInspector,
+ kotlinApi: TypeSpec = typeElement.toTypeSpec(elementHandler)
): Map {
- val typeMetadata: KotlinClassMetadata = typeElement.kotlinMetadata as KotlinClassMetadata
- val nameResolver = typeMetadata.data.nameResolver
- val classProto = typeMetadata.data.classProto
-
- val annotationHolders = mutableMapOf()
- val fields = mutableMapOf()
- val setters = mutableMapOf()
- val getters = mutableMapOf()
- for (element in typeElement.enclosedElements) {
- if (element is VariableElement) {
- fields[element.name] = element
- } else if (element is ExecutableElement) {
- when {
- element.name.startsWith("get") -> {
- val name = element.name.substring("get".length).decapitalizeAsciiOnly()
- getters[name] = element
- }
- element.name.startsWith("is") -> {
- val name = element.name.substring("is".length).decapitalizeAsciiOnly()
- getters[name] = element
- }
- element.name.startsWith("set") -> {
- val name = element.name.substring("set".length).decapitalizeAsciiOnly()
- setters[name] = element
- }
- }
-
- val propertyProto = typeMetadata.data.getPropertyOrNull(element)
- if (propertyProto != null) {
- val name = nameResolver.getString(propertyProto.name)
- annotationHolders[name] = element
- }
- }
- }
val result = mutableMapOf()
- for (property in classProto.propertyList) {
- val name = nameResolver.getString(property.name)
- val type = typeResolver.resolve(property.returnType.asTypeName(
- nameResolver, classProto::getTypeParameter, false))
+ for (property in kotlinApi.propertySpecs) {
+ val name = property.name
val parameter = constructor.parameters[name]
- val fieldElement = fields[name]
- val annotationHolder = annotationHolders[name]
- // Used for setter/getter/is lookups. Guaranteed to be safe because kotlin doesn't allow you to
- // have both "AAA" and "aAA".
- val decapitalizedName = name.decapitalizeAsciiOnly()
- result[name] = TargetProperty(name = name,
- type = type,
+ result[name] = TargetProperty(
+ propertySpec = property,
parameter = parameter,
- annotationHolder = annotationHolder?.asFunSpec(),
- field = fieldElement?.asPropertySpec(),
- setter = setters[decapitalizedName]?.asFunSpec(),
- getter = getters[decapitalizedName]?.asFunSpec(),
- visibility = property.visibility.asKModifier(),
- jsonName = jsonName(
- fieldElement = fieldElement,
- parameter = parameter,
- annotationHolder = annotationHolder,
- name = name
- )
+ visibility = property.modifiers.visibility(),
+ jsonName = parameter?.jsonName ?: property.annotations.jsonName()
+ ?: name.escapeDollarSigns()
)
}
return result
}
-/** Returns the @Json name of this property, or this property's name if none is provided. */
-private fun jsonName(
- fieldElement: Element?,
- parameter: TargetParameter?,
- annotationHolder: ExecutableElement?,
- name: String): String {
- val fieldJsonName = fieldElement.jsonName
- val annotationHolderJsonName = annotationHolder.jsonName
- val parameterJsonName = parameter?.jsonName
-
- return when {
- fieldJsonName != null -> fieldJsonName
- annotationHolderJsonName != null -> annotationHolderJsonName
- parameterJsonName != null -> parameterJsonName
- else -> name
- }
-}
-
-private val Element.name get() = simpleName.toString()
-
-private fun genericTypeNames(proto: Class, nameResolver: NameResolver): List {
- return proto.typeParameterList.map { typeParameter ->
- val possibleBounds = typeParameter.upperBoundList
- .map { it.asTypeName(nameResolver, proto::getTypeParameter, false) }
- val typeVar = if (possibleBounds.isEmpty()) {
- TypeVariableName(
- name = nameResolver.getString(typeParameter.name),
- variance = typeParameter.varianceModifier)
- } else {
- TypeVariableName(
- name = nameResolver.getString(typeParameter.name),
- bounds = *possibleBounds.toTypedArray(),
- variance = typeParameter.varianceModifier)
- }
- return@map typeVar.copy(reified = typeParameter.reified)
- }
-}
-
-private val TypeParameter.varianceModifier: KModifier?
- get() {
- return variance.asKModifier().let {
- // We don't redeclare out variance here
- if (it == KModifier.OUT) {
- null
- } else {
- it
- }
- }
- }
-
-/**
- * Returns a new [PropertySpec] representation of [this].
- *
- * This will copy its name, type, visibility modifiers, constant value, and annotations. Note that
- * Java modifiers that correspond to annotations in kotlin will be added as well (`volatile`,
- * `transient`, etc`.
- *
- * The original `field` ([this]) is stored in [PropertySpec.tag].
- */
-internal fun VariableElement.asPropertySpec(asJvmField: Boolean = false): PropertySpec {
- require(kind == ElementKind.FIELD) {
- "Must be a field!"
- }
- val modifiers: Set = modifiers
- val fieldName = simpleName.toString()
- val propertyBuilder = PropertySpec.builder(fieldName, asType().asTypeName())
- propertyBuilder.addModifiers(*modifiers.mapNotNull { it.asKModifier() }.toTypedArray())
- constantValue?.let {
- if (it is String) {
- propertyBuilder.initializer(CodeBlock.of("%S", it))
- } else {
- propertyBuilder.initializer(CodeBlock.of("%L", it))
- }
- }
- propertyBuilder.addAnnotations(annotationMirrors.map(AnnotationMirror::asAnnotationSpec))
- propertyBuilder.addAnnotations(modifiers.mapNotNull { it.asAnnotation() })
- propertyBuilder.tag(this)
- if (asJvmField && KModifier.PRIVATE !in propertyBuilder.modifiers) {
- propertyBuilder.addAnnotation(JvmField::class)
- }
- return propertyBuilder.build()
-}
-
-/**
- * Returns a new [AnnotationSpec] representation of [this].
- *
- * Identical and delegates to [AnnotationSpec.get], but the original `mirror` is also stored
- * in [AnnotationSpec.tag].
- */
-internal fun AnnotationMirror.asAnnotationSpec(): AnnotationSpec {
- return AnnotationSpec.get(this)
- .toBuilder()
- .tag(MoreTypes.asTypeElement(annotationType))
- .build()
-}
-
-/**
- * Returns a new [FunSpec] representation of [this].
- *
- * This will copy its visibility modifiers, type parameters, return type, name, parameters, and
- * throws declarations.
- *
- * The original `method` ([this]) is stored in [FunSpec.tag].
- *
- * Nearly identical to [FunSpec.overriding], but no override modifier is added nor are checks around
- * overridability done
- */
-internal fun ExecutableElement.asFunSpec(): FunSpec {
- var modifiers: Set = modifiers
- val methodName = simpleName.toString()
- val funBuilder = FunSpec.builder(methodName)
-
- modifiers = modifiers.toMutableSet()
- funBuilder.jvmModifiers(modifiers)
-
- typeParameters
- .map { it.asType() as TypeVariable }
- .map { it.asTypeVariableName() }
- .forEach { funBuilder.addTypeVariable(it) }
-
- funBuilder.returns(returnType.asTypeName())
- funBuilder.addParameters(ParameterSpec.parametersOf(this))
- if (isVarArgs) {
- funBuilder.parameters[funBuilder.parameters.lastIndex] = funBuilder.parameters.last()
- .toBuilder()
- .addModifiers(VARARG)
- .build()
- }
-
- if (thrownTypes.isNotEmpty()) {
- val throwsValueString = thrownTypes.joinToString { "%T::class" }
- funBuilder.addAnnotation(AnnotationSpec.builder(Throws::class)
- .addMember(throwsValueString, *thrownTypes.toTypedArray())
- .build())
- }
-
- funBuilder.tag(this)
- return funBuilder.build()
-}
-
-private val TargetProperty.isTransient get() = field != null && field.annotations.any { it.className == Transient::class.asClassName() }
-private val TargetProperty.isSettable get() = setter != null || parameter != null
+private val TargetProperty.isTransient get() = propertySpec.annotations.any { it.className == Transient::class.asClassName() }
+private val TargetProperty.isSettable get() = propertySpec.mutable || parameter != null
private val TargetProperty.isVisible: Boolean
get() {
return visibility == KModifier.INTERNAL
@@ -559,26 +235,24 @@ private val TargetProperty.isVisible: Boolean
* Returns a generator for this property, or null if either there is an error and this property
* cannot be used with code gen, or if no codegen is necessary for this property.
*/
-internal fun TargetProperty.generator(messager: Messager): PropertyGenerator? {
- val element = field?.tag() ?: setter?.tag()
- ?: getter!!.tag()
+internal fun TargetProperty.generator(
+ messager: Messager,
+ sourceElement: TypeElement,
+ elements: Elements
+): PropertyGenerator? {
if (isTransient) {
if (!hasDefault) {
- element?.let {
- messager.printMessage(
- Diagnostic.Kind.ERROR, "No default value for transient property ${this}",
- it)
- }
+ messager.printMessage(
+ Diagnostic.Kind.ERROR, "No default value for transient property $name",
+ sourceElement)
return null
}
return PropertyGenerator(this, DelegateKey(type, emptyList()), true)
}
if (!isVisible) {
- element?.let {
- messager.printMessage(Diagnostic.Kind.ERROR, "property ${this} is not visible",
- it)
- }
+ messager.printMessage(Diagnostic.Kind.ERROR, "property $name is not visible",
+ sourceElement)
return null
}
@@ -586,10 +260,12 @@ internal fun TargetProperty.generator(messager: Messager): PropertyGenerator? {
return null // This property is not settable. Ignore it.
}
- val jsonQualifierMirrors = jsonQualifiers(element)
- for (jsonQualifier in jsonQualifierMirrors) {
+ // Merge parameter and property annotations
+ val qualifiers = parameter?.qualifiers.orEmpty() + propertySpec.annotations.qualifiers(elements)
+ for (jsonQualifier in qualifiers) {
// Check Java types since that covers both Java and Kotlin annotations.
- val annotationElement = jsonQualifier.tag() ?: continue
+ val annotationElement = elements.getTypeElement(jsonQualifier.className.canonicalName)
+ ?: continue
annotationElement.getAnnotation(Retention::class.java)?.let {
if (it.value != RetentionPolicy.RUNTIME) {
messager.printMessage(Diagnostic.Kind.ERROR,
@@ -604,7 +280,7 @@ internal fun TargetProperty.generator(messager: Messager): PropertyGenerator? {
}
}
- val jsonQualifierSpecs = jsonQualifierMirrors.map {
+ val jsonQualifierSpecs = qualifiers.map {
it.toBuilder()
.useSiteTarget(AnnotationSpec.UseSiteTarget.FIELD)
.build()
@@ -614,57 +290,21 @@ internal fun TargetProperty.generator(messager: Messager): PropertyGenerator? {
DelegateKey(type, jsonQualifierSpecs))
}
-/** Returns the JsonQualifiers on the field and parameter of this property. */
-private fun TargetProperty.jsonQualifiers(element: Element?): Set {
- val elementQualifiers = element.qualifiers
- val annotationHolderQualifiers = annotationHolder?.tag().qualifiers
- val parameterQualifiers = parameter?.qualifiers.orEmpty()
-
- // TODO(jwilson): union the qualifiers somehow?
- return when {
- elementQualifiers.isNotEmpty() -> elementQualifiers.mapTo(mutableSetOf(), AnnotationMirror::asAnnotationSpec)
- annotationHolderQualifiers.isNotEmpty() -> annotationHolderQualifiers.mapTo(
- mutableSetOf(), AnnotationMirror::asAnnotationSpec)
- parameterQualifiers.isNotEmpty() -> parameterQualifiers
- else -> setOf()
+private fun List?.qualifiers(elements: Elements): Set {
+ if (this == null) return setOf()
+ return filterTo(mutableSetOf()) {
+ elements.getTypeElement(it.className.toString()).getAnnotation(JSON_QUALIFIER) != null
}
}
-private fun Modifier.asKModifier(): KModifier? {
- return when (this) {
- Modifier.PUBLIC -> KModifier.PUBLIC
- Modifier.PROTECTED -> KModifier.PROTECTED
- Modifier.PRIVATE -> KModifier.PRIVATE
- Modifier.ABSTRACT -> KModifier.ABSTRACT
- FINAL -> KModifier.FINAL
- else -> null
+/** Gross, but we can't extract values from AnnotationSpecs by member names alone. */
+private fun List?.jsonName(): String? {
+ if (this == null) return null
+ return find { it.className == JSON }?.let {
+ it.members[0].toString().removePrefix("name = \"").removeSuffix("\"")
}
}
-private fun Modifier.asAnnotation(): AnnotationSpec? {
- return when (this) {
- DEFAULT -> JvmDefault::class.asAnnotationSpec()
- STATIC -> JvmStatic::class.asAnnotationSpec()
- TRANSIENT -> Transient::class.asAnnotationSpec()
- VOLATILE -> Volatile::class.asAnnotationSpec()
- SYNCHRONIZED -> Synchronized::class.asAnnotationSpec()
- NATIVE -> JvmDefault::class.asAnnotationSpec()
- else -> null
- }
+private fun String.escapeDollarSigns(): String {
+ return replace("\$", "\${\'\$\'}")
}
-
-private fun KClass.asAnnotationSpec(): AnnotationSpec {
- return AnnotationSpec.builder(this).build()
-}
-
-private val Element?.qualifiers: Set
- get() {
- if (this == null) return setOf()
- return AnnotationMirrors.getAnnotatedAnnotations(this, JsonQualifier::class.java)
- }
-
-private val Element?.jsonName: String?
- get() {
- if (this == null) return null
- return getAnnotation(Json::class.java)?.name
- }
diff --git a/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessorTest.kt b/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessorTest.kt
index f60200c..cb21606 100644
--- a/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessorTest.kt
+++ b/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/JsonClassCodegenProcessorTest.kt
@@ -18,6 +18,7 @@ package com.squareup.moshi.kotlin.codegen
import com.tschuchort.compiletesting.KotlinCompilation
import com.tschuchort.compiletesting.SourceFile
import com.tschuchort.compiletesting.SourceFile.Companion.kotlin
+import com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview
import org.assertj.core.api.Assertions.assertThat
import org.junit.Ignore
import org.junit.Rule
@@ -25,6 +26,7 @@ import org.junit.Test
import org.junit.rules.TemporaryFolder
/** Execute kotlinc to confirm that either files are generated or errors are printed. */
+@UseExperimental(KotlinPoetMetadataPreview::class)
class JsonClassCodegenProcessorTest {
@Rule @JvmField var temporaryFolder: TemporaryFolder = TemporaryFolder()
diff --git a/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/TypeResolverTest.kt b/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/TypeResolverTest.kt
deleted file mode 100644
index 8d1c339..0000000
--- a/kotlin/codegen/src/test/java/com/squareup/moshi/kotlin/codegen/TypeResolverTest.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2018 Square, Inc.
- *
- * 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 com.squareup.moshi.kotlin.codegen
-
-import com.google.common.truth.Truth.assertThat
-import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.plusParameter
-import com.squareup.kotlinpoet.WildcardTypeName
-import com.squareup.kotlinpoet.asClassName
-import com.squareup.moshi.kotlin.codegen.api.TypeResolver
-import org.junit.Test
-
-class TypeResolverTest {
- private val resolver = TypeResolver()
-
- @Test
- fun ensureClassNameNullabilityIsPreserved() {
- assertThat(resolver.resolve(Int::class.asClassName().copy(nullable = true)).isNullable).isTrue()
- }
-
- @Test
- fun ensureParameterizedNullabilityIsPreserved() {
- val nullableTypeName = List::class.plusParameter(String::class).copy(nullable = true)
-
- assertThat(resolver.resolve(nullableTypeName).isNullable).isTrue()
- }
-
- @Test
- fun ensureWildcardNullabilityIsPreserved() {
- val nullableTypeName = WildcardTypeName.producerOf(List::class.asClassName())
- .copy(nullable = true)
-
- assertThat(resolver.resolve(nullableTypeName).isNullable).isTrue()
- }
-}
diff --git a/pom.xml b/pom.xml
index 501a2e9..ba58ee3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,15 +31,13 @@
1.7
- 0.10
1.0-rc5
0.9.17
0.2
1.16.0
2.1.0
1.3.40
- 1.3.0
- 1.4.0
+ 1.4.0
3.1.0
@@ -118,11 +116,6 @@
kotlin-annotation-processing-embeddable
${kotlin.version}
-
- me.eugeniomarletti.kotlin.metadata
- kotlin-metadata
- ${kotlin-metadata.version}
-
com.google.testing.compile
compile-testing