Finish extraction of PropertyGenerator to no-elements/no-kotlin-metadata
This commit is contained in:
parent
be6c7c190c
commit
98c7d7e71c
7 changed files with 230 additions and 191 deletions
|
@ -30,6 +30,7 @@ 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
|
||||
|
||||
/**
|
||||
|
@ -111,7 +112,7 @@ 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.element // TODO how do we pass this? */)
|
||||
ERROR, "No property for required constructor parameter $name", parameter.tag<VariableElement>())
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,158 +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.auto.common.AnnotationMirrors
|
||||
import com.squareup.kotlinpoet.AnnotationSpec
|
||||
import com.squareup.kotlinpoet.KModifier
|
||||
import com.squareup.kotlinpoet.TypeName
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonQualifier
|
||||
import com.squareup.moshi.kotlin.codegen.api.DelegateKey
|
||||
import com.squareup.moshi.kotlin.codegen.api.PropertyGenerator
|
||||
import com.squareup.moshi.kotlin.codegen.api.TargetParameter
|
||||
import javax.annotation.processing.Messager
|
||||
import javax.lang.model.element.AnnotationMirror
|
||||
import javax.lang.model.element.Element
|
||||
import javax.lang.model.element.ExecutableElement
|
||||
import javax.lang.model.element.Modifier
|
||||
import javax.lang.model.element.VariableElement
|
||||
import javax.tools.Diagnostic
|
||||
|
||||
/** A property in user code that maps to JSON. */
|
||||
internal data class TargetProperty constructor(
|
||||
val name: String,
|
||||
val type: TypeName,
|
||||
private val parameter: TargetParameter?,
|
||||
private val annotationHolder: ExecutableElement?,
|
||||
private val field: VariableElement?,
|
||||
private val setter: ExecutableElement?,
|
||||
private val getter: ExecutableElement?,
|
||||
private val visibility: KModifier
|
||||
) {
|
||||
val parameterIndex get() = parameter?.index ?: -1
|
||||
val hasDefault get() = parameter?.hasDefault ?: true
|
||||
|
||||
private val isTransient get() = field != null && Modifier.TRANSIENT in field.modifiers
|
||||
|
||||
private val element get() = field ?: setter ?: getter!!
|
||||
|
||||
private val isSettable get() = setter != null || parameter != null
|
||||
|
||||
private val isVisible: Boolean
|
||||
get() {
|
||||
return visibility == KModifier.INTERNAL
|
||||
|| visibility == KModifier.PROTECTED
|
||||
|| visibility == KModifier.PUBLIC
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
fun generator(messager: Messager): PropertyGenerator? {
|
||||
if (isTransient) {
|
||||
if (!hasDefault) {
|
||||
messager.printMessage(
|
||||
Diagnostic.Kind.ERROR, "No default value for transient property ${this}", element)
|
||||
return null
|
||||
}
|
||||
return null // This property is transient and has a default value. Ignore it.
|
||||
}
|
||||
|
||||
if (!isVisible) {
|
||||
messager.printMessage(Diagnostic.Kind.ERROR, "property ${this} is not visible", element)
|
||||
return null
|
||||
}
|
||||
|
||||
if (!isSettable) {
|
||||
return null // This property is not settable. Ignore it.
|
||||
}
|
||||
|
||||
val jsonQualifierMirrors = jsonQualifiers()
|
||||
// TODO Can we check this after? Should we move this check to jsonQualifiers?
|
||||
// for (jsonQualifier in jsonQualifierMirrors) {
|
||||
// // Check Java types since that covers both Java and Kotlin annotations.
|
||||
// annotationElement.getAnnotation(Retention::class.java)?.let {
|
||||
// if (it.value != RetentionPolicy.RUNTIME) {
|
||||
// messager.printMessage(Diagnostic.Kind.ERROR,
|
||||
// "JsonQualifier @${jsonQualifier.simpleName} must have RUNTIME retention")
|
||||
// }
|
||||
// }
|
||||
// annotationElement.getAnnotation(Target::class.java)?.let {
|
||||
// if (ElementType.FIELD !in it.value) {
|
||||
// messager.printMessage(Diagnostic.Kind.ERROR,
|
||||
// "JsonQualifier @${jsonQualifier.simpleName} must support FIELD target")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
val jsonQualifierSpecs = jsonQualifierMirrors.map {
|
||||
it.toBuilder()
|
||||
.useSiteTarget(AnnotationSpec.UseSiteTarget.FIELD)
|
||||
.build()
|
||||
}
|
||||
|
||||
return PropertyGenerator(this,
|
||||
DelegateKey(type, jsonQualifierSpecs))
|
||||
}
|
||||
|
||||
/** Returns the JsonQualifiers on the field and parameter of this property. */
|
||||
private fun jsonQualifiers(): Set<AnnotationSpec> {
|
||||
val elementQualifiers = element.qualifiers
|
||||
val annotationHolderQualifiers = annotationHolder.qualifiers
|
||||
val parameterQualifiers = parameter?.qualifiers.orEmpty()
|
||||
|
||||
// TODO(jwilson): union the qualifiers somehow?
|
||||
return when {
|
||||
elementQualifiers.isNotEmpty() -> elementQualifiers.mapTo(mutableSetOf()) { AnnotationSpec.get(it) }
|
||||
annotationHolderQualifiers.isNotEmpty() -> annotationHolderQualifiers.mapTo(mutableSetOf()) { AnnotationSpec.get(it) }
|
||||
parameterQualifiers.isNotEmpty() -> parameterQualifiers
|
||||
else -> setOf()
|
||||
}
|
||||
}
|
||||
|
||||
private val Element?.qualifiers: Set<AnnotationMirror>
|
||||
get() {
|
||||
if (this == null) return setOf()
|
||||
return AnnotationMirrors.getAnnotatedAnnotations(this, JsonQualifier::class.java)
|
||||
}
|
||||
|
||||
/** Returns the @Json name of this property, or this property's name if none is provided. */
|
||||
fun jsonName(): String {
|
||||
val fieldJsonName = element.jsonName
|
||||
val annotationHolderJsonName = annotationHolder.jsonName
|
||||
val parameterJsonName = parameter?.jsonName
|
||||
|
||||
return when {
|
||||
fieldJsonName != null -> fieldJsonName
|
||||
annotationHolderJsonName != null -> annotationHolderJsonName
|
||||
parameterJsonName != null -> parameterJsonName
|
||||
else -> name
|
||||
}
|
||||
}
|
||||
|
||||
// private val AnnotationMirror.simpleName: Name
|
||||
// get() = MoreTypes.asTypeElement(annotationType).simpleName!!
|
||||
|
||||
override fun toString() = name
|
||||
}
|
||||
|
||||
internal val Element?.jsonName: String?
|
||||
get() {
|
||||
if (this == null) return null
|
||||
return getAnnotation(Json::class.java)?.name
|
||||
}
|
|
@ -18,7 +18,6 @@ package com.squareup.moshi.kotlin.codegen.api
|
|||
import com.squareup.kotlinpoet.BOOLEAN
|
||||
import com.squareup.kotlinpoet.NameAllocator
|
||||
import com.squareup.kotlinpoet.PropertySpec
|
||||
import com.squareup.moshi.kotlin.codegen.TargetProperty
|
||||
|
||||
/** Generates functions to encode and decode a property as JSON. */
|
||||
internal class PropertyGenerator(
|
||||
|
@ -26,7 +25,7 @@ internal class PropertyGenerator(
|
|||
val delegateKey: DelegateKey
|
||||
) {
|
||||
val name = target.name
|
||||
val jsonName = target.jsonName()
|
||||
val jsonName = target.jsonName
|
||||
val hasDefault = target.hasDefault
|
||||
|
||||
lateinit var localName: String
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
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(
|
||||
|
@ -23,5 +24,15 @@ internal data class TargetParameter(
|
|||
val index: Int,
|
||||
val hasDefault: Boolean,
|
||||
val jsonName: String = name,
|
||||
val qualifiers: Set<AnnotationSpec>? = null
|
||||
)
|
||||
val qualifiers: Set<AnnotationSpec>? = null,
|
||||
private val tags: Map<KClass<*>, Any>
|
||||
) {
|
||||
/** Returns the tag attached with [type] as a key, or null if no tag is attached with that key. */
|
||||
fun <T : Any> tag(type: KClass<out T>): 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 <reified T : Any> tag(): T? = tag(T::class)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.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 parameter: TargetParameter?,
|
||||
val annotationHolder: FunSpec?,
|
||||
val field: PropertySpec?,
|
||||
val setter: FunSpec?,
|
||||
val getter: FunSpec?,
|
||||
val visibility: KModifier,
|
||||
val jsonName: String
|
||||
) {
|
||||
val parameterIndex get() = parameter?.index ?: -1
|
||||
val hasDefault get() = parameter?.hasDefault ?: true
|
||||
|
||||
override fun toString() = name
|
||||
}
|
|
@ -18,7 +18,6 @@ package com.squareup.moshi.kotlin.codegen.api
|
|||
import com.squareup.kotlinpoet.KModifier
|
||||
import com.squareup.kotlinpoet.TypeName
|
||||
import com.squareup.kotlinpoet.TypeVariableName
|
||||
import com.squareup.moshi.kotlin.codegen.TargetProperty
|
||||
|
||||
/** A user type that should be decoded and encoded by generated code. */
|
||||
internal data class TargetType(
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package com.squareup.moshi.kotlin.codegen
|
||||
|
||||
import com.google.auto.common.AnnotationMirrors
|
||||
import com.squareup.kotlinpoet.AnnotationSpec
|
||||
import com.squareup.kotlinpoet.ClassName
|
||||
import com.squareup.kotlinpoet.KModifier
|
||||
import com.squareup.kotlinpoet.KModifier.PUBLIC
|
||||
|
@ -25,10 +27,18 @@ import com.squareup.kotlinpoet.TypeVariableName
|
|||
import com.squareup.kotlinpoet.WildcardTypeName
|
||||
import com.squareup.kotlinpoet.asClassName
|
||||
import com.squareup.kotlinpoet.asTypeName
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonQualifier
|
||||
import com.squareup.moshi.kotlin.codegen.api.DelegateKey
|
||||
import com.squareup.moshi.kotlin.codegen.api.PropertyGenerator
|
||||
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 com.squareup.moshi.kotlin.codegen.api.asAnnotationSpec
|
||||
import com.squareup.moshi.kotlin.codegen.api.asFunSpec
|
||||
import com.squareup.moshi.kotlin.codegen.api.asPropertySpec
|
||||
import me.eugeniomarletti.kotlin.metadata.KotlinClassMetadata
|
||||
import me.eugeniomarletti.kotlin.metadata.KotlinMetadata
|
||||
import me.eugeniomarletti.kotlin.metadata.classKind
|
||||
|
@ -54,7 +64,12 @@ import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf.Visibility.PR
|
|||
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
|
||||
|
@ -62,12 +77,13 @@ import javax.lang.model.element.TypeElement
|
|||
import javax.lang.model.element.VariableElement
|
||||
import javax.lang.model.util.Elements
|
||||
import javax.lang.model.util.Types
|
||||
import javax.tools.Diagnostic
|
||||
import javax.tools.Diagnostic.Kind.ERROR
|
||||
|
||||
internal fun TypeParameter.asTypeName(
|
||||
nameResolver: NameResolver,
|
||||
getTypeParameter: (index: Int) -> TypeParameter,
|
||||
resolveAliases: Boolean = false
|
||||
private fun TypeParameter.asTypeName(
|
||||
nameResolver: NameResolver,
|
||||
getTypeParameter: (index: Int) -> TypeParameter,
|
||||
resolveAliases: Boolean = false
|
||||
): TypeVariableName {
|
||||
val possibleBounds = upperBoundList.map {
|
||||
it.asTypeName(nameResolver, getTypeParameter, resolveAliases)
|
||||
|
@ -84,7 +100,7 @@ internal fun TypeParameter.asTypeName(
|
|||
}
|
||||
}
|
||||
|
||||
internal fun TypeParameter.Variance.asKModifier(): KModifier? {
|
||||
private fun TypeParameter.Variance.asKModifier(): KModifier? {
|
||||
return when (this) {
|
||||
Variance.IN -> KModifier.IN
|
||||
Variance.OUT -> KModifier.OUT
|
||||
|
@ -92,7 +108,7 @@ internal fun TypeParameter.Variance.asKModifier(): KModifier? {
|
|||
}
|
||||
}
|
||||
|
||||
internal fun Visibility?.asKModifier(): KModifier {
|
||||
private fun Visibility?.asKModifier(): KModifier {
|
||||
return when (this) {
|
||||
INTERNAL -> KModifier.INTERNAL
|
||||
PRIVATE -> KModifier.PRIVATE
|
||||
|
@ -112,10 +128,10 @@ internal fun Visibility?.asKModifier(): KModifier {
|
|||
* @param [getTypeParameter] a function that returns the type parameter for the given index. **Only
|
||||
* called if [ProtoBuf.Type.hasTypeParameter] is true!**
|
||||
*/
|
||||
internal fun Type.asTypeName(
|
||||
nameResolver: NameResolver,
|
||||
getTypeParameter: (index: Int) -> TypeParameter,
|
||||
useAbbreviatedType: Boolean = true
|
||||
private fun Type.asTypeName(
|
||||
nameResolver: NameResolver,
|
||||
getTypeParameter: (index: Int) -> TypeParameter,
|
||||
useAbbreviatedType: Boolean = true
|
||||
): TypeName {
|
||||
|
||||
val argumentList = when {
|
||||
|
@ -173,7 +189,8 @@ internal fun Type.asTypeName(
|
|||
return typeName.copy(nullable = nullable)
|
||||
}
|
||||
|
||||
internal fun primaryConstructor(metadata: KotlinClassMetadata, elements: Elements): TargetConstructor {
|
||||
internal fun primaryConstructor(metadata: KotlinClassMetadata,
|
||||
elements: Elements): TargetConstructor {
|
||||
val (nameResolver, classProto) = metadata.data
|
||||
|
||||
// TODO allow custom constructor
|
||||
|
@ -202,8 +219,9 @@ internal fun primaryConstructor(metadata: KotlinClassMetadata, elements: Element
|
|||
name = name,
|
||||
index = index,
|
||||
hasDefault = parameter.declaresDefaultValue,
|
||||
jsonName = paramElement.jsonName ?: name
|
||||
|
||||
qualifiers = paramElement.qualifiers.mapTo(mutableSetOf(), AnnotationMirror::asAnnotationSpec),
|
||||
jsonName = paramElement.jsonName ?: name,
|
||||
tags = mapOf(VariableElement::class to paramElement)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -214,7 +232,10 @@ internal fun primaryConstructor(metadata: KotlinClassMetadata, elements: Element
|
|||
private val OBJECT_CLASS = ClassName("java.lang", "Object")
|
||||
|
||||
/** Returns a target type for `element`, or null if it cannot be used with code gen. */
|
||||
internal fun targetType(messager: Messager, elements: Elements, types: Types, element: Element): TargetType? {
|
||||
internal fun targetType(messager: Messager,
|
||||
elements: Elements,
|
||||
types: Types,
|
||||
element: Element): TargetType? {
|
||||
val typeMetadata: KotlinMetadata? = element.kotlinMetadata
|
||||
if (element !is TypeElement || typeMetadata !is KotlinClassMetadata) {
|
||||
messager.printMessage(
|
||||
|
@ -226,7 +247,9 @@ internal fun targetType(messager: Messager, elements: Elements, types: Types, el
|
|||
when {
|
||||
proto.classKind == Class.Kind.ENUM_CLASS -> {
|
||||
messager.printMessage(
|
||||
ERROR, "@JsonClass with 'generateAdapter = \"true\"' can't be applied to $element: code gen for enums is not supported or necessary", element)
|
||||
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 -> {
|
||||
|
@ -337,37 +360,66 @@ private fun declaredProperties(
|
|||
val type = typeResolver.resolve(property.returnType.asTypeName(
|
||||
nameResolver, classProto::getTypeParameter, false))
|
||||
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,
|
||||
parameter = parameter,
|
||||
annotationHolder = annotationHolders[name],
|
||||
field = fields[name],
|
||||
setter = setters[name],
|
||||
getter = getters[name],
|
||||
visibility = property.visibility.asKModifier()
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
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<TypeVariableName> {
|
||||
return proto.typeParameterList.map {
|
||||
val possibleBounds = it.upperBoundList
|
||||
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(it.name),
|
||||
variance = it.varianceModifier)
|
||||
name = nameResolver.getString(typeParameter.name),
|
||||
variance = typeParameter.varianceModifier)
|
||||
} else {
|
||||
TypeVariableName(
|
||||
name = nameResolver.getString(it.name),
|
||||
name = nameResolver.getString(typeParameter.name),
|
||||
bounds = *possibleBounds.toTypedArray(),
|
||||
variance = it.varianceModifier)
|
||||
variance = typeParameter.varianceModifier)
|
||||
}
|
||||
return@map typeVar.copy(reified = it.reified)
|
||||
return@map typeVar.copy(reified = typeParameter.reified)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -382,3 +434,99 @@ private val TypeParameter.varianceModifier: KModifier?
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.isVisible: Boolean
|
||||
get() {
|
||||
return visibility == KModifier.INTERNAL
|
||||
|| visibility == KModifier.PROTECTED
|
||||
|| visibility == KModifier.PUBLIC
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<VariableElement>() ?: setter?.tag<ExecutableElement>()
|
||||
?: getter!!.tag<ExecutableElement>()
|
||||
if (isTransient) {
|
||||
if (!hasDefault) {
|
||||
element?.let {
|
||||
messager.printMessage(
|
||||
Diagnostic.Kind.ERROR, "No default value for transient property ${this}",
|
||||
it)
|
||||
}
|
||||
return null
|
||||
}
|
||||
return null // This property is transient and has a default value. Ignore it.
|
||||
}
|
||||
|
||||
if (!isVisible) {
|
||||
element?.let {
|
||||
messager.printMessage(Diagnostic.Kind.ERROR, "property ${this} is not visible",
|
||||
it)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
if (!isSettable) {
|
||||
return null // This property is not settable. Ignore it.
|
||||
}
|
||||
|
||||
val jsonQualifierMirrors = jsonQualifiers(element)
|
||||
for (jsonQualifier in jsonQualifierMirrors) {
|
||||
// Check Java types since that covers both Java and Kotlin annotations.
|
||||
val annotationElement = jsonQualifier.tag<TypeElement>() ?: continue
|
||||
annotationElement.getAnnotation(Retention::class.java)?.let {
|
||||
if (it.value != RetentionPolicy.RUNTIME) {
|
||||
messager.printMessage(Diagnostic.Kind.ERROR,
|
||||
"JsonQualifier @${jsonQualifier.className.simpleName} must have RUNTIME retention")
|
||||
}
|
||||
}
|
||||
annotationElement.getAnnotation(Target::class.java)?.let {
|
||||
if (ElementType.FIELD !in it.value) {
|
||||
messager.printMessage(Diagnostic.Kind.ERROR,
|
||||
"JsonQualifier @${jsonQualifier.className.simpleName} must support FIELD target")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val jsonQualifierSpecs = jsonQualifierMirrors.map {
|
||||
it.toBuilder()
|
||||
.useSiteTarget(AnnotationSpec.UseSiteTarget.FIELD)
|
||||
.build()
|
||||
}
|
||||
|
||||
return PropertyGenerator(this,
|
||||
DelegateKey(type, jsonQualifierSpecs))
|
||||
}
|
||||
|
||||
/** Returns the JsonQualifiers on the field and parameter of this property. */
|
||||
private fun TargetProperty.jsonQualifiers(element: Element?): Set<AnnotationSpec> {
|
||||
val elementQualifiers = element.qualifiers
|
||||
val annotationHolderQualifiers = annotationHolder?.tag<ExecutableElement>().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 val Element?.qualifiers: Set<AnnotationMirror>
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue