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

@@ -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 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<? 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> {
private final Class<T> enumType;
private final String[] nameStrings;