From 982f9c94f6779fb7e1e77241f42d4ae502fd8e85 Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Sat, 24 Mar 2018 23:19:37 -0400 Subject: [PATCH] Fold the kotlin-codegen-runtime into Moshi itself. Rename @MoshiSerializable to @JsonClass. Like @Json, I'm anticipating a future where there are other interesting properties on this annotation. Perhaps a future feature where Moshi is strict and only adapts types that have a '@JsonClass' annotation. Also rename MoshiKotlinCodeGenProcessor to JsonClassCodeGenProcessor. We may later support other ways of generating code here; perhaps for regular Java types. --- ...cessor.kt => JsonClassCodeGenProcessor.kt} | 22 ++--- kotlin-codegen/integration-test/pom.xml | 6 -- .../com/squareup/moshi/DataClassesTest.kt | 34 +++++--- .../com/squareup/moshi/KotlinCodeGenTest.kt | 80 +++++++++--------- kotlin-codegen/runtime/pom.xml | 82 ------------------- .../com/squareup/moshi/MoshiSerializable.kt | 77 ----------------- .../java/com/squareup/moshi/JsonClass.java | 32 ++++++++ .../squareup/moshi/StandardJsonAdapters.java | 48 +++++++++++ pom.xml | 1 - 9 files changed, 155 insertions(+), 227 deletions(-) rename kotlin-codegen/compiler/src/main/java/com/squareup/moshi/{MoshiKotlinCodeGenProcessor.kt => JsonClassCodeGenProcessor.kt} (90%) delete mode 100644 kotlin-codegen/runtime/pom.xml delete mode 100644 kotlin-codegen/runtime/src/main/java/com/squareup/moshi/MoshiSerializable.kt create mode 100644 moshi/src/main/java/com/squareup/moshi/JsonClass.java diff --git a/kotlin-codegen/compiler/src/main/java/com/squareup/moshi/MoshiKotlinCodeGenProcessor.kt b/kotlin-codegen/compiler/src/main/java/com/squareup/moshi/JsonClassCodeGenProcessor.kt similarity index 90% rename from kotlin-codegen/compiler/src/main/java/com/squareup/moshi/MoshiKotlinCodeGenProcessor.kt rename to kotlin-codegen/compiler/src/main/java/com/squareup/moshi/JsonClassCodeGenProcessor.kt index 3b280de..345919e 100644 --- a/kotlin-codegen/compiler/src/main/java/com/squareup/moshi/MoshiKotlinCodeGenProcessor.kt +++ b/kotlin-codegen/compiler/src/main/java/com/squareup/moshi/JsonClassCodeGenProcessor.kt @@ -50,23 +50,25 @@ import javax.tools.Diagnostic.Kind.ERROR * 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 [MoshiSerializable] factory implementation. + * If you don't want this though, you can use the runtime [JsonClass] factory implementation. */ @AutoService(Processor::class) -class MoshiKotlinCodeGenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils { +class JsonClassCodeGenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils { - private val annotationName = MoshiSerializable::class.java.canonicalName + private val annotation = JsonClass::class.java - override fun getSupportedAnnotationTypes() = setOf(annotationName) + override fun getSupportedAnnotationTypes() = setOf(annotation.canonicalName) override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latest() override fun process(annotations: Set, roundEnv: RoundEnvironment): Boolean { - val annotationElement = elementUtils.getTypeElement(annotationName) - roundEnv.getElementsAnnotatedWith(annotationElement) - .asSequence() - .mapNotNull { processElement(it) } - .forEach { it.generateAndWrite() } + for (type in roundEnv.getElementsAnnotatedWith(annotation)) { + val jsonClass = type.getAnnotation(annotation) + if (jsonClass.generateAdapter) { + val adapterGenerator = processElement(type) ?: continue + adapterGenerator.generateAndWrite() + } + } return true } @@ -178,7 +180,7 @@ class MoshiKotlinCodeGenProcessor : KotlinAbstractProcessor(), KotlinMetadataUti private fun errorMustBeDataClass(element: Element) { messager.printMessage(ERROR, - "@${MoshiSerializable::class.java.simpleName} can't be applied to $element: must be a Kotlin data class", + "@${JsonClass::class.java.simpleName} can't be applied to $element: must be a Kotlin data class", element) } diff --git a/kotlin-codegen/integration-test/pom.xml b/kotlin-codegen/integration-test/pom.xml index 2ecc4b2..8b24a4e 100644 --- a/kotlin-codegen/integration-test/pom.xml +++ b/kotlin-codegen/integration-test/pom.xml @@ -18,11 +18,6 @@ moshi ${project.version} - - com.squareup.moshi - moshi-kotlin-codegen-runtime - ${project.version} - org.jetbrains.kotlin kotlin-stdlib @@ -57,7 +52,6 @@ src/main/kotlin - src/main/java diff --git a/kotlin-codegen/integration-test/src/test/kotlin/com/squareup/moshi/DataClassesTest.kt b/kotlin-codegen/integration-test/src/test/kotlin/com/squareup/moshi/DataClassesTest.kt index 7dea3d8..9c3bbd5 100644 --- a/kotlin-codegen/integration-test/src/test/kotlin/com/squareup/moshi/DataClassesTest.kt +++ b/kotlin-codegen/integration-test/src/test/kotlin/com/squareup/moshi/DataClassesTest.kt @@ -22,7 +22,7 @@ import org.junit.Test class DataClassesTest { - private val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + private val moshi = Moshi.Builder().build() @Test fun jsonAnnotation() { @@ -42,7 +42,7 @@ class DataClassesTest { assertThat(adapter.toJson(JsonAnnotation("baz"))).isEqualTo(expectedJson) } - @MoshiSerializable + @JsonClass(generateAdapter = true) data class JsonAnnotation(@Json(name = "foo") val bar: String) @Test @@ -79,7 +79,7 @@ class DataClassesTest { assertThat(adapter.toJson(instance2)).isEqualTo(json2) } - @MoshiSerializable + @JsonClass(generateAdapter = true) data class DefaultValues(val foo: String, val bar: String = "", val nullableBar: String? = null, @@ -97,7 +97,7 @@ class DataClassesTest { assertThat(adapter.toJson(instance)).isEqualTo(json) } - @MoshiSerializable + @JsonClass(generateAdapter = true) data class NullableArray(val data: Array) @Test @@ -112,7 +112,7 @@ class DataClassesTest { assertThat(adapter.toJson(instance)).isEqualTo(json) } - @MoshiSerializable + @JsonClass(generateAdapter = true) data class PrimitiveArray(val ints: IntArray) @Test @@ -136,7 +136,7 @@ class DataClassesTest { } } - @MoshiSerializable + @JsonClass(generateAdapter = true) data class NullabeTypes( val foo: String, val nullableString: String? @@ -160,7 +160,7 @@ class DataClassesTest { assertThat(newCollections).isEqualTo(specialCollections) } - @MoshiSerializable + @JsonClass(generateAdapter = true) data class SpecialCollections( val mutableList: MutableList, val mutableSet: MutableSet, @@ -194,7 +194,7 @@ class DataClassesTest { assertThat(newMutableProperties).isEqualTo(mutableProperties) } - @MoshiSerializable + @JsonClass(generateAdapter = true) data class MutableProperties( val immutableProperty: String, var mutableProperty: String, @@ -238,10 +238,24 @@ class DataClassesTest { val nullSerializedNullableTypeParams = adapter.fromJson(nullSerializedJson) assertThat(nullSerializedNullableTypeParams).isEqualTo(nullableTypeParams) } + + @Test + fun doNotGenerateAdapter() { + try { + StandardJsonAdapters.generatedAdapter( + moshi, DoNotGenerateAdapter::class.java, DoNotGenerateAdapter::class.java) + fail("found a generated adapter for a type that shouldn't have one") + } catch (e: RuntimeException) { + assertThat(e).hasCauseInstanceOf(ClassNotFoundException::class.java) + } + } + + @JsonClass(generateAdapter = false) + data class DoNotGenerateAdapter(val foo: String) } // Has to be outside to avoid Types seeing an owning class -@MoshiSerializable +@JsonClass(generateAdapter = true) data class NullableTypeParams( val nullableList: List, val nullableSet: Set, @@ -255,7 +269,7 @@ typealias TypeAliasName = String * This is here mostly just to ensure it still compiles. Covers variance, @Json, default values, * nullability, primitive arrays, and some wacky generics. */ -@MoshiSerializable +@JsonClass(generateAdapter = true) data class SmokeTestType( @Json(name = "first_name") val firstName: String, @Json(name = "last_name") val lastName: String, diff --git a/kotlin-codegen/integration-test/src/test/kotlin/com/squareup/moshi/KotlinCodeGenTest.kt b/kotlin-codegen/integration-test/src/test/kotlin/com/squareup/moshi/KotlinCodeGenTest.kt index 6b12614..f4133a0 100644 --- a/kotlin-codegen/integration-test/src/test/kotlin/com/squareup/moshi/KotlinCodeGenTest.kt +++ b/kotlin-codegen/integration-test/src/test/kotlin/com/squareup/moshi/KotlinCodeGenTest.kt @@ -26,7 +26,7 @@ import kotlin.annotation.AnnotationRetention.RUNTIME class KotlinCodeGenTest { @Ignore @Test fun constructorParameters() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(ConstructorParameters::class.java) val encoded = ConstructorParameters(3, 5) @@ -41,7 +41,7 @@ class KotlinCodeGenTest { @Ignore @Test fun properties() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(Properties::class.java) val encoded = Properties() @@ -60,7 +60,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun constructorParametersAndProperties() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(ConstructorParametersAndProperties::class.java) val encoded = ConstructorParametersAndProperties(3) @@ -77,7 +77,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun immutableConstructorParameters() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(ImmutableConstructorParameters::class.java) val encoded = ImmutableConstructorParameters(3, 5) @@ -91,7 +91,7 @@ class KotlinCodeGenTest { class ImmutableConstructorParameters(val a: Int, val b: Int) @Ignore @Test fun immutableProperties() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(ImmutableProperties::class.java) val encoded = ImmutableProperties(3, 5) @@ -108,7 +108,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun constructorDefaults() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(ConstructorDefaultValues::class.java) val encoded = ConstructorDefaultValues(3, 5) @@ -122,7 +122,7 @@ class KotlinCodeGenTest { class ConstructorDefaultValues(var a: Int = -1, var b: Int = -2) @Ignore @Test fun requiredValueAbsent() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(RequiredValueAbsent::class.java) try { @@ -136,7 +136,7 @@ class KotlinCodeGenTest { class RequiredValueAbsent(var a: Int = 3, var b: Int) @Ignore @Test fun nonNullConstructorParameterCalledWithNullFailsWithJsonDataException() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(HasNonNullConstructorParameter::class.java) try { @@ -150,7 +150,7 @@ class KotlinCodeGenTest { class HasNonNullConstructorParameter(val a: String) @Ignore @Test fun nonNullPropertySetToNullFailsWithJsonDataException() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(HasNonNullProperty::class.java) try { @@ -166,7 +166,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun duplicatedValue() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(DuplicateValue::class.java) try { @@ -180,7 +180,7 @@ class KotlinCodeGenTest { class DuplicateValue(var a: Int = -1, var b: Int = -2) @Ignore @Test fun explicitNull() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(ExplicitNull::class.java) val encoded = ExplicitNull(null, 5) @@ -195,7 +195,7 @@ class KotlinCodeGenTest { class ExplicitNull(var a: Int?, var b: Int?) @Ignore @Test fun absentNull() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(AbsentNull::class.java) val encoded = AbsentNull(null, 5) @@ -210,7 +210,7 @@ class KotlinCodeGenTest { class AbsentNull(var a: Int?, var b: Int?) @Ignore @Test fun repeatedValue() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(RepeatedValue::class.java) try { @@ -225,7 +225,6 @@ class KotlinCodeGenTest { @Ignore @Test fun constructorParameterWithQualifier() { val moshi = Moshi.Builder() - .add(MoshiSerializableFactory()) .add(UppercaseJsonAdapter()) .build() val jsonAdapter = moshi.adapter(ConstructorParameterWithQualifier::class.java) @@ -242,7 +241,6 @@ class KotlinCodeGenTest { @Ignore @Test fun propertyWithQualifier() { val moshi = Moshi.Builder() - .add(MoshiSerializableFactory()) .add(UppercaseJsonAdapter()) .build() val jsonAdapter = moshi.adapter(PropertyWithQualifier::class.java) @@ -263,7 +261,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun constructorParameterWithJsonName() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(ConstructorParameterWithJsonName::class.java) val encoded = ConstructorParameterWithJsonName(3, 5) @@ -277,7 +275,7 @@ class KotlinCodeGenTest { class ConstructorParameterWithJsonName(@Json(name = "key a") var a: Int, var b: Int) @Ignore @Test fun propertyWithJsonName() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(PropertyWithJsonName::class.java) val encoded = PropertyWithJsonName() @@ -296,7 +294,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun transientConstructorParameter() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(TransientConstructorParameter::class.java) val encoded = TransientConstructorParameter(3, 5) @@ -310,7 +308,7 @@ class KotlinCodeGenTest { class TransientConstructorParameter(@Transient var a: Int = -1, var b: Int = -1) @Ignore @Test fun requiredTransientConstructorParameterFails() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() try { moshi.adapter(RequiredTransientConstructorParameter::class.java) fail() @@ -324,7 +322,7 @@ class KotlinCodeGenTest { class RequiredTransientConstructorParameter(@Transient var a: Int) @Ignore @Test fun transientProperty() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(TransientProperty::class.java) val encoded = TransientProperty() @@ -343,7 +341,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun supertypeConstructorParameters() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(SubtypeConstructorParameters::class.java) val encoded = SubtypeConstructorParameters(3, 5) @@ -359,7 +357,7 @@ class KotlinCodeGenTest { class SubtypeConstructorParameters(a: Int, var b: Int) : SupertypeConstructorParameters(a) @Ignore @Test fun supertypeProperties() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(SubtypeProperties::class.java) val encoded = SubtypeProperties() @@ -381,7 +379,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun extendsPlatformClassWithPrivateField() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(ExtendsPlatformClassWithPrivateField::class.java) val encoded = ExtendsPlatformClassWithPrivateField(3) @@ -395,7 +393,7 @@ class KotlinCodeGenTest { internal class ExtendsPlatformClassWithPrivateField(var a: Int) : SimpleTimeZone(0, "C") @Ignore @Test fun extendsPlatformClassWithProtectedField() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(ExtendsPlatformClassWithProtectedField::class.java) val encoded = ExtendsPlatformClassWithProtectedField(3) @@ -413,7 +411,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun platformTypeThrows() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() try { moshi.adapter(Triple::class.java) fail() @@ -424,7 +422,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun privateConstructorParameters() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(PrivateConstructorParameters::class.java) val encoded = PrivateConstructorParameters(3, 5) @@ -441,7 +439,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun privateConstructor() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(PrivateConstructor::class.java) val encoded = PrivateConstructor.newInstance(3, 5) @@ -461,7 +459,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun privateProperties() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(PrivateProperties::class.java) val encoded = PrivateProperties() @@ -492,7 +490,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun unsettablePropertyIgnored() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(UnsettableProperty::class.java) val encoded = UnsettableProperty() @@ -510,7 +508,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun getterOnlyNoBackingField() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(GetterOnly::class.java) val encoded = GetterOnly(3, 5) @@ -528,7 +526,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun getterAndSetterNoBackingField() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(GetterAndSetter::class.java) val encoded = GetterAndSetter(3, 5) @@ -556,7 +554,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun nonPropertyConstructorParameter() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() try { moshi.adapter(NonPropertyConstructorParameter::class.java) fail() @@ -570,7 +568,7 @@ class KotlinCodeGenTest { class NonPropertyConstructorParameter(a: Int, val b: Int) @Ignore @Test fun kotlinEnumsAreNotCovered() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val adapter = moshi.adapter(UsingEnum::class.java) assertThat(adapter.fromJson("""{"e": "A"}""")).isEqualTo(UsingEnum(KotlinEnum.A)) @@ -583,7 +581,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun interfacesNotSupported() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() try { moshi.adapter(Interface::class.java) fail() @@ -596,7 +594,7 @@ class KotlinCodeGenTest { interface Interface @Ignore @Test fun abstractClassesNotSupported() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() try { moshi.adapter(AbstractClass::class.java) fail() @@ -609,7 +607,7 @@ class KotlinCodeGenTest { abstract class AbstractClass(val a: Int) @Ignore @Test fun innerClassesNotSupported() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() try { moshi.adapter(InnerClass::class.java) fail() @@ -623,7 +621,7 @@ class KotlinCodeGenTest { @Ignore @Test fun localClassesNotSupported() { class LocalClass(val a: Int) - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() try { moshi.adapter(LocalClass::class.java) fail() @@ -634,7 +632,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun objectDeclarationsNotSupported() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() try { moshi.adapter(ObjectDeclaration.javaClass) fail() @@ -652,7 +650,7 @@ class KotlinCodeGenTest { val expression = object : Any() { var a = 5 } - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() try { moshi.adapter(expression.javaClass) fail() @@ -663,7 +661,7 @@ class KotlinCodeGenTest { } @Ignore @Test fun manyProperties32() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(ManyProperties32::class.java) val encoded = ManyProperties32( @@ -703,7 +701,7 @@ class KotlinCodeGenTest { var v31: Int, var v32: Int) @Ignore @Test fun manyProperties33() { - val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() + val moshi = Moshi.Builder().build() val jsonAdapter = moshi.adapter(ManyProperties33::class.java) val encoded = ManyProperties33( diff --git a/kotlin-codegen/runtime/pom.xml b/kotlin-codegen/runtime/pom.xml deleted file mode 100644 index 08b8e36..0000000 --- a/kotlin-codegen/runtime/pom.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - 4.0.0 - - - com.squareup.moshi - moshi-parent - 1.6.0-SNAPSHOT - ../../pom.xml - - - moshi-kotlin-codegen-runtime - - - - com.squareup.moshi - moshi - ${project.version} - - - junit - junit - test - - - org.assertj - assertj-core - test - - - org.jetbrains.kotlin - kotlin-stdlib - - - - - - - org.jetbrains.kotlin - kotlin-maven-plugin - ${kotlin.version} - - - compile - compile - - compile - - - - test-compile - test-compile - - test-compile - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - compile - compile - - compile - - - - testCompile - test-compile - - testCompile - - - - - - - diff --git a/kotlin-codegen/runtime/src/main/java/com/squareup/moshi/MoshiSerializable.kt b/kotlin-codegen/runtime/src/main/java/com/squareup/moshi/MoshiSerializable.kt deleted file mode 100644 index d685765..0000000 --- a/kotlin-codegen/runtime/src/main/java/com/squareup/moshi/MoshiSerializable.kt +++ /dev/null @@ -1,77 +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 - -import java.lang.reflect.InvocationTargetException -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type -import kotlin.annotation.AnnotationRetention.RUNTIME -import kotlin.annotation.AnnotationTarget.CLASS - -@Retention(RUNTIME) -@Target(CLASS) -annotation class MoshiSerializable - -class MoshiSerializableFactory : JsonAdapter.Factory { - - override fun create(type: Type, annotations: Set, moshi: Moshi): JsonAdapter<*>? { - - val rawType = Types.getRawType(type) - if (!rawType.isAnnotationPresent(MoshiSerializable::class.java)) { - return null - } - - val clsName = rawType.name.replace("$", "_") - val constructor = try { - @Suppress("UNCHECKED_CAST") - val bindingClass = rawType.classLoader - .loadClass(clsName + "JsonAdapter") as Class> - if (type is ParameterizedType) { - // This is generic, use the two param moshi + type constructor - bindingClass.getDeclaredConstructor(Moshi::class.java, Array::class.java) - } else { - // The standard single param moshi constructor - bindingClass.getDeclaredConstructor(Moshi::class.java) - } - } catch (e: ClassNotFoundException) { - throw RuntimeException("Unable to find generated Moshi adapter class for $clsName", e) - } catch (e: NoSuchMethodException) { - throw RuntimeException("Unable to find generated Moshi adapter constructor for $clsName", e) - } - - try { - return when { - constructor.parameterTypes.size == 1 -> constructor.newInstance(moshi) - type is ParameterizedType -> constructor.newInstance(moshi, type.actualTypeArguments) - else -> throw IllegalStateException("Unable to handle type $type") - } - } catch (e: IllegalAccessException) { - throw RuntimeException("Unable to invoke $constructor", e) - } catch (e: InstantiationException) { - throw RuntimeException("Unable to invoke $constructor", e) - } catch (e: InvocationTargetException) { - val cause = e.cause - if (cause is RuntimeException) { - throw cause - } - if (cause is Error) { - throw cause - } - throw RuntimeException( - "Could not create generated JsonAdapter instance for type $rawType", cause) - } - } -} diff --git a/moshi/src/main/java/com/squareup/moshi/JsonClass.java b/moshi/src/main/java/com/squareup/moshi/JsonClass.java new file mode 100644 index 0000000..059f41b --- /dev/null +++ b/moshi/src/main/java/com/squareup/moshi/JsonClass.java @@ -0,0 +1,32 @@ +/* + * 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 java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Customizes how a type is encoded as JSON. + * + *

This annotation is currently only permitted on declarations of data classes in Kotlin. + */ +@Retention(RUNTIME) +@Documented +public @interface JsonClass { + boolean generateAdapter(); +} diff --git a/moshi/src/main/java/com/squareup/moshi/StandardJsonAdapters.java b/moshi/src/main/java/com/squareup/moshi/StandardJsonAdapters.java index 5a7eb46..5b1be37 100644 --- a/moshi/src/main/java/com/squareup/moshi/StandardJsonAdapters.java +++ b/moshi/src/main/java/com/squareup/moshi/StandardJsonAdapters.java @@ -18,6 +18,9 @@ package com.squareup.moshi; import com.squareup.moshi.internal.Util; import java.io.IOException; import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Collection; @@ -53,6 +56,12 @@ final class StandardJsonAdapters { if (type == Object.class) return new ObjectJsonAdapter(moshi).nullSafe(); Class rawType = Types.getRawType(type); + + JsonClass jsonClass = rawType.getAnnotation(JsonClass.class); + if (jsonClass != null && jsonClass.generateAdapter()) { + return generatedAdapter(moshi, type, rawType); + } + if (rawType.isEnum()) { //noinspection unchecked return new EnumJsonAdapter<>((Class) rawType).nullSafe(); @@ -215,6 +224,45 @@ final class StandardJsonAdapters { } }; + /** + * Loads the generated JsonAdapter for classes annotated {@link JsonClass}. This works because it + * uses the same naming conventions as {@code JsonClassCodeGenProcessor}. + */ + static JsonAdapter generatedAdapter(Moshi moshi, Type type, Class rawType) { + String adapterClassName = rawType.getName().replace("$", "_") + "JsonAdapter"; + try { + @SuppressWarnings("unchecked") // We generate types to match. + Class> adapterClass = (Class>) + Class.forName(adapterClassName, true, rawType.getClassLoader()); + if (type instanceof ParameterizedType) { + Constructor> constructor + = adapterClass.getDeclaredConstructor(Moshi.class, Type[].class); + constructor.setAccessible(true); + return constructor.newInstance(moshi, ((ParameterizedType) type).getActualTypeArguments()); + } else { + Constructor> constructor + = adapterClass.getDeclaredConstructor(Moshi.class); + constructor.setAccessible(true); + return constructor.newInstance(moshi); + } + } catch (ClassNotFoundException e) { + throw new RuntimeException( + "Failed to find the generated JsonAdapter class for " + rawType, e); + } catch (NoSuchMethodException e) { + throw new RuntimeException( + "Failed to find the generated JsonAdapter constructor for " + rawType, e); + } catch (IllegalAccessException e) { + throw new RuntimeException( + "Failed to access the generated JsonAdapter for " + rawType, e); + } catch (InvocationTargetException e) { + throw new RuntimeException( + "Failed to construct the generated JsonAdapter for " + rawType, e); + } catch (InstantiationException e) { + throw new RuntimeException( + "Failed to instantiate the generated JsonAdapter for " + rawType, e); + } + } + static final class EnumJsonAdapter> extends JsonAdapter { private final Class enumType; private final String[] nameStrings; diff --git a/pom.xml b/pom.xml index 30b6416..d1d2a10 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,6 @@ kotlin kotlin-codegen/compiler kotlin-codegen/integration-test - kotlin-codegen/runtime