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.
This commit is contained in:
Jesse Wilson
2018-03-24 23:19:37 -04:00
parent d045947ea7
commit 982f9c94f6
9 changed files with 155 additions and 227 deletions

View File

@@ -50,23 +50,25 @@ import javax.tools.Diagnostic.Kind.ERROR
* adapter will also be internal). * adapter will also be internal).
* *
* If you define a companion object, a jsonAdapter() extension function will be generated onto it. * 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) @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 getSupportedSourceVersion(): SourceVersion = SourceVersion.latest()
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean { override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
val annotationElement = elementUtils.getTypeElement(annotationName) for (type in roundEnv.getElementsAnnotatedWith(annotation)) {
roundEnv.getElementsAnnotatedWith(annotationElement) val jsonClass = type.getAnnotation(annotation)
.asSequence() if (jsonClass.generateAdapter) {
.mapNotNull { processElement(it) } val adapterGenerator = processElement(type) ?: continue
.forEach { it.generateAndWrite() } adapterGenerator.generateAndWrite()
}
}
return true return true
} }
@@ -178,7 +180,7 @@ class MoshiKotlinCodeGenProcessor : KotlinAbstractProcessor(), KotlinMetadataUti
private fun errorMustBeDataClass(element: Element) { private fun errorMustBeDataClass(element: Element) {
messager.printMessage(ERROR, 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) element)
} }

View File

@@ -18,11 +18,6 @@
<artifactId>moshi</artifactId> <artifactId>moshi</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.squareup.moshi</groupId>
<artifactId>moshi-kotlin-codegen-runtime</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.jetbrains.kotlin</groupId> <groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId> <artifactId>kotlin-stdlib</artifactId>
@@ -57,7 +52,6 @@
<configuration> <configuration>
<sourceDirs> <sourceDirs>
<sourceDir>src/main/kotlin</sourceDir> <sourceDir>src/main/kotlin</sourceDir>
<sourceDir>src/main/java</sourceDir>
</sourceDirs> </sourceDirs>
<annotationProcessorPaths> <annotationProcessorPaths>
<annotationProcessorPath> <annotationProcessorPath>

View File

@@ -22,7 +22,7 @@ import org.junit.Test
class DataClassesTest { class DataClassesTest {
private val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() private val moshi = Moshi.Builder().build()
@Test @Test
fun jsonAnnotation() { fun jsonAnnotation() {
@@ -42,7 +42,7 @@ class DataClassesTest {
assertThat(adapter.toJson(JsonAnnotation("baz"))).isEqualTo(expectedJson) assertThat(adapter.toJson(JsonAnnotation("baz"))).isEqualTo(expectedJson)
} }
@MoshiSerializable @JsonClass(generateAdapter = true)
data class JsonAnnotation(@Json(name = "foo") val bar: String) data class JsonAnnotation(@Json(name = "foo") val bar: String)
@Test @Test
@@ -79,7 +79,7 @@ class DataClassesTest {
assertThat(adapter.toJson(instance2)).isEqualTo(json2) assertThat(adapter.toJson(instance2)).isEqualTo(json2)
} }
@MoshiSerializable @JsonClass(generateAdapter = true)
data class DefaultValues(val foo: String, data class DefaultValues(val foo: String,
val bar: String = "", val bar: String = "",
val nullableBar: String? = null, val nullableBar: String? = null,
@@ -97,7 +97,7 @@ class DataClassesTest {
assertThat(adapter.toJson(instance)).isEqualTo(json) assertThat(adapter.toJson(instance)).isEqualTo(json)
} }
@MoshiSerializable @JsonClass(generateAdapter = true)
data class NullableArray(val data: Array<String?>) data class NullableArray(val data: Array<String?>)
@Test @Test
@@ -112,7 +112,7 @@ class DataClassesTest {
assertThat(adapter.toJson(instance)).isEqualTo(json) assertThat(adapter.toJson(instance)).isEqualTo(json)
} }
@MoshiSerializable @JsonClass(generateAdapter = true)
data class PrimitiveArray(val ints: IntArray) data class PrimitiveArray(val ints: IntArray)
@Test @Test
@@ -136,7 +136,7 @@ class DataClassesTest {
} }
} }
@MoshiSerializable @JsonClass(generateAdapter = true)
data class NullabeTypes( data class NullabeTypes(
val foo: String, val foo: String,
val nullableString: String? val nullableString: String?
@@ -160,7 +160,7 @@ class DataClassesTest {
assertThat(newCollections).isEqualTo(specialCollections) assertThat(newCollections).isEqualTo(specialCollections)
} }
@MoshiSerializable @JsonClass(generateAdapter = true)
data class SpecialCollections( data class SpecialCollections(
val mutableList: MutableList<String>, val mutableList: MutableList<String>,
val mutableSet: MutableSet<String>, val mutableSet: MutableSet<String>,
@@ -194,7 +194,7 @@ class DataClassesTest {
assertThat(newMutableProperties).isEqualTo(mutableProperties) assertThat(newMutableProperties).isEqualTo(mutableProperties)
} }
@MoshiSerializable @JsonClass(generateAdapter = true)
data class MutableProperties( data class MutableProperties(
val immutableProperty: String, val immutableProperty: String,
var mutableProperty: String, var mutableProperty: String,
@@ -238,10 +238,24 @@ class DataClassesTest {
val nullSerializedNullableTypeParams = adapter.fromJson(nullSerializedJson) val nullSerializedNullableTypeParams = adapter.fromJson(nullSerializedJson)
assertThat(nullSerializedNullableTypeParams).isEqualTo(nullableTypeParams) 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 // Has to be outside to avoid Types seeing an owning class
@MoshiSerializable @JsonClass(generateAdapter = true)
data class NullableTypeParams<T>( data class NullableTypeParams<T>(
val nullableList: List<String?>, val nullableList: List<String?>,
val nullableSet: Set<String?>, val nullableSet: Set<String?>,
@@ -255,7 +269,7 @@ typealias TypeAliasName = String
* This is here mostly just to ensure it still compiles. Covers variance, @Json, default values, * This is here mostly just to ensure it still compiles. Covers variance, @Json, default values,
* nullability, primitive arrays, and some wacky generics. * nullability, primitive arrays, and some wacky generics.
*/ */
@MoshiSerializable @JsonClass(generateAdapter = true)
data class SmokeTestType( data class SmokeTestType(
@Json(name = "first_name") val firstName: String, @Json(name = "first_name") val firstName: String,
@Json(name = "last_name") val lastName: String, @Json(name = "last_name") val lastName: String,

View File

@@ -26,7 +26,7 @@ import kotlin.annotation.AnnotationRetention.RUNTIME
class KotlinCodeGenTest { class KotlinCodeGenTest {
@Ignore @Test fun constructorParameters() { @Ignore @Test fun constructorParameters() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(ConstructorParameters::class.java) val jsonAdapter = moshi.adapter(ConstructorParameters::class.java)
val encoded = ConstructorParameters(3, 5) val encoded = ConstructorParameters(3, 5)
@@ -41,7 +41,7 @@ class KotlinCodeGenTest {
@Ignore @Test fun properties() { @Ignore @Test fun properties() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(Properties::class.java) val jsonAdapter = moshi.adapter(Properties::class.java)
val encoded = Properties() val encoded = Properties()
@@ -60,7 +60,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun constructorParametersAndProperties() { @Ignore @Test fun constructorParametersAndProperties() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(ConstructorParametersAndProperties::class.java) val jsonAdapter = moshi.adapter(ConstructorParametersAndProperties::class.java)
val encoded = ConstructorParametersAndProperties(3) val encoded = ConstructorParametersAndProperties(3)
@@ -77,7 +77,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun immutableConstructorParameters() { @Ignore @Test fun immutableConstructorParameters() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(ImmutableConstructorParameters::class.java) val jsonAdapter = moshi.adapter(ImmutableConstructorParameters::class.java)
val encoded = ImmutableConstructorParameters(3, 5) val encoded = ImmutableConstructorParameters(3, 5)
@@ -91,7 +91,7 @@ class KotlinCodeGenTest {
class ImmutableConstructorParameters(val a: Int, val b: Int) class ImmutableConstructorParameters(val a: Int, val b: Int)
@Ignore @Test fun immutableProperties() { @Ignore @Test fun immutableProperties() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(ImmutableProperties::class.java) val jsonAdapter = moshi.adapter(ImmutableProperties::class.java)
val encoded = ImmutableProperties(3, 5) val encoded = ImmutableProperties(3, 5)
@@ -108,7 +108,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun constructorDefaults() { @Ignore @Test fun constructorDefaults() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(ConstructorDefaultValues::class.java) val jsonAdapter = moshi.adapter(ConstructorDefaultValues::class.java)
val encoded = ConstructorDefaultValues(3, 5) val encoded = ConstructorDefaultValues(3, 5)
@@ -122,7 +122,7 @@ class KotlinCodeGenTest {
class ConstructorDefaultValues(var a: Int = -1, var b: Int = -2) class ConstructorDefaultValues(var a: Int = -1, var b: Int = -2)
@Ignore @Test fun requiredValueAbsent() { @Ignore @Test fun requiredValueAbsent() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(RequiredValueAbsent::class.java) val jsonAdapter = moshi.adapter(RequiredValueAbsent::class.java)
try { try {
@@ -136,7 +136,7 @@ class KotlinCodeGenTest {
class RequiredValueAbsent(var a: Int = 3, var b: Int) class RequiredValueAbsent(var a: Int = 3, var b: Int)
@Ignore @Test fun nonNullConstructorParameterCalledWithNullFailsWithJsonDataException() { @Ignore @Test fun nonNullConstructorParameterCalledWithNullFailsWithJsonDataException() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(HasNonNullConstructorParameter::class.java) val jsonAdapter = moshi.adapter(HasNonNullConstructorParameter::class.java)
try { try {
@@ -150,7 +150,7 @@ class KotlinCodeGenTest {
class HasNonNullConstructorParameter(val a: String) class HasNonNullConstructorParameter(val a: String)
@Ignore @Test fun nonNullPropertySetToNullFailsWithJsonDataException() { @Ignore @Test fun nonNullPropertySetToNullFailsWithJsonDataException() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(HasNonNullProperty::class.java) val jsonAdapter = moshi.adapter(HasNonNullProperty::class.java)
try { try {
@@ -166,7 +166,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun duplicatedValue() { @Ignore @Test fun duplicatedValue() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(DuplicateValue::class.java) val jsonAdapter = moshi.adapter(DuplicateValue::class.java)
try { try {
@@ -180,7 +180,7 @@ class KotlinCodeGenTest {
class DuplicateValue(var a: Int = -1, var b: Int = -2) class DuplicateValue(var a: Int = -1, var b: Int = -2)
@Ignore @Test fun explicitNull() { @Ignore @Test fun explicitNull() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(ExplicitNull::class.java) val jsonAdapter = moshi.adapter(ExplicitNull::class.java)
val encoded = ExplicitNull(null, 5) val encoded = ExplicitNull(null, 5)
@@ -195,7 +195,7 @@ class KotlinCodeGenTest {
class ExplicitNull(var a: Int?, var b: Int?) class ExplicitNull(var a: Int?, var b: Int?)
@Ignore @Test fun absentNull() { @Ignore @Test fun absentNull() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(AbsentNull::class.java) val jsonAdapter = moshi.adapter(AbsentNull::class.java)
val encoded = AbsentNull(null, 5) val encoded = AbsentNull(null, 5)
@@ -210,7 +210,7 @@ class KotlinCodeGenTest {
class AbsentNull(var a: Int?, var b: Int?) class AbsentNull(var a: Int?, var b: Int?)
@Ignore @Test fun repeatedValue() { @Ignore @Test fun repeatedValue() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(RepeatedValue::class.java) val jsonAdapter = moshi.adapter(RepeatedValue::class.java)
try { try {
@@ -225,7 +225,6 @@ class KotlinCodeGenTest {
@Ignore @Test fun constructorParameterWithQualifier() { @Ignore @Test fun constructorParameterWithQualifier() {
val moshi = Moshi.Builder() val moshi = Moshi.Builder()
.add(MoshiSerializableFactory())
.add(UppercaseJsonAdapter()) .add(UppercaseJsonAdapter())
.build() .build()
val jsonAdapter = moshi.adapter(ConstructorParameterWithQualifier::class.java) val jsonAdapter = moshi.adapter(ConstructorParameterWithQualifier::class.java)
@@ -242,7 +241,6 @@ class KotlinCodeGenTest {
@Ignore @Test fun propertyWithQualifier() { @Ignore @Test fun propertyWithQualifier() {
val moshi = Moshi.Builder() val moshi = Moshi.Builder()
.add(MoshiSerializableFactory())
.add(UppercaseJsonAdapter()) .add(UppercaseJsonAdapter())
.build() .build()
val jsonAdapter = moshi.adapter(PropertyWithQualifier::class.java) val jsonAdapter = moshi.adapter(PropertyWithQualifier::class.java)
@@ -263,7 +261,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun constructorParameterWithJsonName() { @Ignore @Test fun constructorParameterWithJsonName() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(ConstructorParameterWithJsonName::class.java) val jsonAdapter = moshi.adapter(ConstructorParameterWithJsonName::class.java)
val encoded = ConstructorParameterWithJsonName(3, 5) val encoded = ConstructorParameterWithJsonName(3, 5)
@@ -277,7 +275,7 @@ class KotlinCodeGenTest {
class ConstructorParameterWithJsonName(@Json(name = "key a") var a: Int, var b: Int) class ConstructorParameterWithJsonName(@Json(name = "key a") var a: Int, var b: Int)
@Ignore @Test fun propertyWithJsonName() { @Ignore @Test fun propertyWithJsonName() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(PropertyWithJsonName::class.java) val jsonAdapter = moshi.adapter(PropertyWithJsonName::class.java)
val encoded = PropertyWithJsonName() val encoded = PropertyWithJsonName()
@@ -296,7 +294,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun transientConstructorParameter() { @Ignore @Test fun transientConstructorParameter() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(TransientConstructorParameter::class.java) val jsonAdapter = moshi.adapter(TransientConstructorParameter::class.java)
val encoded = TransientConstructorParameter(3, 5) val encoded = TransientConstructorParameter(3, 5)
@@ -310,7 +308,7 @@ class KotlinCodeGenTest {
class TransientConstructorParameter(@Transient var a: Int = -1, var b: Int = -1) class TransientConstructorParameter(@Transient var a: Int = -1, var b: Int = -1)
@Ignore @Test fun requiredTransientConstructorParameterFails() { @Ignore @Test fun requiredTransientConstructorParameterFails() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
try { try {
moshi.adapter(RequiredTransientConstructorParameter::class.java) moshi.adapter(RequiredTransientConstructorParameter::class.java)
fail() fail()
@@ -324,7 +322,7 @@ class KotlinCodeGenTest {
class RequiredTransientConstructorParameter(@Transient var a: Int) class RequiredTransientConstructorParameter(@Transient var a: Int)
@Ignore @Test fun transientProperty() { @Ignore @Test fun transientProperty() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(TransientProperty::class.java) val jsonAdapter = moshi.adapter(TransientProperty::class.java)
val encoded = TransientProperty() val encoded = TransientProperty()
@@ -343,7 +341,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun supertypeConstructorParameters() { @Ignore @Test fun supertypeConstructorParameters() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(SubtypeConstructorParameters::class.java) val jsonAdapter = moshi.adapter(SubtypeConstructorParameters::class.java)
val encoded = SubtypeConstructorParameters(3, 5) val encoded = SubtypeConstructorParameters(3, 5)
@@ -359,7 +357,7 @@ class KotlinCodeGenTest {
class SubtypeConstructorParameters(a: Int, var b: Int) : SupertypeConstructorParameters(a) class SubtypeConstructorParameters(a: Int, var b: Int) : SupertypeConstructorParameters(a)
@Ignore @Test fun supertypeProperties() { @Ignore @Test fun supertypeProperties() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(SubtypeProperties::class.java) val jsonAdapter = moshi.adapter(SubtypeProperties::class.java)
val encoded = SubtypeProperties() val encoded = SubtypeProperties()
@@ -381,7 +379,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun extendsPlatformClassWithPrivateField() { @Ignore @Test fun extendsPlatformClassWithPrivateField() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(ExtendsPlatformClassWithPrivateField::class.java) val jsonAdapter = moshi.adapter(ExtendsPlatformClassWithPrivateField::class.java)
val encoded = ExtendsPlatformClassWithPrivateField(3) val encoded = ExtendsPlatformClassWithPrivateField(3)
@@ -395,7 +393,7 @@ class KotlinCodeGenTest {
internal class ExtendsPlatformClassWithPrivateField(var a: Int) : SimpleTimeZone(0, "C") internal class ExtendsPlatformClassWithPrivateField(var a: Int) : SimpleTimeZone(0, "C")
@Ignore @Test fun extendsPlatformClassWithProtectedField() { @Ignore @Test fun extendsPlatformClassWithProtectedField() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(ExtendsPlatformClassWithProtectedField::class.java) val jsonAdapter = moshi.adapter(ExtendsPlatformClassWithProtectedField::class.java)
val encoded = ExtendsPlatformClassWithProtectedField(3) val encoded = ExtendsPlatformClassWithProtectedField(3)
@@ -413,7 +411,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun platformTypeThrows() { @Ignore @Test fun platformTypeThrows() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
try { try {
moshi.adapter(Triple::class.java) moshi.adapter(Triple::class.java)
fail() fail()
@@ -424,7 +422,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun privateConstructorParameters() { @Ignore @Test fun privateConstructorParameters() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(PrivateConstructorParameters::class.java) val jsonAdapter = moshi.adapter(PrivateConstructorParameters::class.java)
val encoded = PrivateConstructorParameters(3, 5) val encoded = PrivateConstructorParameters(3, 5)
@@ -441,7 +439,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun privateConstructor() { @Ignore @Test fun privateConstructor() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(PrivateConstructor::class.java) val jsonAdapter = moshi.adapter(PrivateConstructor::class.java)
val encoded = PrivateConstructor.newInstance(3, 5) val encoded = PrivateConstructor.newInstance(3, 5)
@@ -461,7 +459,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun privateProperties() { @Ignore @Test fun privateProperties() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(PrivateProperties::class.java) val jsonAdapter = moshi.adapter(PrivateProperties::class.java)
val encoded = PrivateProperties() val encoded = PrivateProperties()
@@ -492,7 +490,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun unsettablePropertyIgnored() { @Ignore @Test fun unsettablePropertyIgnored() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(UnsettableProperty::class.java) val jsonAdapter = moshi.adapter(UnsettableProperty::class.java)
val encoded = UnsettableProperty() val encoded = UnsettableProperty()
@@ -510,7 +508,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun getterOnlyNoBackingField() { @Ignore @Test fun getterOnlyNoBackingField() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(GetterOnly::class.java) val jsonAdapter = moshi.adapter(GetterOnly::class.java)
val encoded = GetterOnly(3, 5) val encoded = GetterOnly(3, 5)
@@ -528,7 +526,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun getterAndSetterNoBackingField() { @Ignore @Test fun getterAndSetterNoBackingField() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(GetterAndSetter::class.java) val jsonAdapter = moshi.adapter(GetterAndSetter::class.java)
val encoded = GetterAndSetter(3, 5) val encoded = GetterAndSetter(3, 5)
@@ -556,7 +554,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun nonPropertyConstructorParameter() { @Ignore @Test fun nonPropertyConstructorParameter() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
try { try {
moshi.adapter(NonPropertyConstructorParameter::class.java) moshi.adapter(NonPropertyConstructorParameter::class.java)
fail() fail()
@@ -570,7 +568,7 @@ class KotlinCodeGenTest {
class NonPropertyConstructorParameter(a: Int, val b: Int) class NonPropertyConstructorParameter(a: Int, val b: Int)
@Ignore @Test fun kotlinEnumsAreNotCovered() { @Ignore @Test fun kotlinEnumsAreNotCovered() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val adapter = moshi.adapter(UsingEnum::class.java) val adapter = moshi.adapter(UsingEnum::class.java)
assertThat(adapter.fromJson("""{"e": "A"}""")).isEqualTo(UsingEnum(KotlinEnum.A)) assertThat(adapter.fromJson("""{"e": "A"}""")).isEqualTo(UsingEnum(KotlinEnum.A))
@@ -583,7 +581,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun interfacesNotSupported() { @Ignore @Test fun interfacesNotSupported() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
try { try {
moshi.adapter(Interface::class.java) moshi.adapter(Interface::class.java)
fail() fail()
@@ -596,7 +594,7 @@ class KotlinCodeGenTest {
interface Interface interface Interface
@Ignore @Test fun abstractClassesNotSupported() { @Ignore @Test fun abstractClassesNotSupported() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
try { try {
moshi.adapter(AbstractClass::class.java) moshi.adapter(AbstractClass::class.java)
fail() fail()
@@ -609,7 +607,7 @@ class KotlinCodeGenTest {
abstract class AbstractClass(val a: Int) abstract class AbstractClass(val a: Int)
@Ignore @Test fun innerClassesNotSupported() { @Ignore @Test fun innerClassesNotSupported() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
try { try {
moshi.adapter(InnerClass::class.java) moshi.adapter(InnerClass::class.java)
fail() fail()
@@ -623,7 +621,7 @@ class KotlinCodeGenTest {
@Ignore @Test fun localClassesNotSupported() { @Ignore @Test fun localClassesNotSupported() {
class LocalClass(val a: Int) class LocalClass(val a: Int)
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
try { try {
moshi.adapter(LocalClass::class.java) moshi.adapter(LocalClass::class.java)
fail() fail()
@@ -634,7 +632,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun objectDeclarationsNotSupported() { @Ignore @Test fun objectDeclarationsNotSupported() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
try { try {
moshi.adapter(ObjectDeclaration.javaClass) moshi.adapter(ObjectDeclaration.javaClass)
fail() fail()
@@ -652,7 +650,7 @@ class KotlinCodeGenTest {
val expression = object : Any() { val expression = object : Any() {
var a = 5 var a = 5
} }
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
try { try {
moshi.adapter(expression.javaClass) moshi.adapter(expression.javaClass)
fail() fail()
@@ -663,7 +661,7 @@ class KotlinCodeGenTest {
} }
@Ignore @Test fun manyProperties32() { @Ignore @Test fun manyProperties32() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(ManyProperties32::class.java) val jsonAdapter = moshi.adapter(ManyProperties32::class.java)
val encoded = ManyProperties32( val encoded = ManyProperties32(
@@ -703,7 +701,7 @@ class KotlinCodeGenTest {
var v31: Int, var v32: Int) var v31: Int, var v32: Int)
@Ignore @Test fun manyProperties33() { @Ignore @Test fun manyProperties33() {
val moshi = Moshi.Builder().add(MoshiSerializableFactory()).build() val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(ManyProperties33::class.java) val jsonAdapter = moshi.adapter(ManyProperties33::class.java)
val encoded = ManyProperties33( val encoded = ManyProperties33(

View File

@@ -1,82 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.squareup.moshi</groupId>
<artifactId>moshi-parent</artifactId>
<version>1.6.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>moshi-kotlin-codegen-runtime</artifactId>
<dependencies>
<dependency>
<groupId>com.squareup.moshi</groupId>
<artifactId>moshi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -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<Annotation>, 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<out JsonAdapter<*>>
if (type is ParameterizedType) {
// This is generic, use the two param moshi + type constructor
bindingClass.getDeclaredConstructor(Moshi::class.java, Array<Type>::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)
}
}
}

View File

@@ -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.
*
* <p>This annotation is currently only permitted on declarations of data classes in Kotlin.
*/
@Retention(RUNTIME)
@Documented
public @interface JsonClass {
boolean generateAdapter();
}

View File

@@ -18,6 +18,9 @@ package com.squareup.moshi;
import com.squareup.moshi.internal.Util; import com.squareup.moshi.internal.Util;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Annotation; 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.lang.reflect.Type;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@@ -53,6 +56,12 @@ final class StandardJsonAdapters {
if (type == Object.class) return new ObjectJsonAdapter(moshi).nullSafe(); if (type == Object.class) return new ObjectJsonAdapter(moshi).nullSafe();
Class<?> rawType = Types.getRawType(type); Class<?> rawType = Types.getRawType(type);
JsonClass jsonClass = rawType.getAnnotation(JsonClass.class);
if (jsonClass != null && jsonClass.generateAdapter()) {
return generatedAdapter(moshi, type, rawType);
}
if (rawType.isEnum()) { if (rawType.isEnum()) {
//noinspection unchecked //noinspection unchecked
return new EnumJsonAdapter<>((Class<? extends Enum>) rawType).nullSafe(); return new EnumJsonAdapter<>((Class<? extends Enum>) 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<? extends JsonAdapter<?>> adapterClass = (Class<? extends JsonAdapter<?>>)
Class.forName(adapterClassName, true, rawType.getClassLoader());
if (type instanceof ParameterizedType) {
Constructor<? extends JsonAdapter<?>> constructor
= adapterClass.getDeclaredConstructor(Moshi.class, Type[].class);
constructor.setAccessible(true);
return constructor.newInstance(moshi, ((ParameterizedType) type).getActualTypeArguments());
} else {
Constructor<? extends JsonAdapter<?>> 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<T extends Enum<T>> extends JsonAdapter<T> { static final class EnumJsonAdapter<T extends Enum<T>> extends JsonAdapter<T> {
private final Class<T> enumType; private final Class<T> enumType;
private final String[] nameStrings; private final String[] nameStrings;

View File

@@ -24,7 +24,6 @@
<module>kotlin</module> <module>kotlin</module>
<module>kotlin-codegen/compiler</module> <module>kotlin-codegen/compiler</module>
<module>kotlin-codegen/integration-test</module> <module>kotlin-codegen/integration-test</module>
<module>kotlin-codegen/runtime</module>
</modules> </modules>
<properties> <properties>