Skip to content

Commit

Permalink
Use functions in Util where possible (#1444)
Browse files Browse the repository at this point in the history
  • Loading branch information
swankjesse committed Dec 4, 2021
1 parent 81bf3b1 commit 75abba3
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 110 deletions.
Expand Up @@ -15,7 +15,8 @@
*/
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.JsonDataException;
import com.squareup.moshi.JsonReader;
Expand Down Expand Up @@ -67,12 +68,7 @@ public EnumJsonAdapter<T> withUnknownFallback(@Nullable T fallbackValue) {
nameStrings = new String[constants.length];
for (int i = 0; i < constants.length; i++) {
String constantName = constants[i].name();
Json annotation = enumType.getField(constantName).getAnnotation(Json.class);
String name =
annotation != null && !Json.UNSET_NAME.equals(annotation.name())
? annotation.name()
: constantName;
nameStrings[i] = name;
nameStrings[i] = jsonName(constantName, enumType.getField(constantName));
}
options = JsonReader.Options.of(nameStrings);
} catch (NoSuchFieldException e) {
Expand Down
Expand Up @@ -17,13 +17,13 @@

import static java.lang.annotation.RetentionPolicy.RUNTIME;

import com.squareup.moshi.Json;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import com.squareup.moshi.internal.Util;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
Expand Down Expand Up @@ -77,13 +77,8 @@ public JsonAdapter<?> create(
constants = enumType.getEnumConstants();
nameStrings = new String[constants.length];
for (int i = 0; i < constants.length; i++) {
T constant = constants[i];
Json annotation = enumType.getField(constant.name()).getAnnotation(Json.class);
String name =
annotation != null && !Json.UNSET_NAME.equals(annotation.name())
? annotation.name()
: constant.name();
nameStrings[i] = name;
String constantName = constants[i].name();
nameStrings[i] = Util.jsonName(constantName, enumType.getField(constantName));
}
options = JsonReader.Options.of(nameStrings);
} catch (NoSuchFieldException e) {
Expand Down
Expand Up @@ -135,7 +135,7 @@ internal class KotlinJsonAdapter<T>(
for (binding in allBindings) {
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))
}
writer.endObject()
Expand All @@ -144,8 +144,7 @@ internal class KotlinJsonAdapter<T>(
override fun toString() = "KotlinJsonAdapter(${constructor.returnType})"

data class Binding<K, P>(
val name: String,
val jsonName: String?,
val jsonName: String,
val adapter: JsonAdapter<P>,
val property: KProperty1<K, P>,
val parameter: KParameter?,
Expand Down Expand Up @@ -264,7 +263,7 @@ public class KotlinJsonAdapterFactory : JsonAdapter.Factory {

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) {
is KClass<*> -> {
if (propertyTypeClassifier.isValue) {
Expand Down Expand Up @@ -298,8 +297,7 @@ public class KotlinJsonAdapterFactory : JsonAdapter.Factory {

@Suppress("UNCHECKED_CAST")
bindingsByName[property.name] = KotlinJsonAdapter.Binding(
name,
jsonAnnotation?.name?.takeUnless { it == Json.UNSET_NAME } ?: name,
jsonName,
adapter,
property as KProperty1<Any, Any?>,
parameter,
Expand All @@ -323,7 +321,7 @@ public class KotlinJsonAdapterFactory : JsonAdapter.Factory {
}

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()
}
}
10 changes: 4 additions & 6 deletions moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java
Expand Up @@ -15,6 +15,7 @@
*/
package com.squareup.moshi;

import static com.squareup.moshi.internal.Util.jsonName;
import static com.squareup.moshi.internal.Util.resolve;

import com.squareup.moshi.internal.Util;
Expand Down Expand Up @@ -147,12 +148,9 @@ private void createFieldBindings(
field.setAccessible(true);

// Store it using the field's name. If there was already a field with this name, fail!
String name =
jsonAnnotation != null && !Json.UNSET_NAME.equals(jsonAnnotation.name())
? jsonAnnotation.name()
: fieldName;
FieldBinding<Object> fieldBinding = new FieldBinding<>(name, field, adapter);
FieldBinding<?> replaced = fieldBindings.put(name, fieldBinding);
String jsonName = jsonName(fieldName, jsonAnnotation);
FieldBinding<Object> fieldBinding = new FieldBinding<>(jsonName, field, adapter);
FieldBinding<?> replaced = fieldBindings.put(jsonName, fieldBinding);
if (replaced != null) {
throw new IllegalArgumentException(
"Conflicting fields:\n"
Expand Down
Expand Up @@ -274,12 +274,8 @@ static final class EnumJsonAdapter<T extends Enum<T>> extends JsonAdapter<T> {
nameStrings = new String[constants.length];
for (int i = 0; i < constants.length; i++) {
T constant = constants[i];
Json annotation = enumType.getField(constant.name()).getAnnotation(Json.class);
String name =
annotation != null && !Json.UNSET_NAME.equals(annotation.name())
? annotation.name()
: constant.name();
nameStrings[i] = name;
String constantName = constant.name();
nameStrings[i] = Util.jsonName(constantName, enumType.getField(constantName));
}
options = JsonReader.Options.of(nameStrings);
} catch (NoSuchFieldException e) {
Expand Down
11 changes: 11 additions & 0 deletions moshi/src/main/java/com/squareup/moshi/internal/Util.java
Expand Up @@ -19,6 +19,7 @@
import static com.squareup.moshi.Types.subtypeOf;
import static com.squareup.moshi.Types.supertypeOf;

import com.squareup.moshi.Json;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonClass;
import com.squareup.moshi.JsonDataException;
Expand Down Expand Up @@ -95,6 +96,16 @@ private static String getKotlinMetadataClassName() {

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) {
// TODO: permit raw types (like Set.class) to match non-raw candidates (like Set<Long>).
return Types.equals(pattern, candidate);
Expand Down
142 changes: 66 additions & 76 deletions moshi/src/main/java16/com/squareup/moshi/RecordJsonAdapter.java
Expand Up @@ -15,6 +15,7 @@
*/
package com.squareup.moshi;

import static com.squareup.moshi.internal.Util.rethrowCause;
import static java.lang.invoke.MethodType.methodType;

import com.squareup.moshi.internal.Util;
Expand All @@ -25,9 +26,8 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.RecordComponent;
import java.util.Collections;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

Expand All @@ -39,80 +39,81 @@
final class RecordJsonAdapter<T> extends JsonAdapter<T> {

static final JsonAdapter.Factory FACTORY =
(type, annotations, moshi) -> {
if (!annotations.isEmpty()) {
return null;
}
new Factory() {
@Override
public JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (!annotations.isEmpty()) {
return null;
}

if (!(type instanceof Class) && !(type instanceof ParameterizedType)) {
return null;
}
if (!(type instanceof Class) && !(type instanceof ParameterizedType)) {
return null;
}

var rawType = Types.getRawType(type);
if (!rawType.isRecord()) {
return null;
}
var rawType = Types.getRawType(type);
if (!rawType.isRecord()) {
return null;
}

var components = rawType.getRecordComponents();
var bindings = new LinkedHashMap<String, ComponentBinding<?>>();
var componentRawTypes = new Class<?>[components.length];
var lookup = MethodHandles.lookup();
for (int i = 0, componentsLength = components.length; i < componentsLength; i++) {
RecordComponent component = components[i];
componentRawTypes[i] = component.getType();
var name = component.getName();
var componentType = Util.resolve(type, rawType, component.getGenericType());
var jsonName = name;
Set<Annotation> qualifiers = null;
for (var annotation : component.getDeclaredAnnotations()) {
if (annotation instanceof Json jsonAnnotation) {
var annotationName = jsonAnnotation.name();
if (!Json.UNSET_NAME.equals(annotationName)) {
jsonName = jsonAnnotation.name();
}
} else {
if (annotation.annotationType().isAnnotationPresent(JsonQualifier.class)) {
if (qualifiers == null) {
qualifiers = new LinkedHashSet<>();
}
qualifiers.add(annotation);
}
var components = rawType.getRecordComponents();
var bindings = new LinkedHashMap<String, ComponentBinding<?>>();
var componentRawTypes = new Class<?>[components.length];
var lookup = MethodHandles.lookup();
for (int i = 0, componentsLength = components.length; i < componentsLength; i++) {
RecordComponent component = components[i];
componentRawTypes[i] = component.getType();
ComponentBinding<Object> componentBinding =
createComponentBinding(type, rawType, moshi, lookup, component);
var replaced = bindings.put(componentBinding.jsonName, componentBinding);
if (replaced != null) {
throw new IllegalArgumentException(
"Conflicting components:\n"
+ " "
+ replaced.componentName
+ "\n"
+ " "
+ componentBinding.componentName);
}
}
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);

MethodHandle accessor;
try {
accessor = lookup.unreflect(component.getAccessor());
} catch (IllegalAccessException 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;
try {
constructor = lookup.findConstructor(rawType, methodType(void.class, componentRawTypes));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new AssertionError(e);
return new ComponentBinding<>(componentName, jsonName, adapter, accessor);
}
return new RecordJsonAdapter<>(constructor, rawType.getSimpleName(), bindings).nullSafe();
};

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 MethodHandle constructor;
Expand Down Expand Up @@ -146,23 +147,17 @@ public T fromJson(JsonReader reader) throws IOException {
reader.skipValue();
continue;
}
var result = componentBindingsArray[index].adapter.fromJson(reader);
resultsArray[index] = result;
resultsArray[index] = componentBindingsArray[index].adapter.fromJson(reader);
}
reader.endObject();

try {
//noinspection unchecked
return (T) constructor.invokeWithArguments(resultsArray);
} catch (InvocationTargetException e) {
throw rethrowCause(e);
} catch (Throwable e) {
if (e instanceof InvocationTargetException ite) {
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);
}
throw new AssertionError(e);
}
}

Expand All @@ -175,15 +170,10 @@ public void toJson(JsonWriter writer, T value) throws IOException {
Object componentValue;
try {
componentValue = binding.accessor.invoke(value);
} catch (InvocationTargetException e) {
throw Util.rethrowCause(e);
} catch (Throwable e) {
if (e instanceof InvocationTargetException ite) {
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);
}
throw new AssertionError(e);
}
binding.adapter.toJson(writer, componentValue);
}
Expand Down

0 comments on commit 75abba3

Please sign in to comment.