Use functions in Util where possible (#1444)

This commit is contained in:
Jesse Wilson
2021-12-04 17:22:12 -05:00
committed by GitHub
parent 81bf3b1835
commit 75abba37ef
7 changed files with 94 additions and 110 deletions

View File

@@ -15,7 +15,8 @@
*/ */
package com.squareup.moshi.adapters; package com.squareup.moshi.adapters;
import com.squareup.moshi.Json; import static com.squareup.moshi.internal.Util.jsonName;
import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonDataException; import com.squareup.moshi.JsonDataException;
import com.squareup.moshi.JsonReader; import com.squareup.moshi.JsonReader;
@@ -67,12 +68,7 @@ public final class EnumJsonAdapter<T extends Enum<T>> extends JsonAdapter<T> {
nameStrings = new String[constants.length]; nameStrings = new String[constants.length];
for (int i = 0; i < constants.length; i++) { for (int i = 0; i < constants.length; i++) {
String constantName = constants[i].name(); String constantName = constants[i].name();
Json annotation = enumType.getField(constantName).getAnnotation(Json.class); nameStrings[i] = jsonName(constantName, enumType.getField(constantName));
String name =
annotation != null && !Json.UNSET_NAME.equals(annotation.name())
? annotation.name()
: constantName;
nameStrings[i] = name;
} }
options = JsonReader.Options.of(nameStrings); options = JsonReader.Options.of(nameStrings);
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {

View File

@@ -17,13 +17,13 @@ package com.squareup.moshi.recipes;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.squareup.moshi.Json;
import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier; import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.JsonReader; import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter; import com.squareup.moshi.JsonWriter;
import com.squareup.moshi.Moshi; import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types; import com.squareup.moshi.Types;
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.annotation.Retention; import java.lang.annotation.Retention;
@@ -77,13 +77,8 @@ final class FallbackEnum {
constants = enumType.getEnumConstants(); constants = enumType.getEnumConstants();
nameStrings = new String[constants.length]; nameStrings = new String[constants.length];
for (int i = 0; i < constants.length; i++) { for (int i = 0; i < constants.length; i++) {
T constant = constants[i]; String constantName = constants[i].name();
Json annotation = enumType.getField(constant.name()).getAnnotation(Json.class); nameStrings[i] = Util.jsonName(constantName, enumType.getField(constantName));
String name =
annotation != null && !Json.UNSET_NAME.equals(annotation.name())
? annotation.name()
: constant.name();
nameStrings[i] = name;
} }
options = JsonReader.Options.of(nameStrings); options = JsonReader.Options.of(nameStrings);
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {

View File

@@ -135,7 +135,7 @@ internal class KotlinJsonAdapter<T>(
for (binding in allBindings) { for (binding in allBindings) {
if (binding == null) continue // Skip constructor parameters that aren't properties. if (binding == null) continue // Skip constructor parameters that aren't properties.
writer.name(binding.name) writer.name(binding.jsonName)
binding.adapter.toJson(writer, binding.get(value)) binding.adapter.toJson(writer, binding.get(value))
} }
writer.endObject() writer.endObject()
@@ -144,8 +144,7 @@ internal class KotlinJsonAdapter<T>(
override fun toString() = "KotlinJsonAdapter(${constructor.returnType})" override fun toString() = "KotlinJsonAdapter(${constructor.returnType})"
data class Binding<K, P>( data class Binding<K, P>(
val name: String, val jsonName: String,
val jsonName: String?,
val adapter: JsonAdapter<P>, val adapter: JsonAdapter<P>,
val property: KProperty1<K, P>, val property: KProperty1<K, P>,
val parameter: KParameter?, val parameter: KParameter?,
@@ -264,7 +263,7 @@ public class KotlinJsonAdapterFactory : JsonAdapter.Factory {
if (property !is KMutableProperty1 && parameter == null) continue if (property !is KMutableProperty1 && parameter == null) continue
val name = jsonAnnotation?.name?.takeUnless { it == Json.UNSET_NAME } ?: property.name val jsonName = jsonAnnotation?.name?.takeUnless { it == Json.UNSET_NAME } ?: property.name
val propertyType = when (val propertyTypeClassifier = property.returnType.classifier) { val propertyType = when (val propertyTypeClassifier = property.returnType.classifier) {
is KClass<*> -> { is KClass<*> -> {
if (propertyTypeClassifier.isValue) { if (propertyTypeClassifier.isValue) {
@@ -298,8 +297,7 @@ public class KotlinJsonAdapterFactory : JsonAdapter.Factory {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
bindingsByName[property.name] = KotlinJsonAdapter.Binding( bindingsByName[property.name] = KotlinJsonAdapter.Binding(
name, jsonName,
jsonAnnotation?.name?.takeUnless { it == Json.UNSET_NAME } ?: name,
adapter, adapter,
property as KProperty1<Any, Any?>, property as KProperty1<Any, Any?>,
parameter, parameter,
@@ -323,7 +321,7 @@ public class KotlinJsonAdapterFactory : JsonAdapter.Factory {
} }
val nonIgnoredBindings = bindings.filterNotNull() val nonIgnoredBindings = bindings.filterNotNull()
val options = JsonReader.Options.of(*nonIgnoredBindings.map { it.name }.toTypedArray()) val options = JsonReader.Options.of(*nonIgnoredBindings.map { it.jsonName }.toTypedArray())
return KotlinJsonAdapter(constructor, bindings, nonIgnoredBindings, options).nullSafe() return KotlinJsonAdapter(constructor, bindings, nonIgnoredBindings, options).nullSafe()
} }
} }

View File

@@ -15,6 +15,7 @@
*/ */
package com.squareup.moshi; package com.squareup.moshi;
import static com.squareup.moshi.internal.Util.jsonName;
import static com.squareup.moshi.internal.Util.resolve; import static com.squareup.moshi.internal.Util.resolve;
import com.squareup.moshi.internal.Util; import com.squareup.moshi.internal.Util;
@@ -147,12 +148,9 @@ final class ClassJsonAdapter<T> extends JsonAdapter<T> {
field.setAccessible(true); field.setAccessible(true);
// Store it using the field's name. If there was already a field with this name, fail! // Store it using the field's name. If there was already a field with this name, fail!
String name = String jsonName = jsonName(fieldName, jsonAnnotation);
jsonAnnotation != null && !Json.UNSET_NAME.equals(jsonAnnotation.name()) FieldBinding<Object> fieldBinding = new FieldBinding<>(jsonName, field, adapter);
? jsonAnnotation.name() FieldBinding<?> replaced = fieldBindings.put(jsonName, fieldBinding);
: fieldName;
FieldBinding<Object> fieldBinding = new FieldBinding<>(name, field, adapter);
FieldBinding<?> replaced = fieldBindings.put(name, fieldBinding);
if (replaced != null) { if (replaced != null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Conflicting fields:\n" "Conflicting fields:\n"

View File

@@ -274,12 +274,8 @@ final class StandardJsonAdapters {
nameStrings = new String[constants.length]; nameStrings = new String[constants.length];
for (int i = 0; i < constants.length; i++) { for (int i = 0; i < constants.length; i++) {
T constant = constants[i]; T constant = constants[i];
Json annotation = enumType.getField(constant.name()).getAnnotation(Json.class); String constantName = constant.name();
String name = nameStrings[i] = Util.jsonName(constantName, enumType.getField(constantName));
annotation != null && !Json.UNSET_NAME.equals(annotation.name())
? annotation.name()
: constant.name();
nameStrings[i] = name;
} }
options = JsonReader.Options.of(nameStrings); options = JsonReader.Options.of(nameStrings);
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {

View File

@@ -19,6 +19,7 @@ import static com.squareup.moshi.Types.arrayOf;
import static com.squareup.moshi.Types.subtypeOf; import static com.squareup.moshi.Types.subtypeOf;
import static com.squareup.moshi.Types.supertypeOf; import static com.squareup.moshi.Types.supertypeOf;
import com.squareup.moshi.Json;
import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonClass; import com.squareup.moshi.JsonClass;
import com.squareup.moshi.JsonDataException; import com.squareup.moshi.JsonDataException;
@@ -95,6 +96,16 @@ public final class Util {
private Util() {} private Util() {}
public static String jsonName(String declaredName, AnnotatedElement element) {
return jsonName(declaredName, element.getAnnotation(Json.class));
}
public static String jsonName(String declaredName, @Nullable Json annotation) {
if (annotation == null) return declaredName;
String annotationName = annotation.name();
return Json.UNSET_NAME.equals(annotationName) ? declaredName : annotationName;
}
public static boolean typesMatch(Type pattern, Type candidate) { public static boolean typesMatch(Type pattern, Type candidate) {
// TODO: permit raw types (like Set.class) to match non-raw candidates (like Set<Long>). // TODO: permit raw types (like Set.class) to match non-raw candidates (like Set<Long>).
return Types.equals(pattern, candidate); return Types.equals(pattern, candidate);

View File

@@ -15,6 +15,7 @@
*/ */
package com.squareup.moshi; package com.squareup.moshi;
import static com.squareup.moshi.internal.Util.rethrowCause;
import static java.lang.invoke.MethodType.methodType; import static java.lang.invoke.MethodType.methodType;
import com.squareup.moshi.internal.Util; import com.squareup.moshi.internal.Util;
@@ -25,9 +26,8 @@ import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.RecordComponent; import java.lang.reflect.RecordComponent;
import java.util.Collections; import java.lang.reflect.Type;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -39,80 +39,81 @@ import java.util.Set;
final class RecordJsonAdapter<T> extends JsonAdapter<T> { final class RecordJsonAdapter<T> extends JsonAdapter<T> {
static final JsonAdapter.Factory FACTORY = static final JsonAdapter.Factory FACTORY =
(type, annotations, moshi) -> { new Factory() {
if (!annotations.isEmpty()) { @Override
return null; public JsonAdapter<?> create(
} Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (!annotations.isEmpty()) {
return null;
}
if (!(type instanceof Class) && !(type instanceof ParameterizedType)) { if (!(type instanceof Class) && !(type instanceof ParameterizedType)) {
return null; return null;
} }
var rawType = Types.getRawType(type); var rawType = Types.getRawType(type);
if (!rawType.isRecord()) { if (!rawType.isRecord()) {
return null; return null;
} }
var components = rawType.getRecordComponents(); var components = rawType.getRecordComponents();
var bindings = new LinkedHashMap<String, ComponentBinding<?>>(); var bindings = new LinkedHashMap<String, ComponentBinding<?>>();
var componentRawTypes = new Class<?>[components.length]; var componentRawTypes = new Class<?>[components.length];
var lookup = MethodHandles.lookup(); var lookup = MethodHandles.lookup();
for (int i = 0, componentsLength = components.length; i < componentsLength; i++) { for (int i = 0, componentsLength = components.length; i < componentsLength; i++) {
RecordComponent component = components[i]; RecordComponent component = components[i];
componentRawTypes[i] = component.getType(); componentRawTypes[i] = component.getType();
var name = component.getName(); ComponentBinding<Object> componentBinding =
var componentType = Util.resolve(type, rawType, component.getGenericType()); createComponentBinding(type, rawType, moshi, lookup, component);
var jsonName = name; var replaced = bindings.put(componentBinding.jsonName, componentBinding);
Set<Annotation> qualifiers = null; if (replaced != null) {
for (var annotation : component.getDeclaredAnnotations()) { throw new IllegalArgumentException(
if (annotation instanceof Json jsonAnnotation) { "Conflicting components:\n"
var annotationName = jsonAnnotation.name(); + " "
if (!Json.UNSET_NAME.equals(annotationName)) { + replaced.componentName
jsonName = jsonAnnotation.name(); + "\n"
} + " "
} else { + componentBinding.componentName);
if (annotation.annotationType().isAnnotationPresent(JsonQualifier.class)) {
if (qualifiers == null) {
qualifiers = new LinkedHashSet<>();
}
qualifiers.add(annotation);
}
} }
} }
if (qualifiers == null) {
qualifiers = Collections.emptySet(); MethodHandle constructor;
try {
constructor =
lookup.findConstructor(rawType, methodType(void.class, componentRawTypes));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new AssertionError(e);
} }
return new RecordJsonAdapter<>(constructor, rawType.getSimpleName(), bindings).nullSafe();
}
private static ComponentBinding<Object> createComponentBinding(
Type type,
Class<?> rawType,
Moshi moshi,
MethodHandles.Lookup lookup,
RecordComponent component) {
var componentName = component.getName();
var jsonName = Util.jsonName(componentName, component);
var componentType = Util.resolve(type, rawType, component.getGenericType());
Set<? extends Annotation> qualifiers = Util.jsonAnnotations(component);
var adapter = moshi.adapter(componentType, qualifiers); var adapter = moshi.adapter(componentType, qualifiers);
MethodHandle accessor; MethodHandle accessor;
try { try {
accessor = lookup.unreflect(component.getAccessor()); accessor = lookup.unreflect(component.getAccessor());
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }
var componentBinding = new ComponentBinding<>(name, jsonName, adapter, accessor);
var replaced = bindings.put(jsonName, componentBinding);
if (replaced != null) {
throw new IllegalArgumentException(
"Conflicting components:\n"
+ " "
+ replaced.name
+ "\n"
+ " "
+ componentBinding.name);
}
}
MethodHandle constructor; return new ComponentBinding<>(componentName, jsonName, adapter, accessor);
try {
constructor = lookup.findConstructor(rawType, methodType(void.class, componentRawTypes));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new AssertionError(e);
} }
return new RecordJsonAdapter<>(constructor, rawType.getSimpleName(), bindings).nullSafe();
}; };
private static record ComponentBinding<T>( private static record ComponentBinding<T>(
String name, String jsonName, JsonAdapter<T> adapter, MethodHandle accessor) {} String componentName, String jsonName, JsonAdapter<T> adapter, MethodHandle accessor) {}
private final String targetClass; private final String targetClass;
private final MethodHandle constructor; private final MethodHandle constructor;
@@ -146,23 +147,17 @@ final class RecordJsonAdapter<T> extends JsonAdapter<T> {
reader.skipValue(); reader.skipValue();
continue; continue;
} }
var result = componentBindingsArray[index].adapter.fromJson(reader); resultsArray[index] = componentBindingsArray[index].adapter.fromJson(reader);
resultsArray[index] = result;
} }
reader.endObject(); reader.endObject();
try { try {
//noinspection unchecked //noinspection unchecked
return (T) constructor.invokeWithArguments(resultsArray); return (T) constructor.invokeWithArguments(resultsArray);
} catch (InvocationTargetException e) {
throw rethrowCause(e);
} catch (Throwable e) { } catch (Throwable e) {
if (e instanceof InvocationTargetException ite) { throw new AssertionError(e);
Throwable cause = ite.getCause();
if (cause instanceof RuntimeException) throw (RuntimeException) cause;
if (cause instanceof Error) throw (Error) cause;
throw new RuntimeException(cause);
} else {
throw new AssertionError(e);
}
} }
} }
@@ -175,15 +170,10 @@ final class RecordJsonAdapter<T> extends JsonAdapter<T> {
Object componentValue; Object componentValue;
try { try {
componentValue = binding.accessor.invoke(value); componentValue = binding.accessor.invoke(value);
} catch (InvocationTargetException e) {
throw Util.rethrowCause(e);
} catch (Throwable e) { } catch (Throwable e) {
if (e instanceof InvocationTargetException ite) { throw new AssertionError(e);
Throwable cause = ite.getCause();
if (cause instanceof RuntimeException) throw (RuntimeException) cause;
if (cause instanceof Error) throw (Error) cause;
throw new RuntimeException(cause);
} else {
throw new AssertionError(e);
}
} }
binding.adapter.toJson(writer, componentValue); binding.adapter.toJson(writer, componentValue);
} }