mirror of
https://github.com/fankes/moshi.git
synced 2025-10-20 00:19:21 +08:00
Begin to resolve supertype type parameters
This commit is contained in:
@@ -18,6 +18,7 @@ package com.squareup.moshi
|
|||||||
import com.squareup.kotlinpoet.ARRAY
|
import com.squareup.kotlinpoet.ARRAY
|
||||||
import com.squareup.kotlinpoet.AnnotationSpec
|
import com.squareup.kotlinpoet.AnnotationSpec
|
||||||
import com.squareup.kotlinpoet.ClassName
|
import com.squareup.kotlinpoet.ClassName
|
||||||
|
import com.squareup.kotlinpoet.CodeBlock
|
||||||
import com.squareup.kotlinpoet.FileSpec
|
import com.squareup.kotlinpoet.FileSpec
|
||||||
import com.squareup.kotlinpoet.FunSpec
|
import com.squareup.kotlinpoet.FunSpec
|
||||||
import com.squareup.kotlinpoet.KModifier
|
import com.squareup.kotlinpoet.KModifier
|
||||||
@@ -34,31 +35,28 @@ import me.eugeniomarletti.kotlin.metadata.visibility
|
|||||||
import org.jetbrains.kotlin.serialization.ProtoBuf.Visibility
|
import org.jetbrains.kotlin.serialization.ProtoBuf.Visibility
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
import javax.lang.model.element.TypeElement
|
import javax.lang.model.element.TypeElement
|
||||||
import javax.lang.model.util.Elements
|
|
||||||
|
|
||||||
/** Generates a JSON adapter for a target type. */
|
/** Generates a JSON adapter for a target type. */
|
||||||
internal class AdapterGenerator(
|
internal class AdapterGenerator(
|
||||||
target: TargetType,
|
target: TargetType,
|
||||||
private val propertyList: List<PropertyGenerator>,
|
private val propertyList: List<PropertyGenerator>
|
||||||
val elements: Elements
|
|
||||||
) {
|
) {
|
||||||
private val className = target.name
|
private val className = target.name
|
||||||
private val isDataClass = target.proto.isDataClass
|
private val isDataClass = target.proto.isDataClass
|
||||||
private val hasCompanionObject = target.hasCompanionObject
|
private val hasCompanionObject = target.hasCompanionObject
|
||||||
private val visibility = target.proto.visibility!!
|
private val visibility = target.proto.visibility!!
|
||||||
val genericTypeNames = target.genericTypeNames
|
private val typeVariables = target.typeVariables
|
||||||
|
|
||||||
private val nameAllocator = NameAllocator()
|
private val nameAllocator = NameAllocator()
|
||||||
private val adapterName = "${className.simpleNames().joinToString(separator = "_")}JsonAdapter"
|
private val adapterName = "${className.simpleNames().joinToString(separator = "_")}JsonAdapter"
|
||||||
private val originalTypeName = target.element.asType().asTypeName()
|
private val originalTypeName = target.element.asType().asTypeName()
|
||||||
|
|
||||||
val moshiParam = ParameterSpec.builder(
|
private val moshiParam = ParameterSpec.builder(
|
||||||
nameAllocator.newName("moshi"),
|
nameAllocator.newName("moshi"),
|
||||||
Moshi::class).build()
|
Moshi::class).build()
|
||||||
val typesParam = ParameterSpec.builder(
|
private val typesParam = ParameterSpec.builder(
|
||||||
nameAllocator.newName("types"),
|
nameAllocator.newName("types"),
|
||||||
ParameterizedTypeName.get(ARRAY,
|
ParameterizedTypeName.get(ARRAY, Type::class.asTypeName()))
|
||||||
Type::class.asTypeName()))
|
|
||||||
.build()
|
.build()
|
||||||
private val readerParam = ParameterSpec.builder(
|
private val readerParam = ParameterSpec.builder(
|
||||||
nameAllocator.newName("reader"),
|
nameAllocator.newName("reader"),
|
||||||
@@ -83,12 +81,7 @@ internal class AdapterGenerator(
|
|||||||
.joinToString(", ") { "\"$it\"" }})", JsonReader.Options::class.asTypeName())
|
.joinToString(", ") { "\"$it\"" }})", JsonReader.Options::class.asTypeName())
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private val delegateAdapters = propertyList.distinctBy { it.delegateKey }
|
|
||||||
|
|
||||||
fun generateFile(generatedOption: TypeElement?): FileSpec {
|
fun generateFile(generatedOption: TypeElement?): FileSpec {
|
||||||
for (property in delegateAdapters) {
|
|
||||||
property.delegateKey.reserveName(nameAllocator)
|
|
||||||
}
|
|
||||||
for (property in propertyList) {
|
for (property in propertyList) {
|
||||||
property.allocateNames(nameAllocator)
|
property.allocateNames(nameAllocator)
|
||||||
}
|
}
|
||||||
@@ -114,8 +107,8 @@ internal class AdapterGenerator(
|
|||||||
|
|
||||||
result.superclass(jsonAdapterTypeName)
|
result.superclass(jsonAdapterTypeName)
|
||||||
|
|
||||||
if (genericTypeNames.isNotEmpty()) {
|
if (typeVariables.isNotEmpty()) {
|
||||||
result.addTypeVariables(genericTypeNames)
|
result.addTypeVariables(typeVariables)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO make this configurable. Right now it just matches the source model
|
// TODO make this configurable. Right now it just matches the source model
|
||||||
@@ -125,9 +118,18 @@ internal class AdapterGenerator(
|
|||||||
|
|
||||||
result.primaryConstructor(generateConstructor())
|
result.primaryConstructor(generateConstructor())
|
||||||
|
|
||||||
|
val typeRenderer: TypeRenderer = object : TypeRenderer() {
|
||||||
|
override fun renderTypeVariable(typeVariable: TypeVariableName): CodeBlock {
|
||||||
|
val index = typeVariables.indexOfFirst { it == typeVariable }
|
||||||
|
check(index != -1) { "Unexpected type variable $typeVariable" }
|
||||||
|
return CodeBlock.of("%N[%L]", typesParam, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result.addProperty(optionsProperty)
|
result.addProperty(optionsProperty)
|
||||||
for (uniqueAdapter in delegateAdapters) {
|
for (uniqueAdapter in propertyList.distinctBy { it.delegateKey }) {
|
||||||
result.addProperty(uniqueAdapter.delegateKey.generateProperty(nameAllocator, this))
|
result.addProperty(uniqueAdapter.delegateKey.generateProperty(
|
||||||
|
nameAllocator, typeRenderer, moshiParam))
|
||||||
}
|
}
|
||||||
|
|
||||||
result.addFunction(generateToStringFun())
|
result.addFunction(generateToStringFun())
|
||||||
@@ -141,7 +143,7 @@ internal class AdapterGenerator(
|
|||||||
val result = FunSpec.constructorBuilder()
|
val result = FunSpec.constructorBuilder()
|
||||||
result.addParameter(moshiParam)
|
result.addParameter(moshiParam)
|
||||||
|
|
||||||
if (genericTypeNames.isNotEmpty()) {
|
if (typeVariables.isNotEmpty()) {
|
||||||
result.addParameter(typesParam)
|
result.addParameter(typesParam)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,9 +309,9 @@ internal class AdapterGenerator(
|
|||||||
result.addModifiers(KModifier.INTERNAL)
|
result.addModifiers(KModifier.INTERNAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (genericTypeNames.isNotEmpty()) {
|
if (typeVariables.isNotEmpty()) {
|
||||||
result.addParameter(typesParam)
|
result.addParameter(typesParam)
|
||||||
result.addTypeVariables(genericTypeNames)
|
result.addTypeVariables(typeVariables)
|
||||||
result.addStatement("return %N(%N, %N)", adapterName, moshiParam, typesParam)
|
result.addStatement("return %N(%N, %N)", adapterName, moshiParam, typesParam)
|
||||||
} else {
|
} else {
|
||||||
result.addStatement("return %N(%N)", adapterName, moshiParam)
|
result.addStatement("return %N(%N)", adapterName, moshiParam)
|
||||||
|
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import com.squareup.kotlinpoet.TypeName
|
||||||
|
import com.squareup.kotlinpoet.TypeVariableName
|
||||||
|
import com.squareup.kotlinpoet.asTypeName
|
||||||
|
import javax.lang.model.element.TypeElement
|
||||||
|
import javax.lang.model.type.DeclaredType
|
||||||
|
import javax.lang.model.util.Types
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A concrete type like `List<String>` with enough information to know how to resolve its type
|
||||||
|
* variables.
|
||||||
|
*/
|
||||||
|
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. */
|
||||||
|
fun supertypes(
|
||||||
|
types: Types,
|
||||||
|
result: MutableSet<AppliedType> = mutableSetOf()
|
||||||
|
): Set<AppliedType> {
|
||||||
|
result.add(this)
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -19,6 +19,7 @@ import com.squareup.kotlinpoet.ClassName
|
|||||||
import com.squareup.kotlinpoet.CodeBlock
|
import com.squareup.kotlinpoet.CodeBlock
|
||||||
import com.squareup.kotlinpoet.KModifier
|
import com.squareup.kotlinpoet.KModifier
|
||||||
import com.squareup.kotlinpoet.NameAllocator
|
import com.squareup.kotlinpoet.NameAllocator
|
||||||
|
import com.squareup.kotlinpoet.ParameterSpec
|
||||||
import com.squareup.kotlinpoet.ParameterizedTypeName
|
import com.squareup.kotlinpoet.ParameterizedTypeName
|
||||||
import com.squareup.kotlinpoet.PropertySpec
|
import com.squareup.kotlinpoet.PropertySpec
|
||||||
import com.squareup.kotlinpoet.TypeName
|
import com.squareup.kotlinpoet.TypeName
|
||||||
@@ -34,25 +35,27 @@ internal data class DelegateKey(
|
|||||||
) {
|
) {
|
||||||
val nullable get() = type.nullable || type is TypeVariableName
|
val nullable get() = type.nullable || type is TypeVariableName
|
||||||
|
|
||||||
fun reserveName(nameAllocator: NameAllocator) {
|
/** Returns an adapter to use when encoding and decoding this property. */
|
||||||
|
fun generateProperty(
|
||||||
|
nameAllocator: NameAllocator,
|
||||||
|
typeRenderer: TypeRenderer,
|
||||||
|
moshiParameter: ParameterSpec): PropertySpec {
|
||||||
val qualifierNames = jsonQualifiers.joinToString("") {
|
val qualifierNames = jsonQualifiers.joinToString("") {
|
||||||
"At${it.annotationType.asElement().simpleName}"
|
"At${it.annotationType.asElement().simpleName}"
|
||||||
}
|
}
|
||||||
nameAllocator.newName("${type.toVariableName().decapitalize()}${qualifierNames}Adapter", this)
|
val adapterName = nameAllocator.newName(
|
||||||
}
|
"${type.toVariableName().decapitalize()}${qualifierNames}Adapter", this)
|
||||||
|
|
||||||
/** Returns an adapter to use when encoding and decoding this property. */
|
|
||||||
fun generateProperty(nameAllocator: NameAllocator, enclosing: AdapterGenerator): PropertySpec {
|
|
||||||
val adapterTypeName = ParameterizedTypeName.get(
|
val adapterTypeName = ParameterizedTypeName.get(
|
||||||
JsonAdapter::class.asTypeName(), type)
|
JsonAdapter::class.asTypeName(), type)
|
||||||
val qualifiers = jsonQualifiers
|
val qualifiers = jsonQualifiers
|
||||||
val standardArgs = arrayOf(enclosing.moshiParam,
|
val standardArgs = arrayOf(moshiParameter,
|
||||||
if (type is ClassName && qualifiers.isEmpty()) {
|
if (type is ClassName && qualifiers.isEmpty()) {
|
||||||
""
|
""
|
||||||
} else {
|
} else {
|
||||||
CodeBlock.of("<%T>", type)
|
CodeBlock.of("<%T>", type)
|
||||||
},
|
},
|
||||||
type.makeType(enclosing.elements, enclosing.typesParam, enclosing.genericTypeNames))
|
typeRenderer.render(type))
|
||||||
val standardArgsSize = standardArgs.size + 1
|
val standardArgsSize = standardArgs.size + 1
|
||||||
val (initializerString, args) = when {
|
val (initializerString, args) = when {
|
||||||
qualifiers.isEmpty() -> "" to emptyArray()
|
qualifiers.isEmpty() -> "" to emptyArray()
|
||||||
@@ -77,7 +80,7 @@ internal data class DelegateKey(
|
|||||||
|
|
||||||
val nullModifier = if (nullable) ".nullSafe()" else ".nonNull()"
|
val nullModifier = if (nullable) ".nullSafe()" else ".nonNull()"
|
||||||
|
|
||||||
return PropertySpec.builder(nameAllocator.get(this), adapterTypeName, KModifier.PRIVATE)
|
return PropertySpec.builder(adapterName, adapterTypeName, KModifier.PRIVATE)
|
||||||
.initializer("%1N.adapter%2L(%3L$initializerString)$nullModifier", *finalArgs)
|
.initializer("%1N.adapter%2L(%3L$initializerString)$nullModifier", *finalArgs)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@@ -120,7 +120,7 @@ class JsonClassCodeGenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return AdapterGenerator(type, sortedProperties, elementUtils)
|
return AdapterGenerator(type, sortedProperties)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun AdapterGenerator.generateAndWrite(generatedOption: TypeElement?) {
|
private fun AdapterGenerator.generateAndWrite(generatedOption: TypeElement?) {
|
||||||
|
@@ -36,7 +36,6 @@ import javax.tools.Diagnostic
|
|||||||
internal data class TargetProperty(
|
internal data class TargetProperty(
|
||||||
val name: String,
|
val name: String,
|
||||||
val type: TypeName,
|
val type: TypeName,
|
||||||
private val typeWithResolvedAliases: TypeName,
|
|
||||||
private val proto: Property,
|
private val proto: Property,
|
||||||
private val parameter: TargetParameter?,
|
private val parameter: TargetParameter?,
|
||||||
private val annotationHolder: ExecutableElement?,
|
private val annotationHolder: ExecutableElement?,
|
||||||
@@ -87,7 +86,7 @@ internal data class TargetProperty(
|
|||||||
return PropertyGenerator(this)
|
return PropertyGenerator(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun delegateKey() = DelegateKey(typeWithResolvedAliases, jsonQualifiers())
|
fun delegateKey() = DelegateKey(type, jsonQualifiers())
|
||||||
|
|
||||||
/** Returns the JsonQualifiers on the field and parameter of this property. */
|
/** Returns the JsonQualifiers on the field and parameter of this property. */
|
||||||
private fun jsonQualifiers(): Set<AnnotationMirror> {
|
private fun jsonQualifiers(): Set<AnnotationMirror> {
|
||||||
@@ -107,8 +106,7 @@ internal data class TargetProperty(
|
|||||||
private val Element?.qualifiers: Set<AnnotationMirror>
|
private val Element?.qualifiers: Set<AnnotationMirror>
|
||||||
get() {
|
get() {
|
||||||
if (this == null) return setOf()
|
if (this == null) return setOf()
|
||||||
return AnnotationMirrors.getAnnotatedAnnotations(this,
|
return AnnotationMirrors.getAnnotatedAnnotations(this, JsonQualifier::class.java)
|
||||||
JsonQualifier::class.java)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the @Json name of this property, or this property's name if none is provided. */
|
/** Returns the @Json name of this property, or this property's name if none is provided. */
|
||||||
|
@@ -41,7 +41,6 @@ import javax.lang.model.element.ElementKind
|
|||||||
import javax.lang.model.element.ExecutableElement
|
import javax.lang.model.element.ExecutableElement
|
||||||
import javax.lang.model.element.TypeElement
|
import javax.lang.model.element.TypeElement
|
||||||
import javax.lang.model.element.VariableElement
|
import javax.lang.model.element.VariableElement
|
||||||
import javax.lang.model.type.DeclaredType
|
|
||||||
import javax.lang.model.util.Elements
|
import javax.lang.model.util.Elements
|
||||||
import javax.lang.model.util.Types
|
import javax.lang.model.util.Types
|
||||||
import javax.tools.Diagnostic.Kind.ERROR
|
import javax.tools.Diagnostic.Kind.ERROR
|
||||||
@@ -52,7 +51,7 @@ internal data class TargetType(
|
|||||||
val element: TypeElement,
|
val element: TypeElement,
|
||||||
val constructor: TargetConstructor,
|
val constructor: TargetConstructor,
|
||||||
val properties: Map<String, TargetProperty>,
|
val properties: Map<String, TargetProperty>,
|
||||||
val genericTypeNames: List<TypeVariableName>
|
val typeVariables: List<TypeVariableName>
|
||||||
) {
|
) {
|
||||||
val name = element.className
|
val name = element.className
|
||||||
val hasCompanionObject = proto.hasCompanionObjectName()
|
val hasCompanionObject = proto.hasCompanionObjectName()
|
||||||
@@ -93,31 +92,37 @@ internal data class TargetType(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val typeVariables = genericTypeNames(proto, typeMetadata.data.nameResolver)
|
||||||
|
val appliedType = AppliedType.get(element)
|
||||||
|
|
||||||
val constructor = TargetConstructor.primary(typeMetadata, elements)
|
val constructor = TargetConstructor.primary(typeMetadata, elements)
|
||||||
val properties = mutableMapOf<String, TargetProperty>()
|
val properties = mutableMapOf<String, TargetProperty>()
|
||||||
for (supertype in element.supertypes(types)) {
|
for (supertype in appliedType.supertypes(types)) {
|
||||||
if (supertype.asClassName() == OBJECT_CLASS) {
|
if (supertype.element.asClassName() == OBJECT_CLASS) {
|
||||||
continue // Don't load properties for java.lang.Object.
|
continue // Don't load properties for java.lang.Object.
|
||||||
}
|
}
|
||||||
if (supertype.kind != ElementKind.CLASS) {
|
if (supertype.element.kind != ElementKind.CLASS) {
|
||||||
continue // Don't load properties for interface types.
|
continue // Don't load properties for interface types.
|
||||||
}
|
}
|
||||||
if (supertype.kotlinMetadata == null) {
|
if (supertype.element.kotlinMetadata == null) {
|
||||||
messager.printMessage(ERROR,
|
messager.printMessage(ERROR,
|
||||||
"@JsonClass can't be applied to $element: supertype $supertype is not a Kotlin type",
|
"@JsonClass can't be applied to $element: supertype $supertype is not a Kotlin type",
|
||||||
element)
|
element)
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
for ((name, property) in declaredProperties(supertype, constructor)) {
|
val supertypeProperties = declaredProperties(
|
||||||
|
supertype.element, supertype.resolver, constructor)
|
||||||
|
for ((name, property) in supertypeProperties) {
|
||||||
properties.putIfAbsent(name, property)
|
properties.putIfAbsent(name, property)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val genericTypeNames = genericTypeNames(proto, typeMetadata.data.nameResolver)
|
return TargetType(proto, element, constructor, properties, typeVariables)
|
||||||
return TargetType(proto, element, constructor, properties, genericTypeNames)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the properties declared by `typeElement`. */
|
/** Returns the properties declared by `typeElement`. */
|
||||||
private fun declaredProperties(
|
private fun declaredProperties(
|
||||||
typeElement: TypeElement,
|
typeElement: TypeElement,
|
||||||
|
typeResolver: TypeResolver,
|
||||||
constructor: TargetConstructor
|
constructor: TargetConstructor
|
||||||
): Map<String, TargetProperty> {
|
): Map<String, TargetProperty> {
|
||||||
val typeMetadata: KotlinClassMetadata = typeElement.kotlinMetadata as KotlinClassMetadata
|
val typeMetadata: KotlinClassMetadata = typeElement.kotlinMetadata as KotlinClassMetadata
|
||||||
@@ -158,13 +163,10 @@ internal data class TargetType(
|
|||||||
val result = mutableMapOf<String, TargetProperty>()
|
val result = mutableMapOf<String, TargetProperty>()
|
||||||
for (property in classProto.propertyList) {
|
for (property in classProto.propertyList) {
|
||||||
val name = nameResolver.getString(property.name)
|
val name = nameResolver.getString(property.name)
|
||||||
val type = property.returnType.asTypeName(
|
val type = typeResolver.resolve(property.returnType.asTypeName(
|
||||||
nameResolver, classProto::getTypeParameter, false)
|
nameResolver, classProto::getTypeParameter, true))
|
||||||
val typeWithResolvedAliases = property.returnType.asTypeName(
|
result[name] = TargetProperty(name, type, property, constructor.parameters[name],
|
||||||
nameResolver, classProto::getTypeParameter, true)
|
annotationHolders[name], fields[name], setters[name], getters[name])
|
||||||
result[name] = TargetProperty(name, type, typeWithResolvedAliases, property,
|
|
||||||
constructor.parameters[name], annotationHolders[name], fields[name],
|
|
||||||
setters[name], getters[name])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@@ -180,19 +182,6 @@ internal data class TargetType(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns all supertypes of this, recursively. Includes interface and class supertypes. */
|
|
||||||
private fun TypeElement.supertypes(
|
|
||||||
types: Types,
|
|
||||||
result: MutableSet<TypeElement> = mutableSetOf()
|
|
||||||
): Set<TypeElement> {
|
|
||||||
result.add(this)
|
|
||||||
for (supertype in types.directSupertypes(asType())) {
|
|
||||||
val supertypeElement = (supertype as DeclaredType).asElement() as TypeElement
|
|
||||||
supertypeElement.supertypes(types, result)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private val Element.name get() = simpleName.toString()
|
private val Element.name get() = simpleName.toString()
|
||||||
|
|
||||||
private fun genericTypeNames(proto: Class, nameResolver: NameResolver): List<TypeVariableName> {
|
private fun genericTypeNames(proto: Class, nameResolver: NameResolver): List<TypeVariableName> {
|
||||||
@@ -219,4 +208,4 @@ internal data class TargetType(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import com.squareup.kotlinpoet.ARRAY
|
||||||
|
import com.squareup.kotlinpoet.BOOLEAN
|
||||||
|
import com.squareup.kotlinpoet.BYTE
|
||||||
|
import com.squareup.kotlinpoet.CHAR
|
||||||
|
import com.squareup.kotlinpoet.ClassName
|
||||||
|
import com.squareup.kotlinpoet.CodeBlock
|
||||||
|
import com.squareup.kotlinpoet.DOUBLE
|
||||||
|
import com.squareup.kotlinpoet.FLOAT
|
||||||
|
import com.squareup.kotlinpoet.INT
|
||||||
|
import com.squareup.kotlinpoet.LONG
|
||||||
|
import com.squareup.kotlinpoet.ParameterizedTypeName
|
||||||
|
import com.squareup.kotlinpoet.SHORT
|
||||||
|
import com.squareup.kotlinpoet.TypeName
|
||||||
|
import com.squareup.kotlinpoet.TypeVariableName
|
||||||
|
import com.squareup.kotlinpoet.WildcardTypeName
|
||||||
|
import com.squareup.kotlinpoet.asTypeName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders literals like `Types.newParameterizedType(List::class.java, String::class.java)`.
|
||||||
|
* Rendering is pluggable so that type variables can either be resolved or emitted as other code
|
||||||
|
* blocks.
|
||||||
|
*/
|
||||||
|
abstract class TypeRenderer {
|
||||||
|
abstract fun renderTypeVariable(typeVariable: TypeVariableName): CodeBlock
|
||||||
|
|
||||||
|
fun render(typeName: TypeName): CodeBlock {
|
||||||
|
if (typeName.nullable) {
|
||||||
|
return render(typeName.asNonNullable())
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (typeName) {
|
||||||
|
is ClassName -> CodeBlock.of("%T::class.java", typeName)
|
||||||
|
|
||||||
|
is ParameterizedTypeName -> {
|
||||||
|
// If it's an Array type, we shortcut this to return Types.arrayOf()
|
||||||
|
if (typeName.rawType == ARRAY) {
|
||||||
|
CodeBlock.of("%T.arrayOf(%L)",
|
||||||
|
Types::class,
|
||||||
|
render(typeName.typeArguments[0].objectType()))
|
||||||
|
} else {
|
||||||
|
val placeholders = typeName.typeArguments.joinToString(", ") { "%L" }
|
||||||
|
CodeBlock.of(
|
||||||
|
"%T.newParameterizedType(%T::class.java, $placeholders)",
|
||||||
|
Types::class,
|
||||||
|
typeName.rawType.objectType(),
|
||||||
|
*(typeName.typeArguments.map { render(it.objectType()) }.toTypedArray()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is WildcardTypeName -> {
|
||||||
|
val target: TypeName
|
||||||
|
val method: String
|
||||||
|
when {
|
||||||
|
typeName.lowerBounds.size == 1 -> {
|
||||||
|
target = typeName.lowerBounds[0]
|
||||||
|
method = "supertypeOf"
|
||||||
|
}
|
||||||
|
typeName.upperBounds.size == 1 -> {
|
||||||
|
target = typeName.upperBounds[0]
|
||||||
|
method = "subtypeOf"
|
||||||
|
}
|
||||||
|
else -> throw IllegalArgumentException(
|
||||||
|
"Unrepresentable wildcard type. Cannot have more than one bound: $typeName")
|
||||||
|
}
|
||||||
|
CodeBlock.of("%T.%L(%T::class.java)", Types::class, method, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
is TypeVariableName -> renderTypeVariable(typeName)
|
||||||
|
|
||||||
|
else -> throw IllegalArgumentException("Unrepresentable type: $typeName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TypeName.objectType(): TypeName {
|
||||||
|
return when (this) {
|
||||||
|
BOOLEAN -> Boolean::class.javaObjectType.asTypeName()
|
||||||
|
BYTE -> Byte::class.javaObjectType.asTypeName()
|
||||||
|
SHORT -> Short::class.javaObjectType.asTypeName()
|
||||||
|
INT -> Integer::class.javaObjectType.asTypeName()
|
||||||
|
LONG -> Long::class.javaObjectType.asTypeName()
|
||||||
|
CHAR -> Character::class.javaObjectType.asTypeName()
|
||||||
|
FLOAT -> Float::class.javaObjectType.asTypeName()
|
||||||
|
DOUBLE -> Double::class.javaObjectType.asTypeName()
|
||||||
|
else -> this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import com.squareup.kotlinpoet.ClassName
|
||||||
|
import com.squareup.kotlinpoet.ParameterizedTypeName
|
||||||
|
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 -> {
|
||||||
|
ParameterizedTypeName.get(
|
||||||
|
typeName.rawType, *(typeName.typeArguments.map { resolve(it) }.toTypedArray()))
|
||||||
|
}
|
||||||
|
|
||||||
|
is WildcardTypeName -> {
|
||||||
|
when {
|
||||||
|
typeName.lowerBounds.size == 1 -> {
|
||||||
|
WildcardTypeName.supertypeOf(resolve(typeName.lowerBounds[0]))
|
||||||
|
}
|
||||||
|
typeName.upperBounds.size == 1 -> {
|
||||||
|
WildcardTypeName.subtypeOf(resolve(typeName.upperBounds[0]))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
throw IllegalArgumentException(
|
||||||
|
"Unrepresentable wildcard type. Cannot have more than one bound: $typeName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is TypeVariableName -> resolveTypeVariable(typeName)
|
||||||
|
|
||||||
|
else -> throw IllegalArgumentException("Unrepresentable type: $typeName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -15,25 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.squareup.moshi
|
package com.squareup.moshi
|
||||||
|
|
||||||
import com.squareup.kotlinpoet.ARRAY
|
|
||||||
import com.squareup.kotlinpoet.BOOLEAN
|
|
||||||
import com.squareup.kotlinpoet.BYTE
|
|
||||||
import com.squareup.kotlinpoet.CHAR
|
|
||||||
import com.squareup.kotlinpoet.ClassName
|
import com.squareup.kotlinpoet.ClassName
|
||||||
import com.squareup.kotlinpoet.CodeBlock
|
|
||||||
import com.squareup.kotlinpoet.DOUBLE
|
|
||||||
import com.squareup.kotlinpoet.FLOAT
|
|
||||||
import com.squareup.kotlinpoet.INT
|
|
||||||
import com.squareup.kotlinpoet.LONG
|
|
||||||
import com.squareup.kotlinpoet.ParameterSpec
|
|
||||||
import com.squareup.kotlinpoet.ParameterizedTypeName
|
import com.squareup.kotlinpoet.ParameterizedTypeName
|
||||||
import com.squareup.kotlinpoet.SHORT
|
|
||||||
import com.squareup.kotlinpoet.TypeName
|
import com.squareup.kotlinpoet.TypeName
|
||||||
import com.squareup.kotlinpoet.TypeVariableName
|
|
||||||
import com.squareup.kotlinpoet.WildcardTypeName
|
|
||||||
import com.squareup.kotlinpoet.asTypeName
|
|
||||||
import javax.lang.model.element.ElementKind
|
|
||||||
import javax.lang.model.util.Elements
|
|
||||||
|
|
||||||
internal fun TypeName.rawType(): ClassName {
|
internal fun TypeName.rawType(): ClassName {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
@@ -42,98 +26,3 @@ internal fun TypeName.rawType(): ClassName {
|
|||||||
else -> throw IllegalArgumentException("Cannot get raw type from $this")
|
else -> throw IllegalArgumentException("Cannot get raw type from $this")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ClassName.isClass(elements: Elements): Boolean {
|
|
||||||
val fqcn = toString()
|
|
||||||
if (fqcn.startsWith("kotlin.collections.")) {
|
|
||||||
// These are special kotlin interfaces are only visible in kotlin, because they're replaced by
|
|
||||||
// the compiler with concrete java classes/
|
|
||||||
return false
|
|
||||||
} else if (this == ARRAY) {
|
|
||||||
// This is a "fake" class and not visible to Elements.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return elements.getTypeElement(fqcn).kind == ElementKind.INTERFACE
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun TypeName.objectType(): TypeName {
|
|
||||||
return when (this) {
|
|
||||||
BOOLEAN -> Boolean::class.javaObjectType.asTypeName()
|
|
||||||
BYTE -> Byte::class.javaObjectType.asTypeName()
|
|
||||||
SHORT -> Short::class.javaObjectType.asTypeName()
|
|
||||||
INT -> Integer::class.javaObjectType.asTypeName()
|
|
||||||
LONG -> Long::class.javaObjectType.asTypeName()
|
|
||||||
CHAR -> Character::class.javaObjectType.asTypeName()
|
|
||||||
FLOAT -> Float::class.javaObjectType.asTypeName()
|
|
||||||
DOUBLE -> Double::class.javaObjectType.asTypeName()
|
|
||||||
else -> this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun TypeName.makeType(
|
|
||||||
elementUtils: Elements,
|
|
||||||
typesArray: ParameterSpec,
|
|
||||||
genericTypeNames: List<TypeVariableName>
|
|
||||||
): CodeBlock {
|
|
||||||
if (nullable) {
|
|
||||||
return asNonNullable().makeType(elementUtils, typesArray, genericTypeNames)
|
|
||||||
}
|
|
||||||
return when (this) {
|
|
||||||
is ClassName -> CodeBlock.of(
|
|
||||||
"%T::class.java", this)
|
|
||||||
is ParameterizedTypeName -> {
|
|
||||||
// If it's an Array type, we shortcut this to return Types.arrayOf()
|
|
||||||
if (rawType == ARRAY) {
|
|
||||||
return CodeBlock.of("%T.arrayOf(%L)",
|
|
||||||
Types::class,
|
|
||||||
typeArguments[0].objectType().makeType(elementUtils, typesArray, genericTypeNames))
|
|
||||||
}
|
|
||||||
// If it's a Class type, we have to specify the generics.
|
|
||||||
val rawTypeParameters = if (rawType.isClass(elementUtils)) {
|
|
||||||
CodeBlock.of(
|
|
||||||
typeArguments.joinTo(
|
|
||||||
buffer = StringBuilder(),
|
|
||||||
separator = ", ",
|
|
||||||
prefix = "<",
|
|
||||||
postfix = ">") { "%T" }
|
|
||||||
.toString(),
|
|
||||||
*(typeArguments.map { objectType() }.toTypedArray())
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
CodeBlock.of("")
|
|
||||||
}
|
|
||||||
CodeBlock.of(
|
|
||||||
"%T.newParameterizedType(%T%L::class.java, ${typeArguments
|
|
||||||
.joinToString(", ") { "%L" }})",
|
|
||||||
Types::class,
|
|
||||||
rawType.objectType(),
|
|
||||||
rawTypeParameters,
|
|
||||||
*(typeArguments.map {
|
|
||||||
it.objectType().makeType(elementUtils, typesArray, genericTypeNames)
|
|
||||||
}.toTypedArray()))
|
|
||||||
}
|
|
||||||
is WildcardTypeName -> {
|
|
||||||
val target: TypeName
|
|
||||||
val method: String
|
|
||||||
when {
|
|
||||||
lowerBounds.size == 1 -> {
|
|
||||||
target = lowerBounds[0]
|
|
||||||
method = "supertypeOf"
|
|
||||||
}
|
|
||||||
upperBounds.size == 1 -> {
|
|
||||||
target = upperBounds[0]
|
|
||||||
method = "subtypeOf"
|
|
||||||
}
|
|
||||||
else -> throw IllegalArgumentException(
|
|
||||||
"Unrepresentable wildcard type. Cannot have more than one bound: " + this)
|
|
||||||
}
|
|
||||||
CodeBlock.of("%T.%L(%T::class.java)",
|
|
||||||
Types::class, method, target)
|
|
||||||
}
|
|
||||||
is TypeVariableName -> {
|
|
||||||
CodeBlock.of("%N[%L]", typesArray,
|
|
||||||
genericTypeNames.indexOfFirst { it == this })
|
|
||||||
}
|
|
||||||
else -> throw IllegalArgumentException("Unrepresentable type: " + this)
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user