Switch to spotless and format code (#1196)

* Add spotless configuration

* Reformat!

* Add copyright config for build.gradle.kts files

* Add toeholds for headers
This commit is contained in:
Zac Sweers
2020-08-27 23:40:15 -04:00
committed by GitHub
parent 701d6ba968
commit 538890e8c0
109 changed files with 6748 additions and 4972 deletions

View File

@@ -15,6 +15,10 @@
*/
package com.squareup.moshi;
import static com.squareup.moshi.internal.Util.canonicalize;
import static com.squareup.moshi.internal.Util.jsonAnnotations;
import static com.squareup.moshi.internal.Util.typeAnnotatedWithAnnotations;
import com.squareup.moshi.internal.Util;
import java.io.IOException;
import java.lang.annotation.Annotation;
@@ -27,10 +31,6 @@ import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import static com.squareup.moshi.internal.Util.canonicalize;
import static com.squareup.moshi.internal.Util.jsonAnnotations;
import static com.squareup.moshi.internal.Util.typeAnnotatedWithAnnotations;
final class AdapterMethodsFactory implements JsonAdapter.Factory {
private final List<AdapterMethod> toAdapters;
private final List<AdapterMethod> fromAdapters;
@@ -40,7 +40,8 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
this.fromAdapters = fromAdapters;
}
@Override public @Nullable JsonAdapter<?> create(
@Override
public @Nullable JsonAdapter<?> create(
final Type type, final Set<? extends Annotation> annotations, final Moshi moshi) {
final AdapterMethod toAdapter = get(toAdapters, type, annotations);
final AdapterMethod fromAdapter = get(fromAdapters, type, annotations);
@@ -52,8 +53,12 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
delegate = moshi.nextAdapter(this, type, annotations);
} catch (IllegalArgumentException e) {
String missingAnnotation = toAdapter == null ? "@ToJson" : "@FromJson";
throw new IllegalArgumentException("No " + missingAnnotation + " adapter for "
+ typeAnnotatedWithAnnotations(type, annotations), e);
throw new IllegalArgumentException(
"No "
+ missingAnnotation
+ " adapter for "
+ typeAnnotatedWithAnnotations(type, annotations),
e);
}
} else {
delegate = null;
@@ -63,7 +68,8 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
if (fromAdapter != null) fromAdapter.bind(moshi, this);
return new JsonAdapter<Object>() {
@Override public void toJson(JsonWriter writer, @Nullable Object value) throws IOException {
@Override
public void toJson(JsonWriter writer, @Nullable Object value) throws IOException {
if (toAdapter == null) {
delegate.toJson(writer, value);
} else if (!toAdapter.nullable && value == null) {
@@ -79,7 +85,8 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
}
}
@Override public @Nullable Object fromJson(JsonReader reader) throws IOException {
@Override
public @Nullable Object fromJson(JsonReader reader) throws IOException {
if (fromAdapter == null) {
return delegate.fromJson(reader);
} else if (!fromAdapter.nullable && reader.peek() == JsonReader.Token.NULL) {
@@ -96,7 +103,8 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
}
}
@Override public String toString() {
@Override
public String toString() {
return "JsonAdapter" + annotations + "(" + type + ")";
}
};
@@ -112,9 +120,13 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
AdapterMethod toAdapter = toAdapter(adapter, m);
AdapterMethod conflicting = get(toAdapters, toAdapter.type, toAdapter.annotations);
if (conflicting != null) {
throw new IllegalArgumentException("Conflicting @ToJson methods:\n"
+ " " + conflicting.method + "\n"
+ " " + toAdapter.method);
throw new IllegalArgumentException(
"Conflicting @ToJson methods:\n"
+ " "
+ conflicting.method
+ "\n"
+ " "
+ toAdapter.method);
}
toAdapters.add(toAdapter);
}
@@ -123,9 +135,13 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
AdapterMethod fromAdapter = fromAdapter(adapter, m);
AdapterMethod conflicting = get(fromAdapters, fromAdapter.type, fromAdapter.annotations);
if (conflicting != null) {
throw new IllegalArgumentException("Conflicting @FromJson methods:\n"
+ " " + conflicting.method + "\n"
+ " " + fromAdapter.method);
throw new IllegalArgumentException(
"Conflicting @FromJson methods:\n"
+ " "
+ conflicting.method
+ "\n"
+ " "
+ fromAdapter.method);
}
fromAdapters.add(fromAdapter);
}
@@ -133,8 +149,8 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
}
if (toAdapters.isEmpty() && fromAdapters.isEmpty()) {
throw new IllegalArgumentException("Expected at least one @ToJson or @FromJson method on "
+ adapter.getClass().getName());
throw new IllegalArgumentException(
"Expected at least one @ToJson or @FromJson method on " + adapter.getClass().getName());
}
return new AdapterMethodsFactory(toAdapters, fromAdapters);
@@ -157,9 +173,16 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
// void pointToJson(JsonWriter jsonWriter, Point point) {
// void pointToJson(JsonWriter jsonWriter, Point point, JsonAdapter<?> adapter, ...) {
Set<? extends Annotation> qualifierAnnotations = jsonAnnotations(parameterAnnotations[1]);
return new AdapterMethod(parameterTypes[1], qualifierAnnotations, adapter, method,
parameterTypes.length, 2, true) {
@Override public void toJson(Moshi moshi, JsonWriter writer, @Nullable Object value)
return new AdapterMethod(
parameterTypes[1],
qualifierAnnotations,
adapter,
method,
parameterTypes.length,
2,
true) {
@Override
public void toJson(Moshi moshi, JsonWriter writer, @Nullable Object value)
throws IOException, InvocationTargetException {
invoke(writer, value);
}
@@ -171,19 +194,28 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
final Set<? extends Annotation> qualifierAnnotations =
jsonAnnotations(parameterAnnotations[0]);
boolean nullable = Util.hasNullable(parameterAnnotations[0]);
return new AdapterMethod(parameterTypes[0], qualifierAnnotations, adapter, method,
parameterTypes.length, 1, nullable) {
return new AdapterMethod(
parameterTypes[0],
qualifierAnnotations,
adapter,
method,
parameterTypes.length,
1,
nullable) {
private JsonAdapter<Object> delegate;
@Override public void bind(Moshi moshi, JsonAdapter.Factory factory) {
@Override
public void bind(Moshi moshi, JsonAdapter.Factory factory) {
super.bind(moshi, factory);
delegate = Types.equals(parameterTypes[0], returnType)
&& qualifierAnnotations.equals(returnTypeAnnotations)
? moshi.nextAdapter(factory, returnType, returnTypeAnnotations)
: moshi.adapter(returnType, returnTypeAnnotations);
delegate =
Types.equals(parameterTypes[0], returnType)
&& qualifierAnnotations.equals(returnTypeAnnotations)
? moshi.nextAdapter(factory, returnType, returnTypeAnnotations)
: moshi.adapter(returnType, returnTypeAnnotations);
}
@Override public void toJson(Moshi moshi, JsonWriter writer, @Nullable Object value)
@Override
public void toJson(Moshi moshi, JsonWriter writer, @Nullable Object value)
throws IOException, InvocationTargetException {
Object intermediate = invoke(value);
delegate.toJson(writer, intermediate);
@@ -191,12 +223,15 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
};
} else {
throw new IllegalArgumentException("Unexpected signature for " + method + ".\n"
+ "@ToJson method signatures may have one of the following structures:\n"
+ " <any access modifier> void toJson(JsonWriter writer, T value) throws <any>;\n"
+ " <any access modifier> void toJson(JsonWriter writer, T value,"
+ " JsonAdapter<any> delegate, <any more delegates>) throws <any>;\n"
+ " <any access modifier> R toJson(T value) throws <any>;\n");
throw new IllegalArgumentException(
"Unexpected signature for "
+ method
+ ".\n"
+ "@ToJson method signatures may have one of the following structures:\n"
+ " <any access modifier> void toJson(JsonWriter writer, T value) throws <any>;\n"
+ " <any access modifier> void toJson(JsonWriter writer, T value,"
+ " JsonAdapter<any> delegate, <any more delegates>) throws <any>;\n"
+ " <any access modifier> R toJson(T value) throws <any>;\n");
}
}
@@ -226,9 +261,10 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
&& parametersAreJsonAdapters(1, parameterTypes)) {
// Point pointFromJson(JsonReader jsonReader) {
// Point pointFromJson(JsonReader jsonReader, JsonAdapter<?> adapter, ...) {
return new AdapterMethod(returnType, returnTypeAnnotations, adapter, method,
parameterTypes.length, 1, true) {
@Override public Object fromJson(Moshi moshi, JsonReader reader)
return new AdapterMethod(
returnType, returnTypeAnnotations, adapter, method, parameterTypes.length, 1, true) {
@Override
public Object fromJson(Moshi moshi, JsonReader reader)
throws IOException, InvocationTargetException {
return invoke(reader);
}
@@ -236,22 +272,25 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
} else if (parameterTypes.length == 1 && returnType != void.class) {
// Point pointFromJson(List<Integer> o) {
final Set<? extends Annotation> qualifierAnnotations
= jsonAnnotations(parameterAnnotations[0]);
final Set<? extends Annotation> qualifierAnnotations =
jsonAnnotations(parameterAnnotations[0]);
boolean nullable = Util.hasNullable(parameterAnnotations[0]);
return new AdapterMethod(returnType, returnTypeAnnotations, adapter, method,
parameterTypes.length, 1, nullable) {
return new AdapterMethod(
returnType, returnTypeAnnotations, adapter, method, parameterTypes.length, 1, nullable) {
JsonAdapter<Object> delegate;
@Override public void bind(Moshi moshi, JsonAdapter.Factory factory) {
@Override
public void bind(Moshi moshi, JsonAdapter.Factory factory) {
super.bind(moshi, factory);
delegate = Types.equals(parameterTypes[0], returnType)
&& qualifierAnnotations.equals(returnTypeAnnotations)
? moshi.nextAdapter(factory, parameterTypes[0], qualifierAnnotations)
: moshi.adapter(parameterTypes[0], qualifierAnnotations);
delegate =
Types.equals(parameterTypes[0], returnType)
&& qualifierAnnotations.equals(returnTypeAnnotations)
? moshi.nextAdapter(factory, parameterTypes[0], qualifierAnnotations)
: moshi.adapter(parameterTypes[0], qualifierAnnotations);
}
@Override public Object fromJson(Moshi moshi, JsonReader reader)
@Override
public Object fromJson(Moshi moshi, JsonReader reader)
throws IOException, InvocationTargetException {
Object intermediate = delegate.fromJson(reader);
return invoke(intermediate);
@@ -259,12 +298,15 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
};
} else {
throw new IllegalArgumentException("Unexpected signature for " + method + ".\n"
+ "@FromJson method signatures may have one of the following structures:\n"
+ " <any access modifier> R fromJson(JsonReader jsonReader) throws <any>;\n"
+ " <any access modifier> R fromJson(JsonReader jsonReader,"
+ " JsonAdapter<any> delegate, <any more delegates>) throws <any>;\n"
+ " <any access modifier> R fromJson(T value) throws <any>;\n");
throw new IllegalArgumentException(
"Unexpected signature for "
+ method
+ ".\n"
+ "@FromJson method signatures may have one of the following structures:\n"
+ " <any access modifier> R fromJson(JsonReader jsonReader) throws <any>;\n"
+ " <any access modifier> R fromJson(JsonReader jsonReader,"
+ " JsonAdapter<any> delegate, <any more delegates>) throws <any>;\n"
+ " <any access modifier> R fromJson(T value) throws <any>;\n");
}
}
@@ -289,8 +331,14 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
final JsonAdapter<?>[] jsonAdapters;
final boolean nullable;
AdapterMethod(Type type, Set<? extends Annotation> annotations, Object adapter,
Method method, int parameterCount, int adaptersOffset, boolean nullable) {
AdapterMethod(
Type type,
Set<? extends Annotation> annotations,
Object adapter,
Method method,
int parameterCount,
int adaptersOffset,
boolean nullable) {
this.type = canonicalize(type);
this.annotations = annotations;
this.adapter = adapter;

View File

@@ -25,21 +25,23 @@ import java.util.Set;
import javax.annotation.Nullable;
/**
* Converts arrays to JSON arrays containing their converted contents. This
* supports both primitive and object arrays.
* Converts arrays to JSON arrays containing their converted contents. This supports both primitive
* and object arrays.
*/
final class ArrayJsonAdapter extends JsonAdapter<Object> {
public static final Factory FACTORY = new Factory() {
@Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Type elementType = Types.arrayComponentType(type);
if (elementType == null) return null;
if (!annotations.isEmpty()) return null;
Class<?> elementClass = Types.getRawType(elementType);
JsonAdapter<Object> elementAdapter = moshi.adapter(elementType);
return new ArrayJsonAdapter(elementClass, elementAdapter).nullSafe();
}
};
public static final Factory FACTORY =
new Factory() {
@Override
public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Type elementType = Types.arrayComponentType(type);
if (elementType == null) return null;
if (!annotations.isEmpty()) return null;
Class<?> elementClass = Types.getRawType(elementType);
JsonAdapter<Object> elementAdapter = moshi.adapter(elementType);
return new ArrayJsonAdapter(elementClass, elementAdapter).nullSafe();
}
};
private final Class<?> elementClass;
private final JsonAdapter<Object> elementAdapter;
@@ -49,7 +51,8 @@ final class ArrayJsonAdapter extends JsonAdapter<Object> {
this.elementAdapter = elementAdapter;
}
@Override public Object fromJson(JsonReader reader) throws IOException {
@Override
public Object fromJson(JsonReader reader) throws IOException {
List<Object> list = new ArrayList<>();
reader.beginArray();
while (reader.hasNext()) {
@@ -63,7 +66,8 @@ final class ArrayJsonAdapter extends JsonAdapter<Object> {
return array;
}
@Override public void toJson(JsonWriter writer, Object value) throws IOException {
@Override
public void toJson(JsonWriter writer, Object value) throws IOException {
writer.beginArray();
for (int i = 0, size = Array.getLength(value); i < size; i++) {
elementAdapter.toJson(writer, Array.get(value, i));
@@ -71,7 +75,8 @@ final class ArrayJsonAdapter extends JsonAdapter<Object> {
writer.endArray();
}
@Override public String toString() {
@Override
public String toString() {
return elementAdapter + ".array()";
}
}

View File

@@ -31,8 +31,8 @@ import java.lang.reflect.Method;
* @author Jesse Wilson
*/
abstract class ClassFactory<T> {
abstract T newInstance() throws
InvocationTargetException, IllegalAccessException, InstantiationException;
abstract T newInstance()
throws InvocationTargetException, IllegalAccessException, InstantiationException;
public static <T> ClassFactory<T> get(final Class<?> rawType) {
// Try to find a no-args constructor. May be any visibility including private.
@@ -41,12 +41,15 @@ abstract class ClassFactory<T> {
constructor.setAccessible(true);
return new ClassFactory<T>() {
@SuppressWarnings("unchecked") // T is the same raw type as is requested
@Override public T newInstance() throws IllegalAccessException, InvocationTargetException,
InstantiationException {
@Override
public T newInstance()
throws IllegalAccessException, InvocationTargetException, InstantiationException {
Object[] args = null;
return (T) constructor.newInstance(args);
}
@Override public String toString() {
@Override
public String toString() {
return rawType.getName();
}
};
@@ -66,10 +69,13 @@ abstract class ClassFactory<T> {
final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
return new ClassFactory<T>() {
@SuppressWarnings("unchecked")
@Override public T newInstance() throws InvocationTargetException, IllegalAccessException {
@Override
public T newInstance() throws InvocationTargetException, IllegalAccessException {
return (T) allocateInstance.invoke(unsafe, rawType);
}
@Override public String toString() {
@Override
public String toString() {
return rawType.getName();
}
};
@@ -85,19 +91,22 @@ abstract class ClassFactory<T> {
// private static native Object newInstance(Class<?> instantiationClass, int methodId);
// }
try {
Method getConstructorId = ObjectStreamClass.class.getDeclaredMethod(
"getConstructorId", Class.class);
Method getConstructorId =
ObjectStreamClass.class.getDeclaredMethod("getConstructorId", Class.class);
getConstructorId.setAccessible(true);
final int constructorId = (Integer) getConstructorId.invoke(null, Object.class);
final Method newInstance = ObjectStreamClass.class.getDeclaredMethod("newInstance",
Class.class, int.class);
final Method newInstance =
ObjectStreamClass.class.getDeclaredMethod("newInstance", Class.class, int.class);
newInstance.setAccessible(true);
return new ClassFactory<T>() {
@SuppressWarnings("unchecked")
@Override public T newInstance() throws InvocationTargetException, IllegalAccessException {
@Override
public T newInstance() throws InvocationTargetException, IllegalAccessException {
return (T) newInstance.invoke(null, rawType, constructorId);
}
@Override public String toString() {
@Override
public String toString() {
return rawType.getName();
}
};
@@ -115,15 +124,18 @@ abstract class ClassFactory<T> {
// Class<?> instantiationClass, Class<?> constructorClass);
// }
try {
final Method newInstance = ObjectInputStream.class.getDeclaredMethod(
"newInstance", Class.class, Class.class);
final Method newInstance =
ObjectInputStream.class.getDeclaredMethod("newInstance", Class.class, Class.class);
newInstance.setAccessible(true);
return new ClassFactory<T>() {
@SuppressWarnings("unchecked")
@Override public T newInstance() throws InvocationTargetException, IllegalAccessException {
@Override
public T newInstance() throws InvocationTargetException, IllegalAccessException {
return (T) newInstance.invoke(null, rawType, Object.class);
}
@Override public String toString() {
@Override
public String toString() {
return rawType.getName();
}
};

View File

@@ -15,6 +15,8 @@
*/
package com.squareup.moshi;
import static com.squareup.moshi.internal.Util.resolve;
import com.squareup.moshi.internal.Util;
import java.io.IOException;
import java.lang.annotation.Annotation;
@@ -30,14 +32,13 @@ import java.util.Set;
import java.util.TreeMap;
import javax.annotation.Nullable;
import static com.squareup.moshi.internal.Util.resolve;
/**
* Emits a regular class as a JSON object by mapping Java fields to JSON object properties.
*
* <h3>Platform Types</h3>
* Fields from platform classes are omitted from both serialization and deserialization unless
* they are either public or protected. This includes the following packages and their subpackages:
*
* Fields from platform classes are omitted from both serialization and deserialization unless they
* are either public or protected. This includes the following packages and their subpackages:
*
* <ul>
* <li>android.*
@@ -50,108 +51,122 @@ import static com.squareup.moshi.internal.Util.resolve;
* </ul>
*/
final class ClassJsonAdapter<T> extends JsonAdapter<T> {
public static final JsonAdapter.Factory FACTORY = new JsonAdapter.Factory() {
@Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (!(type instanceof Class) && !(type instanceof ParameterizedType)) {
return null;
}
Class<?> rawType = Types.getRawType(type);
if (rawType.isInterface() || rawType.isEnum()) return null;
if (!annotations.isEmpty()) return null;
if (Util.isPlatformType(rawType)) {
throwIfIsCollectionClass(type, List.class);
throwIfIsCollectionClass(type, Set.class);
throwIfIsCollectionClass(type, Map.class);
throwIfIsCollectionClass(type, Collection.class);
public static final JsonAdapter.Factory FACTORY =
new JsonAdapter.Factory() {
@Override
public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (!(type instanceof Class) && !(type instanceof ParameterizedType)) {
return null;
}
Class<?> rawType = Types.getRawType(type);
if (rawType.isInterface() || rawType.isEnum()) return null;
if (!annotations.isEmpty()) return null;
if (Util.isPlatformType(rawType)) {
throwIfIsCollectionClass(type, List.class);
throwIfIsCollectionClass(type, Set.class);
throwIfIsCollectionClass(type, Map.class);
throwIfIsCollectionClass(type, Collection.class);
String messagePrefix = "Platform " + rawType;
if (type instanceof ParameterizedType) {
messagePrefix += " in " + type;
String messagePrefix = "Platform " + rawType;
if (type instanceof ParameterizedType) {
messagePrefix += " in " + type;
}
throw new IllegalArgumentException(
messagePrefix + " requires explicit JsonAdapter to be registered");
}
if (rawType.isAnonymousClass()) {
throw new IllegalArgumentException(
"Cannot serialize anonymous class " + rawType.getName());
}
if (rawType.isLocalClass()) {
throw new IllegalArgumentException("Cannot serialize local class " + rawType.getName());
}
if (rawType.getEnclosingClass() != null && !Modifier.isStatic(rawType.getModifiers())) {
throw new IllegalArgumentException(
"Cannot serialize non-static nested class " + rawType.getName());
}
if (Modifier.isAbstract(rawType.getModifiers())) {
throw new IllegalArgumentException(
"Cannot serialize abstract class " + rawType.getName());
}
if (Util.isKotlin(rawType)) {
throw new IllegalArgumentException(
"Cannot serialize Kotlin type "
+ rawType.getName()
+ ". Reflective serialization of Kotlin classes without using kotlin-reflect has "
+ "undefined and unexpected behavior. Please use KotlinJsonAdapter from the "
+ "moshi-kotlin artifact or use code gen from the moshi-kotlin-codegen artifact.");
}
ClassFactory<Object> classFactory = ClassFactory.get(rawType);
Map<String, FieldBinding<?>> fields = new TreeMap<>();
for (Type t = type; t != Object.class; t = Types.getGenericSuperclass(t)) {
createFieldBindings(moshi, t, fields);
}
return new ClassJsonAdapter<>(classFactory, fields).nullSafe();
}
throw new IllegalArgumentException(
messagePrefix + " requires explicit JsonAdapter to be registered");
}
if (rawType.isAnonymousClass()) {
throw new IllegalArgumentException("Cannot serialize anonymous class " + rawType.getName());
}
if (rawType.isLocalClass()) {
throw new IllegalArgumentException("Cannot serialize local class " + rawType.getName());
}
if (rawType.getEnclosingClass() != null && !Modifier.isStatic(rawType.getModifiers())) {
throw new IllegalArgumentException(
"Cannot serialize non-static nested class " + rawType.getName());
}
if (Modifier.isAbstract(rawType.getModifiers())) {
throw new IllegalArgumentException("Cannot serialize abstract class " + rawType.getName());
}
if (Util.isKotlin(rawType)) {
throw new IllegalArgumentException("Cannot serialize Kotlin type " + rawType.getName()
+ ". Reflective serialization of Kotlin classes without using kotlin-reflect has "
+ "undefined and unexpected behavior. Please use KotlinJsonAdapter from the "
+ "moshi-kotlin artifact or use code gen from the moshi-kotlin-codegen artifact.");
}
ClassFactory<Object> classFactory = ClassFactory.get(rawType);
Map<String, FieldBinding<?>> fields = new TreeMap<>();
for (Type t = type; t != Object.class; t = Types.getGenericSuperclass(t)) {
createFieldBindings(moshi, t, fields);
}
return new ClassJsonAdapter<>(classFactory, fields).nullSafe();
}
/**
* Throw clear error messages for the common beginner mistake of using the concrete
* collection classes instead of the collection interfaces, eg: ArrayList instead of List.
*/
private void throwIfIsCollectionClass(Type type, Class<?> collectionInterface) {
Class<?> rawClass = Types.getRawType(type);
if (collectionInterface.isAssignableFrom(rawClass)) {
throw new IllegalArgumentException(
"No JsonAdapter for " + type + ", you should probably use "
+ collectionInterface.getSimpleName() + " instead of " + rawClass.getSimpleName()
+ " (Moshi only supports the collection interfaces by default)"
+ " or else register a custom JsonAdapter.");
}
}
/** Creates a field binding for each of declared field of {@code type}. */
private void createFieldBindings(
Moshi moshi, Type type, Map<String, FieldBinding<?>> fieldBindings) {
Class<?> rawType = Types.getRawType(type);
boolean platformType = Util.isPlatformType(rawType);
for (Field field : rawType.getDeclaredFields()) {
if (!includeField(platformType, field.getModifiers())) continue;
// Look up a type adapter for this type.
Type fieldType = resolve(type, rawType, field.getGenericType());
Set<? extends Annotation> annotations = Util.jsonAnnotations(field);
String fieldName = field.getName();
JsonAdapter<Object> adapter = moshi.adapter(fieldType, annotations, fieldName);
// Create the binding between field and JSON.
field.setAccessible(true);
// Store it using the field's name. If there was already a field with this name, fail!
Json jsonAnnotation = field.getAnnotation(Json.class);
String name = jsonAnnotation != null ? jsonAnnotation.name() : fieldName;
FieldBinding<Object> fieldBinding = new FieldBinding<>(name, field, adapter);
FieldBinding<?> replaced = fieldBindings.put(name, fieldBinding);
if (replaced != null) {
throw new IllegalArgumentException("Conflicting fields:\n"
+ " " + replaced.field + "\n"
+ " " + fieldBinding.field);
/**
* Throw clear error messages for the common beginner mistake of using the concrete
* collection classes instead of the collection interfaces, eg: ArrayList instead of List.
*/
private void throwIfIsCollectionClass(Type type, Class<?> collectionInterface) {
Class<?> rawClass = Types.getRawType(type);
if (collectionInterface.isAssignableFrom(rawClass)) {
throw new IllegalArgumentException(
"No JsonAdapter for "
+ type
+ ", you should probably use "
+ collectionInterface.getSimpleName()
+ " instead of "
+ rawClass.getSimpleName()
+ " (Moshi only supports the collection interfaces by default)"
+ " or else register a custom JsonAdapter.");
}
}
}
}
/** Returns true if fields with {@code modifiers} are included in the emitted JSON. */
private boolean includeField(boolean platformType, int modifiers) {
if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) return false;
return Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers) || !platformType;
}
};
/** Creates a field binding for each of declared field of {@code type}. */
private void createFieldBindings(
Moshi moshi, Type type, Map<String, FieldBinding<?>> fieldBindings) {
Class<?> rawType = Types.getRawType(type);
boolean platformType = Util.isPlatformType(rawType);
for (Field field : rawType.getDeclaredFields()) {
if (!includeField(platformType, field.getModifiers())) continue;
// Look up a type adapter for this type.
Type fieldType = resolve(type, rawType, field.getGenericType());
Set<? extends Annotation> annotations = Util.jsonAnnotations(field);
String fieldName = field.getName();
JsonAdapter<Object> adapter = moshi.adapter(fieldType, annotations, fieldName);
// Create the binding between field and JSON.
field.setAccessible(true);
// Store it using the field's name. If there was already a field with this name, fail!
Json jsonAnnotation = field.getAnnotation(Json.class);
String name = jsonAnnotation != null ? jsonAnnotation.name() : fieldName;
FieldBinding<Object> fieldBinding = new FieldBinding<>(name, field, adapter);
FieldBinding<?> replaced = fieldBindings.put(name, fieldBinding);
if (replaced != null) {
throw new IllegalArgumentException(
"Conflicting fields:\n"
+ " "
+ replaced.field
+ "\n"
+ " "
+ fieldBinding.field);
}
}
}
/** Returns true if fields with {@code modifiers} are included in the emitted JSON. */
private boolean includeField(boolean platformType, int modifiers) {
if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) return false;
return Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers) || !platformType;
}
};
private final ClassFactory<T> classFactory;
private final FieldBinding<?>[] fieldsArray;
@@ -160,11 +175,11 @@ final class ClassJsonAdapter<T> extends JsonAdapter<T> {
ClassJsonAdapter(ClassFactory<T> classFactory, Map<String, FieldBinding<?>> fieldsMap) {
this.classFactory = classFactory;
this.fieldsArray = fieldsMap.values().toArray(new FieldBinding[fieldsMap.size()]);
this.options = JsonReader.Options.of(
fieldsMap.keySet().toArray(new String[fieldsMap.size()]));
this.options = JsonReader.Options.of(fieldsMap.keySet().toArray(new String[fieldsMap.size()]));
}
@Override public T fromJson(JsonReader reader) throws IOException {
@Override
public T fromJson(JsonReader reader) throws IOException {
T result;
try {
result = classFactory.newInstance();
@@ -194,7 +209,8 @@ final class ClassJsonAdapter<T> extends JsonAdapter<T> {
}
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
@Override
public void toJson(JsonWriter writer, T value) throws IOException {
try {
writer.beginObject();
for (FieldBinding<?> fieldBinding : fieldsArray) {
@@ -207,7 +223,8 @@ final class ClassJsonAdapter<T> extends JsonAdapter<T> {
}
}
@Override public String toString() {
@Override
public String toString() {
return "JsonAdapter(" + classFactory + ")";
}

View File

@@ -27,19 +27,21 @@ import javax.annotation.Nullable;
/** Converts collection types to JSON arrays containing their converted contents. */
abstract class CollectionJsonAdapter<C extends Collection<T>, T> extends JsonAdapter<C> {
public static final JsonAdapter.Factory FACTORY = new JsonAdapter.Factory() {
@Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Class<?> rawType = Types.getRawType(type);
if (!annotations.isEmpty()) return null;
if (rawType == List.class || rawType == Collection.class) {
return newArrayListAdapter(type, moshi).nullSafe();
} else if (rawType == Set.class) {
return newLinkedHashSetAdapter(type, moshi).nullSafe();
}
return null;
}
};
public static final JsonAdapter.Factory FACTORY =
new JsonAdapter.Factory() {
@Override
public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Class<?> rawType = Types.getRawType(type);
if (!annotations.isEmpty()) return null;
if (rawType == List.class || rawType == Collection.class) {
return newArrayListAdapter(type, moshi).nullSafe();
} else if (rawType == Set.class) {
return newLinkedHashSetAdapter(type, moshi).nullSafe();
}
return null;
}
};
private final JsonAdapter<T> elementAdapter;
@@ -51,7 +53,8 @@ abstract class CollectionJsonAdapter<C extends Collection<T>, T> extends JsonAda
Type elementType = Types.collectionElementType(type, Collection.class);
JsonAdapter<T> elementAdapter = moshi.adapter(elementType);
return new CollectionJsonAdapter<Collection<T>, T>(elementAdapter) {
@Override Collection<T> newCollection() {
@Override
Collection<T> newCollection() {
return new ArrayList<>();
}
};
@@ -61,7 +64,8 @@ abstract class CollectionJsonAdapter<C extends Collection<T>, T> extends JsonAda
Type elementType = Types.collectionElementType(type, Collection.class);
JsonAdapter<T> elementAdapter = moshi.adapter(elementType);
return new CollectionJsonAdapter<Set<T>, T>(elementAdapter) {
@Override Set<T> newCollection() {
@Override
Set<T> newCollection() {
return new LinkedHashSet<>();
}
};
@@ -69,7 +73,8 @@ abstract class CollectionJsonAdapter<C extends Collection<T>, T> extends JsonAda
abstract C newCollection();
@Override public C fromJson(JsonReader reader) throws IOException {
@Override
public C fromJson(JsonReader reader) throws IOException {
C result = newCollection();
reader.beginArray();
while (reader.hasNext()) {
@@ -79,7 +84,8 @@ abstract class CollectionJsonAdapter<C extends Collection<T>, T> extends JsonAda
return result;
}
@Override public void toJson(JsonWriter writer, C value) throws IOException {
@Override
public void toJson(JsonWriter writer, C value) throws IOException {
writer.beginArray();
for (T element : value) {
elementAdapter.toJson(writer, element);
@@ -87,7 +93,8 @@ abstract class CollectionJsonAdapter<C extends Collection<T>, T> extends JsonAda
writer.endArray();
}
@Override public String toString() {
@Override
public String toString() {
return elementAdapter + ".collection()";
}
}

View File

@@ -22,5 +22,4 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface FromJson {
}
public @interface FromJson {}

View File

@@ -15,12 +15,12 @@
*/
package com.squareup.moshi;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Customizes how a field is encoded as JSON.
*

View File

@@ -37,13 +37,16 @@ import okio.BufferedSource;
* <p>Custom JsonAdapter implementations should be designed to be thread-safe.
*/
public abstract class JsonAdapter<T> {
@CheckReturnValue public abstract @Nullable T fromJson(JsonReader reader) throws IOException;
@CheckReturnValue
public abstract @Nullable T fromJson(JsonReader reader) throws IOException;
@CheckReturnValue public final @Nullable T fromJson(BufferedSource source) throws IOException {
@CheckReturnValue
public final @Nullable T fromJson(BufferedSource source) throws IOException {
return fromJson(JsonReader.of(source));
}
@CheckReturnValue public final @Nullable T fromJson(String string) throws IOException {
@CheckReturnValue
public final @Nullable T fromJson(String string) throws IOException {
JsonReader reader = JsonReader.of(new Buffer().writeUtf8(string));
T result = fromJson(reader);
if (!isLenient() && reader.peek() != JsonReader.Token.END_DOCUMENT) {
@@ -59,7 +62,8 @@ public abstract class JsonAdapter<T> {
toJson(writer, value);
}
@CheckReturnValue public final String toJson(@Nullable T value) {
@CheckReturnValue
public final String toJson(@Nullable T value) {
Buffer buffer = new Buffer();
try {
toJson(buffer, value);
@@ -74,12 +78,13 @@ public abstract class JsonAdapter<T> {
* booleans, and nulls.
*
* <p>Values encoded using {@code value(double)} or {@code value(long)} are modeled with the
* corresponding boxed type. Values encoded using {@code value(Number)} are modeled as a
* {@link Long} for boxed integer types ({@link Byte}, {@link Short}, {@link Integer}, and {@link
* Long}), as a {@link Double} for boxed floating point types ({@link Float} and {@link Double}),
* and as a {@link BigDecimal} for all other types.
* corresponding boxed type. Values encoded using {@code value(Number)} are modeled as a {@link
* Long} for boxed integer types ({@link Byte}, {@link Short}, {@link Integer}, and {@link Long}),
* as a {@link Double} for boxed floating point types ({@link Float} and {@link Double}), and as a
* {@link BigDecimal} for all other types.
*/
@CheckReturnValue public final @Nullable Object toJsonValue(@Nullable T value) {
@CheckReturnValue
public final @Nullable Object toJsonValue(@Nullable T value) {
JsonValueWriter writer = new JsonValueWriter();
try {
toJson(writer, value);
@@ -93,7 +98,8 @@ public abstract class JsonAdapter<T> {
* Decodes a Java value object from {@code value}, which must be comprised of maps, lists,
* strings, numbers, booleans and nulls.
*/
@CheckReturnValue public final @Nullable T fromJsonValue(@Nullable Object value) {
@CheckReturnValue
public final @Nullable T fromJsonValue(@Nullable Object value) {
JsonValueReader reader = new JsonValueReader(value);
try {
return fromJson(reader);
@@ -106,13 +112,17 @@ public abstract class JsonAdapter<T> {
* Returns a JSON adapter equal to this JSON adapter, but that serializes nulls when encoding
* JSON.
*/
@CheckReturnValue public final JsonAdapter<T> serializeNulls() {
@CheckReturnValue
public final JsonAdapter<T> serializeNulls() {
final JsonAdapter<T> delegate = this;
return new JsonAdapter<T>() {
@Override public @Nullable T fromJson(JsonReader reader) throws IOException {
@Override
public @Nullable T fromJson(JsonReader reader) throws IOException {
return delegate.fromJson(reader);
}
@Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
@Override
public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
boolean serializeNulls = writer.getSerializeNulls();
writer.setSerializeNulls(true);
try {
@@ -121,10 +131,14 @@ public abstract class JsonAdapter<T> {
writer.setSerializeNulls(serializeNulls);
}
}
@Override boolean isLenient() {
@Override
boolean isLenient() {
return delegate.isLenient();
}
@Override public String toString() {
@Override
public String toString() {
return delegate + ".serializeNulls()";
}
};
@@ -134,7 +148,8 @@ public abstract class JsonAdapter<T> {
* Returns a JSON adapter equal to this JSON adapter, but with support for reading and writing
* nulls.
*/
@CheckReturnValue public final JsonAdapter<T> nullSafe() {
@CheckReturnValue
public final JsonAdapter<T> nullSafe() {
if (this instanceof NullSafeJsonAdapter) {
return this;
}
@@ -148,7 +163,8 @@ public abstract class JsonAdapter<T> {
* <p>Note that this adapter will not usually be invoked for absent values and so those must be
* handled elsewhere. This should only be used to fail on explicit nulls.
*/
@CheckReturnValue public final JsonAdapter<T> nonNull() {
@CheckReturnValue
public final JsonAdapter<T> nonNull() {
if (this instanceof NonNullJsonAdapter) {
return this;
}
@@ -156,10 +172,12 @@ public abstract class JsonAdapter<T> {
}
/** Returns a JSON adapter equal to this, but is lenient when reading and writing. */
@CheckReturnValue public final JsonAdapter<T> lenient() {
@CheckReturnValue
public final JsonAdapter<T> lenient() {
final JsonAdapter<T> delegate = this;
return new JsonAdapter<T>() {
@Override public @Nullable T fromJson(JsonReader reader) throws IOException {
@Override
public @Nullable T fromJson(JsonReader reader) throws IOException {
boolean lenient = reader.isLenient();
reader.setLenient(true);
try {
@@ -168,7 +186,9 @@ public abstract class JsonAdapter<T> {
reader.setLenient(lenient);
}
}
@Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
@Override
public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
boolean lenient = writer.isLenient();
writer.setLenient(true);
try {
@@ -177,10 +197,14 @@ public abstract class JsonAdapter<T> {
writer.setLenient(lenient);
}
}
@Override boolean isLenient() {
@Override
boolean isLenient() {
return true;
}
@Override public String toString() {
@Override
public String toString() {
return delegate + ".lenient()";
}
};
@@ -192,10 +216,12 @@ public abstract class JsonAdapter<T> {
* This constraint applies to both the top-level message handled by this type adapter as well as
* to nested messages.
*/
@CheckReturnValue public final JsonAdapter<T> failOnUnknown() {
@CheckReturnValue
public final JsonAdapter<T> failOnUnknown() {
final JsonAdapter<T> delegate = this;
return new JsonAdapter<T>() {
@Override public @Nullable T fromJson(JsonReader reader) throws IOException {
@Override
public @Nullable T fromJson(JsonReader reader) throws IOException {
boolean skipForbidden = reader.failOnUnknown();
reader.setFailOnUnknown(true);
try {
@@ -204,13 +230,19 @@ public abstract class JsonAdapter<T> {
reader.setFailOnUnknown(skipForbidden);
}
}
@Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
@Override
public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
delegate.toJson(writer, value);
}
@Override boolean isLenient() {
@Override
boolean isLenient() {
return delegate.isLenient();
}
@Override public String toString() {
@Override
public String toString() {
return delegate + ".failOnUnknown()";
}
};
@@ -224,16 +256,20 @@ public abstract class JsonAdapter<T> {
*
* @param indent a string containing only whitespace.
*/
@CheckReturnValue public JsonAdapter<T> indent(final String indent) {
@CheckReturnValue
public JsonAdapter<T> indent(final String indent) {
if (indent == null) {
throw new NullPointerException("indent == null");
}
final JsonAdapter<T> delegate = this;
return new JsonAdapter<T>() {
@Override public @Nullable T fromJson(JsonReader reader) throws IOException {
@Override
public @Nullable T fromJson(JsonReader reader) throws IOException {
return delegate.fromJson(reader);
}
@Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
@Override
public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
String originalIndent = writer.getIndent();
writer.setIndent(indent);
try {
@@ -242,10 +278,14 @@ public abstract class JsonAdapter<T> {
writer.setIndent(originalIndent);
}
}
@Override boolean isLenient() {
@Override
boolean isLenient() {
return delegate.isLenient();
}
@Override public String toString() {
@Override
public String toString() {
return delegate + ".indent(\"" + indent + "\")";
}
};
@@ -265,6 +305,7 @@ public abstract class JsonAdapter<T> {
* {@link Moshi#nextAdapter} to delegate to the underlying adapter of the same type.
*/
@CheckReturnValue
@Nullable JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi);
@Nullable
JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi);
}
}

View File

@@ -15,15 +15,13 @@
*/
package com.squareup.moshi;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.reflect.Type;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Customizes how a type is encoded as JSON.
*/
/** Customizes how a type is encoded as JSON. */
@Retention(RUNTIME)
@Documented
public @interface JsonClass {
@@ -32,15 +30,14 @@ public @interface JsonClass {
*
* <p>There are currently some restrictions on which types that can be used with generated
* adapters:
*
* <ul>
* <li>
* The class must be implemented in Kotlin (unless using a custom generator, see
* {@link #generator()}).
* </li>
* <li>The class may not be an abstract class, an inner class, or a local class.</li>
* <li>All superclasses must be implemented in Kotlin.</li>
* <li>All properties must be public, protected, or internal.</li>
* <li>All properties must be either non-transient or have a default value.</li>
* <li>The class must be implemented in Kotlin (unless using a custom generator, see {@link
* #generator()}).
* <li>The class may not be an abstract class, an inner class, or a local class.
* <li>All superclasses must be implemented in Kotlin.
* <li>All properties must be public, protected, or internal.
* <li>All properties must be either non-transient or have a default value.
* </ul>
*/
boolean generateAdapter();
@@ -48,29 +45,28 @@ public @interface JsonClass {
/**
* An optional custom generator tag used to indicate which generator should be used. If empty,
* Moshi's annotation processor will generate an adapter for the annotated type. If not empty,
* Moshi's processor will skip it and defer to a custom generator. This can be used to allow
* other custom code generation tools to run and still allow Moshi to read their generated
* JsonAdapter outputs.
* Moshi's processor will skip it and defer to a custom generator. This can be used to allow other
* custom code generation tools to run and still allow Moshi to read their generated JsonAdapter
* outputs.
*
* <p>Requirements for generated adapter class signatures:
*
* <ul>
* <li>
* The generated adapter must subclass {@link JsonAdapter} and be parameterized by this type.
* </li>
* <li>
* {@link Types#generatedJsonAdapterName} should be used for the fully qualified class name in
* order for Moshi to correctly resolve and load the generated JsonAdapter.
* </li>
* <li>The first parameter must be a {@link Moshi} instance.</li>
* <li>
* If generic, a second {@link Type Type[]} parameter should be declared to accept type arguments.
* </li>
* <li>The generated adapter must subclass {@link JsonAdapter} and be parameterized by this
* type.
* <li>{@link Types#generatedJsonAdapterName} should be used for the fully qualified class name
* in order for Moshi to correctly resolve and load the generated JsonAdapter.
* <li>The first parameter must be a {@link Moshi} instance.
* <li>If generic, a second {@link Type Type[]} parameter should be declared to accept type
* arguments.
* </ul>
*
* <p>Example for a class "CustomType":<pre>{@code
* class CustomTypeJsonAdapter(moshi: Moshi, types: Array<Type>) : JsonAdapter<CustomType>() {
* // ...
* }
* <p>Example for a class "CustomType":
*
* <pre>{@code
* class CustomTypeJsonAdapter(moshi: Moshi, types: Array<Type>) : JsonAdapter<CustomType>() {
* // ...
* }
* }</pre>
*
* <p>To help ensure your own generator meets requirements above, you can use Moshis built-in

View File

@@ -22,16 +22,15 @@ import javax.annotation.Nullable;
* example, suppose the application expects a boolean but the JSON document contains a string. When
* the call to {@link JsonReader#nextBoolean} is made, a {@code JsonDataException} is thrown.
*
* <p>Exceptions of this type should be fixed by either changing the application code to accept
* the unexpected JSON, or by changing the JSON to conform to the application's expectations.
* <p>Exceptions of this type should be fixed by either changing the application code to accept the
* unexpected JSON, or by changing the JSON to conform to the application's expectations.
*
* <p>This exception may also be triggered if a document's nesting exceeds 31 levels. This depth is
* sufficient for all practical applications, but shallow enough to avoid uglier failures like
* {@link StackOverflowError}.
*/
public final class JsonDataException extends RuntimeException {
public JsonDataException() {
}
public JsonDataException() {}
public JsonDataException(@Nullable String message) {
super(message);

View File

@@ -15,16 +15,15 @@
*/
package com.squareup.moshi;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** Annotates another annotation, causing it to specialize how values are encoded and decoded. */
@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
@Documented
public @interface JsonQualifier {
}
public @interface JsonQualifier {}

View File

@@ -15,6 +15,8 @@
*/
package com.squareup.moshi;
import static java.util.Collections.unmodifiableList;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
@@ -27,47 +29,46 @@ import okio.Buffer;
import okio.BufferedSource;
import okio.ByteString;
import static java.util.Collections.unmodifiableList;
/**
* Reads a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>)
* encoded value as a stream of tokens. This stream includes both literal
* values (strings, numbers, booleans, and nulls) as well as the begin and
* end delimiters of objects and arrays. The tokens are traversed in
* depth-first order, the same order that they appear in the JSON document.
* Within JSON objects, name/value pairs are represented by a single token.
* Reads a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>) encoded value as a
* stream of tokens. This stream includes both literal values (strings, numbers, booleans, and
* nulls) as well as the begin and end delimiters of objects and arrays. The tokens are traversed in
* depth-first order, the same order that they appear in the JSON document. Within JSON objects,
* name/value pairs are represented by a single token.
*
* <h3>Parsing JSON</h3>
* To create a recursive descent parser for your own JSON streams, first create
* an entry point method that creates a {@code JsonReader}.
*
* <p>Next, create handler methods for each structure in your JSON text. You'll
* need a method for each object type and for each array type.
* To create a recursive descent parser for your own JSON streams, first create an entry point
* method that creates a {@code JsonReader}.
*
* <p>Next, create handler methods for each structure in your JSON text. You'll need a method for
* each object type and for each array type.
*
* <ul>
* <li>Within <strong>array handling</strong> methods, first call {@link
* #beginArray} to consume the array's opening bracket. Then create a
* while loop that accumulates values, terminating when {@link #hasNext}
* is false. Finally, read the array's closing bracket by calling {@link
* <li>Within <strong>array handling</strong> methods, first call {@link #beginArray} to consume
* the array's opening bracket. Then create a while loop that accumulates values, terminating
* when {@link #hasNext} is false. Finally, read the array's closing bracket by calling {@link
* #endArray}.
* <li>Within <strong>object handling</strong> methods, first call {@link
* #beginObject} to consume the object's opening brace. Then create a
* while loop that assigns values to local variables based on their name.
* This loop should terminate when {@link #hasNext} is false. Finally,
* <li>Within <strong>object handling</strong> methods, first call {@link #beginObject} to consume
* the object's opening brace. Then create a while loop that assigns values to local variables
* based on their name. This loop should terminate when {@link #hasNext} is false. Finally,
* read the object's closing brace by calling {@link #endObject}.
* </ul>
* <p>When a nested object or array is encountered, delegate to the
* corresponding handler method.
*
* <p>When an unknown name is encountered, strict parsers should fail with an
* exception. Lenient parsers should call {@link #skipValue()} to recursively
* skip the value's nested tokens, which may otherwise conflict.
* <p>When a nested object or array is encountered, delegate to the corresponding handler method.
*
* <p>If a value may be null, you should first check using {@link #peek()}.
* Null literals can be consumed using either {@link #nextNull()} or {@link
* #skipValue()}.
* <p>When an unknown name is encountered, strict parsers should fail with an exception. Lenient
* parsers should call {@link #skipValue()} to recursively skip the value's nested tokens, which may
* otherwise conflict.
*
* <p>If a value may be null, you should first check using {@link #peek()}. Null literals can be
* consumed using either {@link #nextNull()} or {@link #skipValue()}.
*
* <h3>Example</h3>
* Suppose we'd like to parse a stream of messages such as the following: <pre> {@code
*
* Suppose we'd like to parse a stream of messages such as the following:
*
* <pre>{@code
* [
* {
* "id": 912345678901,
@@ -87,96 +88,99 @@ import static java.util.Collections.unmodifiableList;
* "followers_count": 2
* }
* }
* ]}</pre>
* This code implements the parser for the above structure: <pre> {@code
* ]
* }</pre>
*
* public List<Message> readJsonStream(BufferedSource source) throws IOException {
* JsonReader reader = JsonReader.of(source);
* try {
* return readMessagesArray(reader);
* } finally {
* reader.close();
* This code implements the parser for the above structure:
*
* <pre>{@code
* public List<Message> readJsonStream(BufferedSource source) throws IOException {
* JsonReader reader = JsonReader.of(source);
* try {
* return readMessagesArray(reader);
* } finally {
* reader.close();
* }
* }
*
* public List<Message> readMessagesArray(JsonReader reader) throws IOException {
* List<Message> messages = new ArrayList<Message>();
*
* reader.beginArray();
* while (reader.hasNext()) {
* messages.add(readMessage(reader));
* }
* reader.endArray();
* return messages;
* }
*
* public Message readMessage(JsonReader reader) throws IOException {
* long id = -1;
* String text = null;
* User user = null;
* List<Double> geo = null;
*
* reader.beginObject();
* while (reader.hasNext()) {
* String name = reader.nextName();
* if (name.equals("id")) {
* id = reader.nextLong();
* } else if (name.equals("text")) {
* text = reader.nextString();
* } else if (name.equals("geo") && reader.peek() != Token.NULL) {
* geo = readDoublesArray(reader);
* } else if (name.equals("user")) {
* user = readUser(reader);
* } else {
* reader.skipValue();
* }
* }
* reader.endObject();
* return new Message(id, text, user, geo);
* }
*
* public List<Message> readMessagesArray(JsonReader reader) throws IOException {
* List<Message> messages = new ArrayList<Message>();
* public List<Double> readDoublesArray(JsonReader reader) throws IOException {
* List<Double> doubles = new ArrayList<Double>();
*
* reader.beginArray();
* while (reader.hasNext()) {
* messages.add(readMessage(reader));
* }
* reader.endArray();
* return messages;
* reader.beginArray();
* while (reader.hasNext()) {
* doubles.add(reader.nextDouble());
* }
* reader.endArray();
* return doubles;
* }
*
* public Message readMessage(JsonReader reader) throws IOException {
* long id = -1;
* String text = null;
* User user = null;
* List<Double> geo = null;
* public User readUser(JsonReader reader) throws IOException {
* String username = null;
* int followersCount = -1;
*
* reader.beginObject();
* while (reader.hasNext()) {
* String name = reader.nextName();
* if (name.equals("id")) {
* id = reader.nextLong();
* } else if (name.equals("text")) {
* text = reader.nextString();
* } else if (name.equals("geo") && reader.peek() != Token.NULL) {
* geo = readDoublesArray(reader);
* } else if (name.equals("user")) {
* user = readUser(reader);
* } else {
* reader.skipValue();
* }
* reader.beginObject();
* while (reader.hasNext()) {
* String name = reader.nextName();
* if (name.equals("name")) {
* username = reader.nextString();
* } else if (name.equals("followers_count")) {
* followersCount = reader.nextInt();
* } else {
* reader.skipValue();
* }
* reader.endObject();
* return new Message(id, text, user, geo);
* }
*
* public List<Double> readDoublesArray(JsonReader reader) throws IOException {
* List<Double> doubles = new ArrayList<Double>();
*
* reader.beginArray();
* while (reader.hasNext()) {
* doubles.add(reader.nextDouble());
* }
* reader.endArray();
* return doubles;
* }
*
* public User readUser(JsonReader reader) throws IOException {
* String username = null;
* int followersCount = -1;
*
* reader.beginObject();
* while (reader.hasNext()) {
* String name = reader.nextName();
* if (name.equals("name")) {
* username = reader.nextString();
* } else if (name.equals("followers_count")) {
* followersCount = reader.nextInt();
* } else {
* reader.skipValue();
* }
* }
* reader.endObject();
* return new User(username, followersCount);
* }}</pre>
* reader.endObject();
* return new User(username, followersCount);
* }
* }</pre>
*
* <h3>Number Handling</h3>
* This reader permits numeric values to be read as strings and string values to
* be read as numbers. For example, both elements of the JSON array {@code
* [1, "1"]} may be read using either {@link #nextInt} or {@link #nextString}.
* This behavior is intended to prevent lossy numeric conversions: double is
* JavaScript's only numeric type and very large values like {@code
* 9007199254740993} cannot be represented exactly on that platform. To minimize
* precision loss, extremely large values should be written and read as strings
* in JSON.
*
* <p>Each {@code JsonReader} may be used to read a single JSON stream. Instances
* of this class are not thread safe.
* This reader permits numeric values to be read as strings and string values to be read as numbers.
* For example, both elements of the JSON array {@code [1, "1"]} may be read using either {@link
* #nextInt} or {@link #nextString}. This behavior is intended to prevent lossy numeric conversions:
* double is JavaScript's only numeric type and very large values like {@code 9007199254740993}
* cannot be represented exactly on that platform. To minimize precision loss, extremely large
* values should be written and read as strings in JSON.
*
* <p>Each {@code JsonReader} may be used to read a single JSON stream. Instances of this class are
* not thread safe.
*/
public abstract class JsonReader implements Closeable {
// The nesting stack. Using a manual array rather than an ArrayList saves 20%. This stack will
@@ -194,7 +198,8 @@ public abstract class JsonReader implements Closeable {
boolean failOnUnknown;
/** Returns a new instance that reads UTF-8 encoded JSON from {@code source}. */
@CheckReturnValue public static JsonReader of(BufferedSource source) {
@CheckReturnValue
public static JsonReader of(BufferedSource source) {
return new JsonUtf8Reader(source);
}
@@ -228,8 +233,8 @@ public abstract class JsonReader implements Closeable {
}
/**
* Throws a new IO exception with the given message and a context snippet
* with this reader's content.
* Throws a new IO exception with the given message and a context snippet with this reader's
* content.
*/
final JsonEncodingException syntaxError(String message) throws JsonEncodingException {
throw new JsonEncodingException(message + " at path " + getPath());
@@ -237,36 +242,39 @@ public abstract class JsonReader implements Closeable {
final JsonDataException typeMismatch(@Nullable Object value, Object expected) {
if (value == null) {
return new JsonDataException(
"Expected " + expected + " but was null at path " + getPath());
return new JsonDataException("Expected " + expected + " but was null at path " + getPath());
} else {
return new JsonDataException("Expected " + expected + " but was " + value + ", a "
+ value.getClass().getName() + ", at path " + getPath());
return new JsonDataException(
"Expected "
+ expected
+ " but was "
+ value
+ ", a "
+ value.getClass().getName()
+ ", at path "
+ getPath());
}
}
/**
* Configure this parser to be liberal in what it accepts. By default
* this parser is strict and only accepts JSON as specified by <a
* href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>. Setting the
* parser to lenient causes it to ignore the following syntax errors:
* Configure this parser to be liberal in what it accepts. By default this parser is strict and
* only accepts JSON as specified by <a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>.
* Setting the parser to lenient causes it to ignore the following syntax errors:
*
* <ul>
* <li>Streams that include multiple top-level values. With strict parsing,
* each stream must contain exactly one top-level value.
* <li>Numbers may be {@linkplain Double#isNaN() NaNs} or {@link
* Double#isInfinite() infinities}.
* <li>End of line comments starting with {@code //} or {@code #} and
* ending with a newline character.
* <li>C-style comments starting with {@code /*} and ending with
* {@code *}{@code /}. Such comments may not be nested.
* <li>Streams that include multiple top-level values. With strict parsing, each stream must
* contain exactly one top-level value.
* <li>Numbers may be {@linkplain Double#isNaN() NaNs} or {@link Double#isInfinite()
* infinities}.
* <li>End of line comments starting with {@code //} or {@code #} and ending with a newline
* character.
* <li>C-style comments starting with {@code /*} and ending with {@code *}{@code /}. Such
* comments may not be nested.
* <li>Names that are unquoted or {@code 'single quoted'}.
* <li>Strings that are unquoted or {@code 'single quoted'}.
* <li>Array elements separated by {@code ;} instead of {@code ,}.
* <li>Unnecessary array separators. These are interpreted as if null
* was the omitted value.
* <li>Names and values separated by {@code =} or {@code =>} instead of
* {@code :}.
* <li>Unnecessary array separators. These are interpreted as if null was the omitted value.
* <li>Names and values separated by {@code =} or {@code =>} instead of {@code :}.
* <li>Name/value pairs separated by {@code ;} instead of {@code ,}.
* </ul>
*/
@@ -274,10 +282,9 @@ public abstract class JsonReader implements Closeable {
this.lenient = lenient;
}
/**
* Returns true if this parser is liberal in what it accepts.
*/
@CheckReturnValue public final boolean isLenient() {
/** Returns true if this parser is liberal in what it accepts. */
@CheckReturnValue
public final boolean isLenient() {
return lenient;
}
@@ -293,10 +300,9 @@ public abstract class JsonReader implements Closeable {
this.failOnUnknown = failOnUnknown;
}
/**
* Returns true if this parser forbids skipping names and values.
*/
@CheckReturnValue public final boolean failOnUnknown() {
/** Returns true if this parser forbids skipping names and values. */
@CheckReturnValue
public final boolean failOnUnknown() {
return failOnUnknown;
}
@@ -307,8 +313,8 @@ public abstract class JsonReader implements Closeable {
public abstract void beginArray() throws IOException;
/**
* Consumes the next token from the JSON stream and asserts that it is the
* end of the current array.
* Consumes the next token from the JSON stream and asserts that it is the end of the current
* array.
*/
public abstract void endArray() throws IOException;
@@ -324,28 +330,28 @@ public abstract class JsonReader implements Closeable {
*/
public abstract void endObject() throws IOException;
/**
* Returns true if the current array or object has another element.
*/
@CheckReturnValue public abstract boolean hasNext() throws IOException;
/** Returns true if the current array or object has another element. */
@CheckReturnValue
public abstract boolean hasNext() throws IOException;
/**
* Returns the type of the next token without consuming it.
*/
@CheckReturnValue public abstract Token peek() throws IOException;
/** Returns the type of the next token without consuming it. */
@CheckReturnValue
public abstract Token peek() throws IOException;
/**
* Returns the next token, a {@linkplain Token#NAME property name}, and consumes it.
*
* @throws JsonDataException if the next token in the stream is not a property name.
*/
@CheckReturnValue public abstract String nextName() throws IOException;
@CheckReturnValue
public abstract String nextName() throws IOException;
/**
* If the next token is a {@linkplain Token#NAME property name} that's in {@code options}, this
* consumes it and returns its index. Otherwise this returns -1 and no name is consumed.
*/
@CheckReturnValue public abstract int selectName(Options options) throws IOException;
@CheckReturnValue
public abstract int selectName(Options options) throws IOException;
/**
* Skips the next token, consuming it. This method is intended for use when the JSON token stream
@@ -368,7 +374,8 @@ public abstract class JsonReader implements Closeable {
* If the next token is a {@linkplain Token#STRING string} that's in {@code options}, this
* consumes it and returns its index. Otherwise this returns -1 and no string is consumed.
*/
@CheckReturnValue public abstract int selectString(Options options) throws IOException;
@CheckReturnValue
public abstract int selectString(Options options) throws IOException;
/**
* Returns the {@linkplain Token#BOOLEAN boolean} value of the next token, consuming it.
@@ -430,7 +437,7 @@ public abstract class JsonReader implements Closeable {
* null, map, or list, according to the JSON structure.
*
* @throws JsonDataException if the next token is not a literal value, if a JSON object has a
* duplicate key.
* duplicate key.
* @see JsonWriter#jsonValue(Object)
*/
public final @Nullable Object readJsonValue() throws IOException {
@@ -452,8 +459,15 @@ public abstract class JsonReader implements Closeable {
Object value = readJsonValue();
Object replaced = map.put(name, value);
if (replaced != null) {
throw new JsonDataException("Map key '" + name + "' has multiple values at path "
+ getPath() + ": " + replaced + " and " + value);
throw new JsonDataException(
"Map key '"
+ name
+ "' has multiple values at path "
+ getPath()
+ ": "
+ replaced
+ " and "
+ value);
}
}
endObject();
@@ -484,30 +498,31 @@ public abstract class JsonReader implements Closeable {
* <p>For example, we can use {@code peekJson()} to lookahead and read the same data multiple
* times.
*
* <pre> {@code
* <pre>{@code
* Buffer buffer = new Buffer();
* buffer.writeUtf8("[123, 456, 789]")
*
* Buffer buffer = new Buffer();
* buffer.writeUtf8("[123, 456, 789]")
* JsonReader jsonReader = JsonReader.of(buffer);
* jsonReader.beginArray();
* jsonReader.nextInt(); // Returns 123, reader contains 456, 789 and ].
*
* JsonReader jsonReader = JsonReader.of(buffer);
* jsonReader.beginArray();
* jsonReader.nextInt(); // Returns 123, reader contains 456, 789 and ].
* JsonReader peek = reader.peekJson();
* peek.nextInt() // Returns 456.
* peek.nextInt() // Returns 789.
* peek.endArray()
*
* JsonReader peek = reader.peekJson();
* peek.nextInt() // Returns 456.
* peek.nextInt() // Returns 789.
* peek.endArray()
*
* jsonReader.nextInt() // Returns 456, reader contains 789 and ].
* jsonReader.nextInt() // Returns 456, reader contains 789 and ].
* }</pre>
*/
@CheckReturnValue public abstract JsonReader peekJson();
@CheckReturnValue
public abstract JsonReader peekJson();
/**
* Returns a <a href="http://goessner.net/articles/JsonPath/">JsonPath</a> to
* the current location in the JSON value.
* Returns a <a href="http://goessner.net/articles/JsonPath/">JsonPath</a> to the current location
* in the JSON value.
*/
@CheckReturnValue public final String getPath() {
@CheckReturnValue
public final String getPath() {
return JsonScope.getPath(stackSize, scopes, pathNames, pathIndices);
}
@@ -516,14 +531,14 @@ public abstract class JsonReader implements Closeable {
* that arbitrary type adapters can use {@link #nextString} to read a name value.
*
* <p>In this example, calling this method allows two sequential calls to {@link #nextString()}:
* <pre> {@code
*
* JsonReader reader = JsonReader.of(new Buffer().writeUtf8("{\"a\":\"b\"}"));
* reader.beginObject();
* reader.promoteNameToValue();
* assertEquals("a", reader.nextString());
* assertEquals("b", reader.nextString());
* reader.endObject();
* <pre>{@code
* JsonReader reader = JsonReader.of(new Buffer().writeUtf8("{\"a\":\"b\"}"));
* reader.beginObject();
* reader.promoteNameToValue();
* assertEquals("a", reader.nextString());
* assertEquals("b", reader.nextString());
* reader.endObject();
* }</pre>
*/
public abstract void promoteNameToValue() throws IOException;
@@ -546,7 +561,8 @@ public abstract class JsonReader implements Closeable {
return unmodifiableList(Arrays.asList(strings));
}
@CheckReturnValue public static Options of(String... strings) {
@CheckReturnValue
public static Options of(String... strings) {
try {
ByteString[] result = new ByteString[strings.length];
Buffer buffer = new Buffer();
@@ -562,67 +578,56 @@ public abstract class JsonReader implements Closeable {
}
}
/**
* A structure, name, or value type in a JSON-encoded string.
*/
/** A structure, name, or value type in a JSON-encoded string. */
public enum Token {
/**
* The opening of a JSON array. Written using {@link JsonWriter#beginArray}
* and read using {@link JsonReader#beginArray}.
* The opening of a JSON array. Written using {@link JsonWriter#beginArray} and read using
* {@link JsonReader#beginArray}.
*/
BEGIN_ARRAY,
/**
* The closing of a JSON array. Written using {@link JsonWriter#endArray}
* and read using {@link JsonReader#endArray}.
* The closing of a JSON array. Written using {@link JsonWriter#endArray} and read using {@link
* JsonReader#endArray}.
*/
END_ARRAY,
/**
* The opening of a JSON object. Written using {@link JsonWriter#beginObject}
* and read using {@link JsonReader#beginObject}.
* The opening of a JSON object. Written using {@link JsonWriter#beginObject} and read using
* {@link JsonReader#beginObject}.
*/
BEGIN_OBJECT,
/**
* The closing of a JSON object. Written using {@link JsonWriter#endObject}
* and read using {@link JsonReader#endObject}.
* The closing of a JSON object. Written using {@link JsonWriter#endObject} and read using
* {@link JsonReader#endObject}.
*/
END_OBJECT,
/**
* A JSON property name. Within objects, tokens alternate between names and
* their values. Written using {@link JsonWriter#name} and read using {@link
* JsonReader#nextName}
* A JSON property name. Within objects, tokens alternate between names and their values.
* Written using {@link JsonWriter#name} and read using {@link JsonReader#nextName}
*/
NAME,
/**
* A JSON string.
*/
/** A JSON string. */
STRING,
/**
* A JSON number represented in this API by a Java {@code double}, {@code
* long}, or {@code int}.
* A JSON number represented in this API by a Java {@code double}, {@code long}, or {@code int}.
*/
NUMBER,
/**
* A JSON {@code true} or {@code false}.
*/
/** A JSON {@code true} or {@code false}. */
BOOLEAN,
/**
* A JSON {@code null}.
*/
/** A JSON {@code null}. */
NULL,
/**
* The end of the JSON stream. This sentinel value is returned by {@link
* JsonReader#peek()} to signal that the JSON-encoded value has no more
* tokens.
* The end of the JSON stream. This sentinel value is returned by {@link JsonReader#peek()} to
* signal that the JSON-encoded value has no more tokens.
*/
END_DOCUMENT
}

View File

@@ -17,8 +17,7 @@ package com.squareup.moshi;
/** Lexical scoping elements within a JSON reader or writer. */
final class JsonScope {
private JsonScope() {
}
private JsonScope() {}
/** An array with no elements requires no separators or newlines before it is closed. */
static final int EMPTY_ARRAY = 1;

View File

@@ -28,8 +28,8 @@ final class JsonUtf8Reader extends JsonReader {
private static final ByteString SINGLE_QUOTE_OR_SLASH = ByteString.encodeUtf8("'\\");
private static final ByteString DOUBLE_QUOTE_OR_SLASH = ByteString.encodeUtf8("\"\\");
private static final ByteString UNQUOTED_STRING_TERMINALS
= ByteString.encodeUtf8("{}[]:, \n\t\r\f/\\;#=");
private static final ByteString UNQUOTED_STRING_TERMINALS =
ByteString.encodeUtf8("{}[]:, \n\t\r\f/\\;#=");
private static final ByteString LINEFEED_OR_CARRIAGE_RETURN = ByteString.encodeUtf8("\n\r");
private static final ByteString CLOSING_BLOCK_COMMENT = ByteString.encodeUtf8("*/");
@@ -46,12 +46,14 @@ final class JsonUtf8Reader extends JsonReader {
private static final int PEEKED_UNQUOTED = 10;
/** When this is returned, the string value is stored in peekedString. */
private static final int PEEKED_BUFFERED = 11;
private static final int PEEKED_SINGLE_QUOTED_NAME = 12;
private static final int PEEKED_DOUBLE_QUOTED_NAME = 13;
private static final int PEEKED_UNQUOTED_NAME = 14;
private static final int PEEKED_BUFFERED_NAME = 15;
/** When this is returned, the integer value is stored in peekedLong. */
private static final int PEEKED_LONG = 16;
private static final int PEEKED_NUMBER = 17;
private static final int PEEKED_EOF = 18;
@@ -67,25 +69,23 @@ final class JsonUtf8Reader extends JsonReader {
/** The input JSON. */
private final BufferedSource source;
private final Buffer buffer;
private int peeked = PEEKED_NONE;
/**
* A peeked value that was composed entirely of digits with an optional
* leading dash. Positive values may not have a leading 0.
* A peeked value that was composed entirely of digits with an optional leading dash. Positive
* values may not have a leading 0.
*/
private long peekedLong;
/**
* The number of characters in a peeked number literal.
*/
/** The number of characters in a peeked number literal. */
private int peekedNumberLength;
/**
* A peeked string that should be parsed on the next double, long or string.
* This is populated before a numeric value is parsed and used if that parsing
* fails.
* A peeked string that should be parsed on the next double, long or string. This is populated
* before a numeric value is parsed and used if that parsing fails.
*/
private @Nullable String peekedString;
@@ -119,7 +119,8 @@ final class JsonUtf8Reader extends JsonReader {
}
}
@Override public void beginArray() throws IOException {
@Override
public void beginArray() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -129,12 +130,13 @@ final class JsonUtf8Reader extends JsonReader {
pathIndices[stackSize - 1] = 0;
peeked = PEEKED_NONE;
} else {
throw new JsonDataException("Expected BEGIN_ARRAY but was " + peek()
+ " at path " + getPath());
throw new JsonDataException(
"Expected BEGIN_ARRAY but was " + peek() + " at path " + getPath());
}
}
@Override public void endArray() throws IOException {
@Override
public void endArray() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -144,12 +146,12 @@ final class JsonUtf8Reader extends JsonReader {
pathIndices[stackSize - 1]++;
peeked = PEEKED_NONE;
} else {
throw new JsonDataException("Expected END_ARRAY but was " + peek()
+ " at path " + getPath());
throw new JsonDataException("Expected END_ARRAY but was " + peek() + " at path " + getPath());
}
}
@Override public void beginObject() throws IOException {
@Override
public void beginObject() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -158,12 +160,13 @@ final class JsonUtf8Reader extends JsonReader {
pushScope(JsonScope.EMPTY_OBJECT);
peeked = PEEKED_NONE;
} else {
throw new JsonDataException("Expected BEGIN_OBJECT but was " + peek()
+ " at path " + getPath());
throw new JsonDataException(
"Expected BEGIN_OBJECT but was " + peek() + " at path " + getPath());
}
}
@Override public void endObject() throws IOException {
@Override
public void endObject() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -174,12 +177,13 @@ final class JsonUtf8Reader extends JsonReader {
pathIndices[stackSize - 1]++;
peeked = PEEKED_NONE;
} else {
throw new JsonDataException("Expected END_OBJECT but was " + peek()
+ " at path " + getPath());
throw new JsonDataException(
"Expected END_OBJECT but was " + peek() + " at path " + getPath());
}
}
@Override public boolean hasNext() throws IOException {
@Override
public boolean hasNext() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -187,7 +191,8 @@ final class JsonUtf8Reader extends JsonReader {
return p != PEEKED_END_OBJECT && p != PEEKED_END_ARRAY && p != PEEKED_EOF;
}
@Override public Token peek() throws IOException {
@Override
public Token peek() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -474,8 +479,9 @@ final class JsonUtf8Reader extends JsonReader {
return PEEKED_NONE; // Leading '0' prefix is not allowed (since it could be octal).
}
long newValue = value * 10 - (c - '0');
fitsInLong &= value > MIN_INCOMPLETE_INTEGER
|| (value == MIN_INCOMPLETE_INTEGER && newValue < value);
fitsInLong &=
value > MIN_INCOMPLETE_INTEGER
|| (value == MIN_INCOMPLETE_INTEGER && newValue < value);
value = newValue;
} else if (last == NUMBER_CHAR_DECIMAL) {
last = NUMBER_CHAR_FRACTION_DIGIT;
@@ -486,12 +492,15 @@ final class JsonUtf8Reader extends JsonReader {
}
// We've read a complete number. Decide if it's a PEEKED_LONG or a PEEKED_NUMBER.
if (last == NUMBER_CHAR_DIGIT && fitsInLong && (value != Long.MIN_VALUE || negative)
if (last == NUMBER_CHAR_DIGIT
&& fitsInLong
&& (value != Long.MIN_VALUE || negative)
&& (value != 0 || !negative)) {
peekedLong = negative ? value : -value;
buffer.skip(i);
return peeked = PEEKED_LONG;
} else if (last == NUMBER_CHAR_DIGIT || last == NUMBER_CHAR_FRACTION_DIGIT
} else if (last == NUMBER_CHAR_DIGIT
|| last == NUMBER_CHAR_FRACTION_DIGIT
|| last == NUMBER_CHAR_EXP_DIGIT) {
peekedNumberLength = i;
return peeked = PEEKED_NUMBER;
@@ -525,7 +534,8 @@ final class JsonUtf8Reader extends JsonReader {
}
}
@Override public String nextName() throws IOException {
@Override
public String nextName() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -547,7 +557,8 @@ final class JsonUtf8Reader extends JsonReader {
return result;
}
@Override public int selectName(Options options) throws IOException {
@Override
public int selectName(Options options) throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -584,7 +595,8 @@ final class JsonUtf8Reader extends JsonReader {
return result;
}
@Override public void skipName() throws IOException {
@Override
public void skipName() throws IOException {
if (failOnUnknown) {
// Capture the peeked value before nextName() since it will reset its value.
Token peeked = peek();
@@ -609,8 +621,8 @@ final class JsonUtf8Reader extends JsonReader {
}
/**
* If {@code name} is in {@code options} this consumes it and returns its index.
* Otherwise this returns -1 and no name is consumed.
* If {@code name} is in {@code options} this consumes it and returns its index. Otherwise this
* returns -1 and no name is consumed.
*/
private int findName(String name, Options options) {
for (int i = 0, size = options.strings.length; i < size; i++) {
@@ -624,7 +636,8 @@ final class JsonUtf8Reader extends JsonReader {
return -1;
}
@Override public String nextString() throws IOException {
@Override
public String nextString() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -651,7 +664,8 @@ final class JsonUtf8Reader extends JsonReader {
return result;
}
@Override public int selectString(Options options) throws IOException {
@Override
public int selectString(Options options) throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -684,8 +698,8 @@ final class JsonUtf8Reader extends JsonReader {
}
/**
* If {@code string} is in {@code options} this consumes it and returns its index.
* Otherwise this returns -1 and no string is consumed.
* If {@code string} is in {@code options} this consumes it and returns its index. Otherwise this
* returns -1 and no string is consumed.
*/
private int findString(String string, Options options) {
for (int i = 0, size = options.strings.length; i < size; i++) {
@@ -699,7 +713,8 @@ final class JsonUtf8Reader extends JsonReader {
return -1;
}
@Override public boolean nextBoolean() throws IOException {
@Override
public boolean nextBoolean() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -716,7 +731,8 @@ final class JsonUtf8Reader extends JsonReader {
throw new JsonDataException("Expected a boolean but was " + peek() + " at path " + getPath());
}
@Override public @Nullable <T> T nextNull() throws IOException {
@Override
public @Nullable <T> T nextNull() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -730,7 +746,8 @@ final class JsonUtf8Reader extends JsonReader {
}
}
@Override public double nextDouble() throws IOException {
@Override
public double nextDouble() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -759,12 +776,12 @@ final class JsonUtf8Reader extends JsonReader {
try {
result = Double.parseDouble(peekedString);
} catch (NumberFormatException e) {
throw new JsonDataException("Expected a double but was " + peekedString
+ " at path " + getPath());
throw new JsonDataException(
"Expected a double but was " + peekedString + " at path " + getPath());
}
if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) {
throw new JsonEncodingException("JSON forbids NaN and infinities: " + result
+ " at path " + getPath());
throw new JsonEncodingException(
"JSON forbids NaN and infinities: " + result + " at path " + getPath());
}
peekedString = null;
peeked = PEEKED_NONE;
@@ -772,7 +789,8 @@ final class JsonUtf8Reader extends JsonReader {
return result;
}
@Override public long nextLong() throws IOException {
@Override
public long nextLong() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -787,9 +805,10 @@ final class JsonUtf8Reader extends JsonReader {
if (p == PEEKED_NUMBER) {
peekedString = buffer.readUtf8(peekedNumberLength);
} else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_SINGLE_QUOTED) {
peekedString = p == PEEKED_DOUBLE_QUOTED
? nextQuotedValue(DOUBLE_QUOTE_OR_SLASH)
: nextQuotedValue(SINGLE_QUOTE_OR_SLASH);
peekedString =
p == PEEKED_DOUBLE_QUOTED
? nextQuotedValue(DOUBLE_QUOTE_OR_SLASH)
: nextQuotedValue(SINGLE_QUOTE_OR_SLASH);
try {
long result = Long.parseLong(peekedString);
peeked = PEEKED_NONE;
@@ -799,8 +818,7 @@ final class JsonUtf8Reader extends JsonReader {
// Fall back to parse as a BigDecimal below.
}
} else if (p != PEEKED_BUFFERED) {
throw new JsonDataException("Expected a long but was " + peek()
+ " at path " + getPath());
throw new JsonDataException("Expected a long but was " + peek() + " at path " + getPath());
}
peeked = PEEKED_BUFFERED;
@@ -809,8 +827,8 @@ final class JsonUtf8Reader extends JsonReader {
BigDecimal asDecimal = new BigDecimal(peekedString);
result = asDecimal.longValueExact();
} catch (NumberFormatException | ArithmeticException e) {
throw new JsonDataException("Expected a long but was " + peekedString
+ " at path " + getPath());
throw new JsonDataException(
"Expected a long but was " + peekedString + " at path " + getPath());
}
peekedString = null;
peeked = PEEKED_NONE;
@@ -879,7 +897,8 @@ final class JsonUtf8Reader extends JsonReader {
buffer.skip(i != -1L ? i : buffer.size());
}
@Override public int nextInt() throws IOException {
@Override
public int nextInt() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();
@@ -889,8 +908,8 @@ final class JsonUtf8Reader extends JsonReader {
if (p == PEEKED_LONG) {
result = (int) peekedLong;
if (peekedLong != result) { // Make sure no precision was lost casting to 'int'.
throw new JsonDataException("Expected an int but was " + peekedLong
+ " at path " + getPath());
throw new JsonDataException(
"Expected an int but was " + peekedLong + " at path " + getPath());
}
peeked = PEEKED_NONE;
pathIndices[stackSize - 1]++;
@@ -900,9 +919,10 @@ final class JsonUtf8Reader extends JsonReader {
if (p == PEEKED_NUMBER) {
peekedString = buffer.readUtf8(peekedNumberLength);
} else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_SINGLE_QUOTED) {
peekedString = p == PEEKED_DOUBLE_QUOTED
? nextQuotedValue(DOUBLE_QUOTE_OR_SLASH)
: nextQuotedValue(SINGLE_QUOTE_OR_SLASH);
peekedString =
p == PEEKED_DOUBLE_QUOTED
? nextQuotedValue(DOUBLE_QUOTE_OR_SLASH)
: nextQuotedValue(SINGLE_QUOTE_OR_SLASH);
try {
result = Integer.parseInt(peekedString);
peeked = PEEKED_NONE;
@@ -920,13 +940,13 @@ final class JsonUtf8Reader extends JsonReader {
try {
asDouble = Double.parseDouble(peekedString);
} catch (NumberFormatException e) {
throw new JsonDataException("Expected an int but was " + peekedString
+ " at path " + getPath());
throw new JsonDataException(
"Expected an int but was " + peekedString + " at path " + getPath());
}
result = (int) asDouble;
if (result != asDouble) { // Make sure no precision was lost casting to 'int'.
throw new JsonDataException("Expected an int but was " + peekedString
+ " at path " + getPath());
throw new JsonDataException(
"Expected an int but was " + peekedString + " at path " + getPath());
}
peekedString = null;
peeked = PEEKED_NONE;
@@ -934,7 +954,8 @@ final class JsonUtf8Reader extends JsonReader {
return result;
}
@Override public void close() throws IOException {
@Override
public void close() throws IOException {
peeked = PEEKED_NONE;
scopes[0] = JsonScope.CLOSED;
stackSize = 1;
@@ -942,7 +963,8 @@ final class JsonUtf8Reader extends JsonReader {
source.close();
}
@Override public void skipValue() throws IOException {
@Override
public void skipValue() throws IOException {
if (failOnUnknown) {
throw new JsonDataException("Cannot skip unexpected " + peek() + " at " + getPath());
}
@@ -982,8 +1004,7 @@ final class JsonUtf8Reader extends JsonReader {
} else if (p == PEEKED_NUMBER) {
buffer.skip(peekedNumberLength);
} else if (p == PEEKED_EOF) {
throw new JsonDataException(
"Expected a value but was " + peek() + " at path " + getPath());
throw new JsonDataException("Expected a value but was " + peek() + " at path " + getPath());
}
peeked = PEEKED_NONE;
} while (count != 0);
@@ -993,9 +1014,8 @@ final class JsonUtf8Reader extends JsonReader {
}
/**
* Returns the next character in the stream that is neither whitespace nor a
* part of a comment. When this returns, the returned character is always at
* {@code buffer.getByte(0)}.
* Returns the next character in the stream that is neither whitespace nor a part of a comment.
* When this returns, the returned character is always at {@code buffer.getByte(0)}.
*/
private int nextNonWhitespace(boolean throwOnEof) throws IOException {
/*
@@ -1067,18 +1087,15 @@ final class JsonUtf8Reader extends JsonReader {
}
/**
* Advances the position until after the next newline character. If the line
* is terminated by "\r\n", the '\n' must be consumed as whitespace by the
* caller.
* Advances the position until after the next newline character. If the line is terminated by
* "\r\n", the '\n' must be consumed as whitespace by the caller.
*/
private void skipToEndOfLine() throws IOException {
long index = source.indexOfElement(LINEFEED_OR_CARRIAGE_RETURN);
buffer.skip(index != -1 ? index + 1 : buffer.size());
}
/**
* Skips through the next closing block comment.
*/
/** Skips through the next closing block comment. */
private boolean skipToEndOfBlockComment() throws IOException {
long index = source.indexOf(CLOSING_BLOCK_COMMENT);
boolean found = index != -1;
@@ -1086,11 +1103,13 @@ final class JsonUtf8Reader extends JsonReader {
return found;
}
@Override public JsonReader peekJson() {
@Override
public JsonReader peekJson() {
return new JsonUtf8Reader(this);
}
@Override public String toString() {
@Override
public String toString() {
return "JsonReader(" + source + ")";
}
@@ -1158,7 +1177,8 @@ final class JsonUtf8Reader extends JsonReader {
}
}
@Override public void promoteNameToValue() throws IOException {
@Override
public void promoteNameToValue() throws IOException {
if (hasNext()) {
peekedString = nextName();
peeked = PEEKED_BUFFERED;

View File

@@ -15,14 +15,6 @@
*/
package com.squareup.moshi;
import java.io.IOException;
import javax.annotation.Nullable;
import okio.Buffer;
import okio.BufferedSink;
import okio.Okio;
import okio.Sink;
import okio.Timeout;
import static com.squareup.moshi.JsonScope.DANGLING_NAME;
import static com.squareup.moshi.JsonScope.EMPTY_ARRAY;
import static com.squareup.moshi.JsonScope.EMPTY_DOCUMENT;
@@ -32,6 +24,14 @@ import static com.squareup.moshi.JsonScope.NONEMPTY_DOCUMENT;
import static com.squareup.moshi.JsonScope.NONEMPTY_OBJECT;
import static com.squareup.moshi.JsonScope.STREAMING_VALUE;
import java.io.IOException;
import javax.annotation.Nullable;
import okio.Buffer;
import okio.BufferedSink;
import okio.Okio;
import okio.Sink;
import okio.Timeout;
final class JsonUtf8Writer extends JsonWriter {
/*
@@ -45,6 +45,7 @@ final class JsonUtf8Writer extends JsonWriter {
* error. http://code.google.com/p/google-gson/issues/detail?id=341
*/
private static final String[] REPLACEMENT_CHARS;
static {
REPLACEMENT_CHARS = new String[128];
for (int i = 0; i <= 0x1f; i++) {
@@ -75,12 +76,14 @@ final class JsonUtf8Writer extends JsonWriter {
pushScope(EMPTY_DOCUMENT);
}
@Override public void setIndent(String indent) {
@Override
public void setIndent(String indent) {
super.setIndent(indent);
this.separator = !indent.isEmpty() ? ": " : ":";
}
@Override public JsonWriter beginArray() throws IOException {
@Override
public JsonWriter beginArray() throws IOException {
if (promoteValueToName) {
throw new IllegalStateException(
"Array cannot be used as a map key in JSON at path " + getPath());
@@ -89,11 +92,13 @@ final class JsonUtf8Writer extends JsonWriter {
return open(EMPTY_ARRAY, NONEMPTY_ARRAY, '[');
}
@Override public JsonWriter endArray() throws IOException {
@Override
public JsonWriter endArray() throws IOException {
return close(EMPTY_ARRAY, NONEMPTY_ARRAY, ']');
}
@Override public JsonWriter beginObject() throws IOException {
@Override
public JsonWriter beginObject() throws IOException {
if (promoteValueToName) {
throw new IllegalStateException(
"Object cannot be used as a map key in JSON at path " + getPath());
@@ -102,15 +107,13 @@ final class JsonUtf8Writer extends JsonWriter {
return open(EMPTY_OBJECT, NONEMPTY_OBJECT, '{');
}
@Override public JsonWriter endObject() throws IOException {
@Override
public JsonWriter endObject() throws IOException {
promoteValueToName = false;
return close(EMPTY_OBJECT, NONEMPTY_OBJECT, '}');
}
/**
* Enters a new scope by appending any necessary whitespace and the given
* bracket.
*/
/** Enters a new scope by appending any necessary whitespace and the given bracket. */
private JsonWriter open(int empty, int nonempty, char openBracket) throws IOException {
if (stackSize == flattenStackSize
&& (scopes[stackSize - 1] == empty || scopes[stackSize - 1] == nonempty)) {
@@ -126,10 +129,7 @@ final class JsonUtf8Writer extends JsonWriter {
return this;
}
/**
* Closes the current scope by appending any necessary whitespace and the
* given bracket.
*/
/** Closes the current scope by appending any necessary whitespace and the given bracket. */
private JsonWriter close(int empty, int nonempty, char closeBracket) throws IOException {
int context = peekScope();
if (context != nonempty && context != empty) {
@@ -154,7 +154,8 @@ final class JsonUtf8Writer extends JsonWriter {
return this;
}
@Override public JsonWriter name(String name) throws IOException {
@Override
public JsonWriter name(String name) throws IOException {
if (name == null) {
throw new NullPointerException("name == null");
}
@@ -180,7 +181,8 @@ final class JsonUtf8Writer extends JsonWriter {
}
}
@Override public JsonWriter value(String value) throws IOException {
@Override
public JsonWriter value(String value) throws IOException {
if (value == null) {
return nullValue();
}
@@ -195,7 +197,8 @@ final class JsonUtf8Writer extends JsonWriter {
return this;
}
@Override public JsonWriter nullValue() throws IOException {
@Override
public JsonWriter nullValue() throws IOException {
if (promoteValueToName) {
throw new IllegalStateException(
"null cannot be used as a map key in JSON at path " + getPath());
@@ -214,7 +217,8 @@ final class JsonUtf8Writer extends JsonWriter {
return this;
}
@Override public JsonWriter value(boolean value) throws IOException {
@Override
public JsonWriter value(boolean value) throws IOException {
if (promoteValueToName) {
throw new IllegalStateException(
"Boolean cannot be used as a map key in JSON at path " + getPath());
@@ -226,14 +230,16 @@ final class JsonUtf8Writer extends JsonWriter {
return this;
}
@Override public JsonWriter value(Boolean value) throws IOException {
@Override
public JsonWriter value(Boolean value) throws IOException {
if (value == null) {
return nullValue();
}
return value(value.booleanValue());
}
@Override public JsonWriter value(double value) throws IOException {
@Override
public JsonWriter value(double value) throws IOException {
if (!lenient && (Double.isNaN(value) || Double.isInfinite(value))) {
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
}
@@ -248,7 +254,8 @@ final class JsonUtf8Writer extends JsonWriter {
return this;
}
@Override public JsonWriter value(long value) throws IOException {
@Override
public JsonWriter value(long value) throws IOException {
if (promoteValueToName) {
promoteValueToName = false;
return name(Long.toString(value));
@@ -260,7 +267,8 @@ final class JsonUtf8Writer extends JsonWriter {
return this;
}
@Override public JsonWriter value(@Nullable Number value) throws IOException {
@Override
public JsonWriter value(@Nullable Number value) throws IOException {
if (value == null) {
return nullValue();
}
@@ -281,7 +289,8 @@ final class JsonUtf8Writer extends JsonWriter {
return this;
}
@Override public BufferedSink valueSink() throws IOException {
@Override
public BufferedSink valueSink() throws IOException {
if (promoteValueToName) {
throw new IllegalStateException(
"BufferedSink cannot be used as a map key in JSON at path " + getPath());
@@ -289,34 +298,39 @@ final class JsonUtf8Writer extends JsonWriter {
writeDeferredName();
beforeValue();
pushScope(STREAMING_VALUE);
return Okio.buffer(new Sink() {
@Override public void write(Buffer source, long byteCount) throws IOException {
sink.write(source, byteCount);
}
return Okio.buffer(
new Sink() {
@Override
public void write(Buffer source, long byteCount) throws IOException {
sink.write(source, byteCount);
}
@Override public void close() {
if (peekScope() != STREAMING_VALUE) {
throw new AssertionError();
}
stackSize--; // Remove STREAMING_VALUE from the stack.
pathIndices[stackSize - 1]++;
}
@Override
public void close() {
if (peekScope() != STREAMING_VALUE) {
throw new AssertionError();
}
stackSize--; // Remove STREAMING_VALUE from the stack.
pathIndices[stackSize - 1]++;
}
@Override public void flush() throws IOException {
sink.flush();
}
@Override
public void flush() throws IOException {
sink.flush();
}
@Override public Timeout timeout() {
return Timeout.NONE;
}
});
@Override
public Timeout timeout() {
return Timeout.NONE;
}
});
}
/**
* Ensures all buffered data is written to the underlying {@link Sink}
* and flushes that writer.
* Ensures all buffered data is written to the underlying {@link Sink} and flushes that writer.
*/
@Override public void flush() throws IOException {
@Override
public void flush() throws IOException {
if (stackSize == 0) {
throw new IllegalStateException("JsonWriter is closed.");
}
@@ -328,7 +342,8 @@ final class JsonUtf8Writer extends JsonWriter {
*
* @throws JsonDataException if the JSON document is incomplete.
*/
@Override public void close() throws IOException {
@Override
public void close() throws IOException {
sink.close();
int size = stackSize;
@@ -386,8 +401,8 @@ final class JsonUtf8Writer extends JsonWriter {
}
/**
* Inserts any necessary separators and whitespace before a name. Also
* adjusts the stack to expect the name's value.
* Inserts any necessary separators and whitespace before a name. Also adjusts the stack to expect
* the name's value.
*/
private void beforeName() throws IOException {
int context = peekScope();
@@ -401,9 +416,8 @@ final class JsonUtf8Writer extends JsonWriter {
}
/**
* Inserts any necessary separators and whitespace before a literal value,
* inline array, or inline object. Also adjusts the stack to expect either a
* closing bracket or another element.
* Inserts any necessary separators and whitespace before a literal value, inline array, or inline
* object. Also adjusts the stack to expect either a closing bracket or another element.
*/
@SuppressWarnings("fallthrough")
private void beforeValue() throws IOException {
@@ -411,8 +425,7 @@ final class JsonUtf8Writer extends JsonWriter {
switch (peekScope()) {
case NONEMPTY_DOCUMENT:
if (!lenient) {
throw new IllegalStateException(
"JSON must have only one top-level value.");
throw new IllegalStateException("JSON must have only one top-level value.");
}
// fall-through
case EMPTY_DOCUMENT: // first in document

View File

@@ -15,6 +15,8 @@
*/
package com.squareup.moshi;
import static com.squareup.moshi.JsonScope.CLOSED;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Arrays;
@@ -23,8 +25,6 @@ import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import static com.squareup.moshi.JsonScope.CLOSED;
/**
* This class reads a JSON document by traversing a Java object comprising maps, lists, and JSON
* primitives. It does depth-first traversal keeping a stack starting with the root object. During
@@ -70,11 +70,12 @@ final class JsonValueReader extends JsonReader {
}
}
@Override public void beginArray() throws IOException {
@Override
public void beginArray() throws IOException {
List<?> peeked = require(List.class, Token.BEGIN_ARRAY);
JsonIterator iterator = new JsonIterator(
Token.END_ARRAY, peeked.toArray(new Object[peeked.size()]), 0);
JsonIterator iterator =
new JsonIterator(Token.END_ARRAY, peeked.toArray(new Object[peeked.size()]), 0);
stack[stackSize - 1] = iterator;
scopes[stackSize - 1] = JsonScope.EMPTY_ARRAY;
pathIndices[stackSize - 1] = 0;
@@ -85,7 +86,8 @@ final class JsonValueReader extends JsonReader {
}
}
@Override public void endArray() throws IOException {
@Override
public void endArray() throws IOException {
JsonIterator peeked = require(JsonIterator.class, Token.END_ARRAY);
if (peeked.endToken != Token.END_ARRAY || peeked.hasNext()) {
throw typeMismatch(peeked, Token.END_ARRAY);
@@ -93,11 +95,12 @@ final class JsonValueReader extends JsonReader {
remove();
}
@Override public void beginObject() throws IOException {
@Override
public void beginObject() throws IOException {
Map<?, ?> peeked = require(Map.class, Token.BEGIN_OBJECT);
JsonIterator iterator = new JsonIterator(
Token.END_OBJECT, peeked.entrySet().toArray(new Object[peeked.size()]), 0);
JsonIterator iterator =
new JsonIterator(Token.END_OBJECT, peeked.entrySet().toArray(new Object[peeked.size()]), 0);
stack[stackSize - 1] = iterator;
scopes[stackSize - 1] = JsonScope.EMPTY_OBJECT;
@@ -107,7 +110,8 @@ final class JsonValueReader extends JsonReader {
}
}
@Override public void endObject() throws IOException {
@Override
public void endObject() throws IOException {
JsonIterator peeked = require(JsonIterator.class, Token.END_OBJECT);
if (peeked.endToken != Token.END_OBJECT || peeked.hasNext()) {
throw typeMismatch(peeked, Token.END_OBJECT);
@@ -116,14 +120,16 @@ final class JsonValueReader extends JsonReader {
remove();
}
@Override public boolean hasNext() throws IOException {
@Override
public boolean hasNext() throws IOException {
if (stackSize == 0) return false;
Object peeked = stack[stackSize - 1];
return !(peeked instanceof Iterator) || ((Iterator) peeked).hasNext();
}
@Override public Token peek() throws IOException {
@Override
public Token peek() throws IOException {
if (stackSize == 0) return Token.END_DOCUMENT;
// If the top of the stack is an iterator, take its first element and push it on the stack.
@@ -141,7 +147,8 @@ final class JsonValueReader extends JsonReader {
throw typeMismatch(peeked, "a JSON value");
}
@Override public String nextName() throws IOException {
@Override
public String nextName() throws IOException {
Map.Entry<?, ?> peeked = require(Map.Entry.class, Token.NAME);
// Swap the Map.Entry for its value on the stack and return its key.
@@ -151,7 +158,8 @@ final class JsonValueReader extends JsonReader {
return result;
}
@Override public int selectName(Options options) throws IOException {
@Override
public int selectName(Options options) throws IOException {
Map.Entry<?, ?> peeked = require(Map.Entry.class, Token.NAME);
String name = stringKey(peeked);
for (int i = 0, length = options.strings.length; i < length; i++) {
@@ -165,7 +173,8 @@ final class JsonValueReader extends JsonReader {
return -1;
}
@Override public void skipName() throws IOException {
@Override
public void skipName() throws IOException {
if (failOnUnknown) {
// Capture the peeked value before nextName() since it will reset its value.
Token peeked = peek();
@@ -180,7 +189,8 @@ final class JsonValueReader extends JsonReader {
pathNames[stackSize - 2] = "null";
}
@Override public String nextString() throws IOException {
@Override
public String nextString() throws IOException {
Object peeked = (stackSize != 0 ? stack[stackSize - 1] : null);
if (peeked instanceof String) {
remove();
@@ -196,7 +206,8 @@ final class JsonValueReader extends JsonReader {
throw typeMismatch(peeked, Token.STRING);
}
@Override public int selectString(Options options) throws IOException {
@Override
public int selectString(Options options) throws IOException {
Object peeked = (stackSize != 0 ? stack[stackSize - 1] : null);
if (!(peeked instanceof String)) {
@@ -216,19 +227,22 @@ final class JsonValueReader extends JsonReader {
return -1;
}
@Override public boolean nextBoolean() throws IOException {
@Override
public boolean nextBoolean() throws IOException {
Boolean peeked = require(Boolean.class, Token.BOOLEAN);
remove();
return peeked;
}
@Override public @Nullable <T> T nextNull() throws IOException {
@Override
public @Nullable <T> T nextNull() throws IOException {
require(Void.class, Token.NULL);
remove();
return null;
}
@Override public double nextDouble() throws IOException {
@Override
public double nextDouble() throws IOException {
Object peeked = require(Object.class, Token.NUMBER);
double result;
@@ -244,14 +258,15 @@ final class JsonValueReader extends JsonReader {
throw typeMismatch(peeked, Token.NUMBER);
}
if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) {
throw new JsonEncodingException("JSON forbids NaN and infinities: " + result
+ " at path " + getPath());
throw new JsonEncodingException(
"JSON forbids NaN and infinities: " + result + " at path " + getPath());
}
remove();
return result;
}
@Override public long nextLong() throws IOException {
@Override
public long nextLong() throws IOException {
Object peeked = require(Object.class, Token.NUMBER);
long result;
@@ -275,7 +290,8 @@ final class JsonValueReader extends JsonReader {
return result;
}
@Override public int nextInt() throws IOException {
@Override
public int nextInt() throws IOException {
Object peeked = require(Object.class, Token.NUMBER);
int result;
@@ -299,7 +315,8 @@ final class JsonValueReader extends JsonReader {
return result;
}
@Override public void skipValue() throws IOException {
@Override
public void skipValue() throws IOException {
if (failOnUnknown) {
throw new JsonDataException("Cannot skip unexpected " + peek() + " at " + getPath());
}
@@ -326,18 +343,21 @@ final class JsonValueReader extends JsonReader {
}
}
@Override public JsonReader peekJson() {
@Override
public JsonReader peekJson() {
return new JsonValueReader(this);
}
@Override public void promoteNameToValue() throws IOException {
@Override
public void promoteNameToValue() throws IOException {
if (hasNext()) {
String name = nextName();
push(name);
}
}
@Override public void close() throws IOException {
@Override
public void close() throws IOException {
Arrays.fill(stack, 0, stackSize, null);
stack[0] = JSON_READER_CLOSED;
scopes[0] = CLOSED;
@@ -413,19 +433,23 @@ final class JsonValueReader extends JsonReader {
this.next = next;
}
@Override public boolean hasNext() {
@Override
public boolean hasNext() {
return next < array.length;
}
@Override public Object next() {
@Override
public Object next() {
return array[next++];
}
@Override public void remove() {
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override protected JsonIterator clone() {
@Override
protected JsonIterator clone() {
// No need to copy the array; it's read-only.
return new JsonIterator(endToken, array, next);
}

View File

@@ -15,6 +15,14 @@
*/
package com.squareup.moshi;
import static com.squareup.moshi.JsonScope.EMPTY_ARRAY;
import static com.squareup.moshi.JsonScope.EMPTY_DOCUMENT;
import static com.squareup.moshi.JsonScope.EMPTY_OBJECT;
import static com.squareup.moshi.JsonScope.NONEMPTY_DOCUMENT;
import static com.squareup.moshi.JsonScope.STREAMING_VALUE;
import static java.lang.Double.NEGATIVE_INFINITY;
import static java.lang.Double.POSITIVE_INFINITY;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
@@ -26,14 +34,6 @@ import okio.BufferedSink;
import okio.ForwardingSink;
import okio.Okio;
import static com.squareup.moshi.JsonScope.EMPTY_ARRAY;
import static com.squareup.moshi.JsonScope.EMPTY_DOCUMENT;
import static com.squareup.moshi.JsonScope.EMPTY_OBJECT;
import static com.squareup.moshi.JsonScope.NONEMPTY_DOCUMENT;
import static com.squareup.moshi.JsonScope.STREAMING_VALUE;
import static java.lang.Double.NEGATIVE_INFINITY;
import static java.lang.Double.POSITIVE_INFINITY;
/** Writes JSON by building a Java object comprising maps, lists, and JSON primitives. */
final class JsonValueWriter extends JsonWriter {
Object[] stack = new Object[32];
@@ -51,7 +51,8 @@ final class JsonValueWriter extends JsonWriter {
return stack[0];
}
@Override public JsonWriter beginArray() throws IOException {
@Override
public JsonWriter beginArray() throws IOException {
if (promoteValueToName) {
throw new IllegalStateException(
"Array cannot be used as a map key in JSON at path " + getPath());
@@ -70,7 +71,8 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public JsonWriter endArray() throws IOException {
@Override
public JsonWriter endArray() throws IOException {
if (peekScope() != EMPTY_ARRAY) {
throw new IllegalStateException("Nesting problem.");
}
@@ -85,7 +87,8 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public JsonWriter beginObject() throws IOException {
@Override
public JsonWriter beginObject() throws IOException {
if (promoteValueToName) {
throw new IllegalStateException(
"Object cannot be used as a map key in JSON at path " + getPath());
@@ -103,7 +106,8 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public JsonWriter endObject() throws IOException {
@Override
public JsonWriter endObject() throws IOException {
if (peekScope() != EMPTY_OBJECT) {
throw new IllegalStateException("Nesting problem.");
}
@@ -123,7 +127,8 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public JsonWriter name(String name) throws IOException {
@Override
public JsonWriter name(String name) throws IOException {
if (name == null) {
throw new NullPointerException("name == null");
}
@@ -138,7 +143,8 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public JsonWriter value(@Nullable String value) throws IOException {
@Override
public JsonWriter value(@Nullable String value) throws IOException {
if (promoteValueToName) {
promoteValueToName = false;
return name(value);
@@ -148,7 +154,8 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public JsonWriter nullValue() throws IOException {
@Override
public JsonWriter nullValue() throws IOException {
if (promoteValueToName) {
throw new IllegalStateException(
"null cannot be used as a map key in JSON at path " + getPath());
@@ -158,7 +165,8 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public JsonWriter value(boolean value) throws IOException {
@Override
public JsonWriter value(boolean value) throws IOException {
if (promoteValueToName) {
throw new IllegalStateException(
"Boolean cannot be used as a map key in JSON at path " + getPath());
@@ -168,7 +176,8 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public JsonWriter value(@Nullable Boolean value) throws IOException {
@Override
public JsonWriter value(@Nullable Boolean value) throws IOException {
if (promoteValueToName) {
throw new IllegalStateException(
"Boolean cannot be used as a map key in JSON at path " + getPath());
@@ -178,7 +187,8 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public JsonWriter value(double value) throws IOException {
@Override
public JsonWriter value(double value) throws IOException {
if (!lenient
&& (Double.isNaN(value) || value == NEGATIVE_INFINITY || value == POSITIVE_INFINITY)) {
throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
@@ -192,7 +202,8 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public JsonWriter value(long value) throws IOException {
@Override
public JsonWriter value(long value) throws IOException {
if (promoteValueToName) {
promoteValueToName = false;
return name(Long.toString(value));
@@ -202,7 +213,8 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public JsonWriter value(@Nullable Number value) throws IOException {
@Override
public JsonWriter value(@Nullable Number value) throws IOException {
// If it's trivially converted to a long, do that.
if (value instanceof Byte
|| value instanceof Short
@@ -221,9 +233,8 @@ final class JsonValueWriter extends JsonWriter {
}
// Everything else gets converted to a BigDecimal.
BigDecimal bigDecimalValue = value instanceof BigDecimal
? ((BigDecimal) value)
: new BigDecimal(value.toString());
BigDecimal bigDecimalValue =
value instanceof BigDecimal ? ((BigDecimal) value) : new BigDecimal(value.toString());
if (promoteValueToName) {
promoteValueToName = false;
return name(bigDecimalValue.toString());
@@ -233,7 +244,8 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public BufferedSink valueSink() {
@Override
public BufferedSink valueSink() {
if (promoteValueToName) {
throw new IllegalStateException(
"BufferedSink cannot be used as a map key in JSON at path " + getPath());
@@ -244,27 +256,30 @@ final class JsonValueWriter extends JsonWriter {
pushScope(STREAMING_VALUE);
final Buffer buffer = new Buffer();
return Okio.buffer(new ForwardingSink(buffer) {
@Override public void close() throws IOException {
if (peekScope() != STREAMING_VALUE || stack[stackSize] != null) {
throw new AssertionError();
}
stackSize--; // Remove STREAMING_VALUE from the stack.
return Okio.buffer(
new ForwardingSink(buffer) {
@Override
public void close() throws IOException {
if (peekScope() != STREAMING_VALUE || stack[stackSize] != null) {
throw new AssertionError();
}
stackSize--; // Remove STREAMING_VALUE from the stack.
Object value = JsonReader.of(buffer).readJsonValue();
boolean serializeNulls = JsonValueWriter.this.serializeNulls;
JsonValueWriter.this.serializeNulls = true;
try {
add(value);
} finally {
JsonValueWriter.this.serializeNulls = serializeNulls;
}
pathIndices[stackSize - 1]++;
}
});
Object value = JsonReader.of(buffer).readJsonValue();
boolean serializeNulls = JsonValueWriter.this.serializeNulls;
JsonValueWriter.this.serializeNulls = true;
try {
add(value);
} finally {
JsonValueWriter.this.serializeNulls = serializeNulls;
}
pathIndices[stackSize - 1]++;
}
});
}
@Override public void close() throws IOException {
@Override
public void close() throws IOException {
int size = stackSize;
if (size > 1 || size == 1 && scopes[size - 1] != NONEMPTY_DOCUMENT) {
throw new IOException("Incomplete document");
@@ -272,7 +287,8 @@ final class JsonValueWriter extends JsonWriter {
stackSize = 0;
}
@Override public void flush() throws IOException {
@Override
public void flush() throws IOException {
if (stackSize == 0) {
throw new IllegalStateException("JsonWriter is closed.");
}
@@ -294,8 +310,15 @@ final class JsonValueWriter extends JsonWriter {
Map<String, Object> map = (Map<String, Object>) stack[stackSize - 1];
Object replaced = map.put(deferredName, newTop);
if (replaced != null) {
throw new IllegalArgumentException("Map key '" + deferredName
+ "' has multiple values at path " + getPath() + ": " + replaced + " and " + newTop);
throw new IllegalArgumentException(
"Map key '"
+ deferredName
+ "' has multiple values at path "
+ getPath()
+ ": "
+ replaced
+ " and "
+ newTop);
}
}
deferredName = null;

View File

@@ -15,6 +15,11 @@
*/
package com.squareup.moshi;
import static com.squareup.moshi.JsonScope.EMPTY_ARRAY;
import static com.squareup.moshi.JsonScope.EMPTY_OBJECT;
import static com.squareup.moshi.JsonScope.NONEMPTY_ARRAY;
import static com.squareup.moshi.JsonScope.NONEMPTY_OBJECT;
import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException;
@@ -26,36 +31,32 @@ import javax.annotation.Nullable;
import okio.BufferedSink;
import okio.BufferedSource;
import static com.squareup.moshi.JsonScope.EMPTY_ARRAY;
import static com.squareup.moshi.JsonScope.EMPTY_OBJECT;
import static com.squareup.moshi.JsonScope.NONEMPTY_ARRAY;
import static com.squareup.moshi.JsonScope.NONEMPTY_OBJECT;
/**
* Writes a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>)
* encoded value to a stream, one token at a time. The stream includes both
* literal values (strings, numbers, booleans and nulls) as well as the begin
* and end delimiters of objects and arrays.
* Writes a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>) encoded value to a
* stream, one token at a time. The stream includes both literal values (strings, numbers, booleans
* and nulls) as well as the begin and end delimiters of objects and arrays.
*
* <h3>Encoding JSON</h3>
* To encode your data as JSON, create a new {@code JsonWriter}. Each JSON
* document must contain one top-level array or object. Call methods on the
* writer as you walk the structure's contents, nesting arrays and objects as
* necessary:
*
* To encode your data as JSON, create a new {@code JsonWriter}. Each JSON document must contain one
* top-level array or object. Call methods on the writer as you walk the structure's contents,
* nesting arrays and objects as necessary:
*
* <ul>
* <li>To write <strong>arrays</strong>, first call {@link #beginArray()}.
* Write each of the array's elements with the appropriate {@link #value}
* methods or by nesting other arrays and objects. Finally close the array
* using {@link #endArray()}.
* <li>To write <strong>objects</strong>, first call {@link #beginObject()}.
* Write each of the object's properties by alternating calls to
* {@link #name} with the property's value. Write property values with the
* appropriate {@link #value} method or by nesting other objects or arrays.
* Finally close the object using {@link #endObject()}.
* <li>To write <strong>arrays</strong>, first call {@link #beginArray()}. Write each of the
* array's elements with the appropriate {@link #value} methods or by nesting other arrays and
* objects. Finally close the array using {@link #endArray()}.
* <li>To write <strong>objects</strong>, first call {@link #beginObject()}. Write each of the
* object's properties by alternating calls to {@link #name} with the property's value. Write
* property values with the appropriate {@link #value} method or by nesting other objects or
* arrays. Finally close the object using {@link #endObject()}.
* </ul>
*
* <h3>Example</h3>
* Suppose we'd like to encode a stream of messages such as the following: <pre> {@code
*
* Suppose we'd like to encode a stream of messages such as the following:
*
* <pre>{@code
* [
* {
* "id": 912345678901,
@@ -75,56 +76,61 @@ import static com.squareup.moshi.JsonScope.NONEMPTY_OBJECT;
* "followers_count": 2
* }
* }
* ]}</pre>
* This code encodes the above structure: <pre> {@code
* public void writeJsonStream(BufferedSink sink, List<Message> messages) throws IOException {
* JsonWriter writer = JsonWriter.of(sink);
* writer.setIndent(" ");
* writeMessagesArray(writer, messages);
* writer.close();
* ]
* }</pre>
*
* This code encodes the above structure:
*
* <pre>{@code
* public void writeJsonStream(BufferedSink sink, List<Message> messages) throws IOException {
* JsonWriter writer = JsonWriter.of(sink);
* writer.setIndent(" ");
* writeMessagesArray(writer, messages);
* writer.close();
* }
*
* public void writeMessagesArray(JsonWriter writer, List<Message> messages) throws IOException {
* writer.beginArray();
* for (Message message : messages) {
* writeMessage(writer, message);
* }
* writer.endArray();
* }
*
* public void writeMessagesArray(JsonWriter writer, List<Message> messages) throws IOException {
* writer.beginArray();
* for (Message message : messages) {
* writeMessage(writer, message);
* }
* writer.endArray();
* public void writeMessage(JsonWriter writer, Message message) throws IOException {
* writer.beginObject();
* writer.name("id").value(message.getId());
* writer.name("text").value(message.getText());
* if (message.getGeo() != null) {
* writer.name("geo");
* writeDoublesArray(writer, message.getGeo());
* } else {
* writer.name("geo").nullValue();
* }
* writer.name("user");
* writeUser(writer, message.getUser());
* writer.endObject();
* }
*
* public void writeMessage(JsonWriter writer, Message message) throws IOException {
* writer.beginObject();
* writer.name("id").value(message.getId());
* writer.name("text").value(message.getText());
* if (message.getGeo() != null) {
* writer.name("geo");
* writeDoublesArray(writer, message.getGeo());
* } else {
* writer.name("geo").nullValue();
* }
* writer.name("user");
* writeUser(writer, message.getUser());
* writer.endObject();
* public void writeUser(JsonWriter writer, User user) throws IOException {
* writer.beginObject();
* writer.name("name").value(user.getName());
* writer.name("followers_count").value(user.getFollowersCount());
* writer.endObject();
* }
*
* public void writeDoublesArray(JsonWriter writer, List<Double> doubles) throws IOException {
* writer.beginArray();
* for (Double value : doubles) {
* writer.value(value);
* }
* writer.endArray();
* }
* }</pre>
*
* public void writeUser(JsonWriter writer, User user) throws IOException {
* writer.beginObject();
* writer.name("name").value(user.getName());
* writer.name("followers_count").value(user.getFollowersCount());
* writer.endObject();
* }
*
* public void writeDoublesArray(JsonWriter writer, List<Double> doubles) throws IOException {
* writer.beginArray();
* for (Double value : doubles) {
* writer.value(value);
* }
* writer.endArray();
* }}</pre>
*
* <p>Each {@code JsonWriter} may be used to write a single JSON stream.
* Instances of this class are not thread safe. Calls that would result in a
* malformed JSON string will fail with an {@link IllegalStateException}.
* <p>Each {@code JsonWriter} may be used to write a single JSON stream. Instances of this class are
* not thread safe. Calls that would result in a malformed JSON string will fail with an {@link
* IllegalStateException}.
*/
public abstract class JsonWriter implements Closeable, Flushable {
// The nesting stack. Using a manual array rather than an ArrayList saves 20%. This stack will
@@ -140,6 +146,7 @@ public abstract class JsonWriter implements Closeable, Flushable {
* pretty printing.
*/
String indent;
boolean lenient;
boolean serializeNulls;
boolean promoteValueToName;
@@ -148,11 +155,11 @@ public abstract class JsonWriter implements Closeable, Flushable {
* Controls the deepest stack size that has begin/end pairs flattened:
*
* <ul>
* <li>If -1, no begin/end pairs are being suppressed.
* <li>If positive, this is the deepest stack size whose begin/end pairs are eligible to be
* flattened.
* <li>If negative, it is the bitwise inverse (~) of the deepest stack size whose begin/end
* pairs have been flattened.
* <li>If -1, no begin/end pairs are being suppressed.
* <li>If positive, this is the deepest stack size whose begin/end pairs are eligible to be
* flattened.
* <li>If negative, it is the bitwise inverse (~) of the deepest stack size whose begin/end
* pairs have been flattened.
* </ul>
*
* <p>We differentiate between what layer would be flattened (positive) from what layer is being
@@ -165,7 +172,8 @@ public abstract class JsonWriter implements Closeable, Flushable {
int flattenStackSize = -1;
/** Returns a new instance that writes UTF-8 encoded JSON to {@code sink}. */
@CheckReturnValue public static JsonWriter of(BufferedSink sink) {
@CheckReturnValue
public static JsonWriter of(BufferedSink sink) {
return new JsonUtf8Writer(sink);
}
@@ -210,10 +218,9 @@ public abstract class JsonWriter implements Closeable, Flushable {
}
/**
* Sets the indentation string to be repeated for each level of indentation
* in the encoded document. If {@code indent.isEmpty()} the encoded document
* will be compact. Otherwise the encoded document will be more
* human-readable.
* Sets the indentation string to be repeated for each level of indentation in the encoded
* document. If {@code indent.isEmpty()} the encoded document will be compact. Otherwise the
* encoded document will be more human-readable.
*
* @param indent a string containing only whitespace.
*/
@@ -222,55 +229,56 @@ public abstract class JsonWriter implements Closeable, Flushable {
}
/**
* Returns a string containing only whitespace, used for each level of
* indentation. If empty, the encoded document will be compact.
* Returns a string containing only whitespace, used for each level of indentation. If empty, the
* encoded document will be compact.
*/
@CheckReturnValue public final String getIndent() {
@CheckReturnValue
public final String getIndent() {
return indent != null ? indent : "";
}
/**
* Configure this writer to relax its syntax rules. By default, this writer
* only emits well-formed JSON as specified by <a
* href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>. Setting the writer
* to lenient permits the following:
* Configure this writer to relax its syntax rules. By default, this writer only emits well-formed
* JSON as specified by <a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>. Setting the
* writer to lenient permits the following:
*
* <ul>
* <li>Top-level values of any type. With strict writing, the top-level
* value must be an object or an array.
* <li>Numbers may be {@linkplain Double#isNaN() NaNs} or {@linkplain
* Double#isInfinite() infinities}.
* <li>Top-level values of any type. With strict writing, the top-level value must be an object
* or an array.
* <li>Numbers may be {@linkplain Double#isNaN() NaNs} or {@linkplain Double#isInfinite()
* infinities}.
* </ul>
*/
public final void setLenient(boolean lenient) {
this.lenient = lenient;
}
/**
* Returns true if this writer has relaxed syntax rules.
*/
@CheckReturnValue public final boolean isLenient() {
/** Returns true if this writer has relaxed syntax rules. */
@CheckReturnValue
public final boolean isLenient() {
return lenient;
}
/**
* Sets whether object members are serialized when their value is null.
* This has no impact on array elements. The default is false.
* Sets whether object members are serialized when their value is null. This has no impact on
* array elements. The default is false.
*/
public final void setSerializeNulls(boolean serializeNulls) {
this.serializeNulls = serializeNulls;
}
/**
* Returns true if object members are serialized when their value is null.
* This has no impact on array elements. The default is false.
* Returns true if object members are serialized when their value is null. This has no impact on
* array elements. The default is false.
*/
@CheckReturnValue public final boolean getSerializeNulls() {
@CheckReturnValue
public final boolean getSerializeNulls() {
return serializeNulls;
}
/**
* Begins encoding a new array. Each call to this method must be paired with
* a call to {@link #endArray}.
* Begins encoding a new array. Each call to this method must be paired with a call to {@link
* #endArray}.
*
* @return this writer.
*/
@@ -284,8 +292,8 @@ public abstract class JsonWriter implements Closeable, Flushable {
public abstract JsonWriter endArray() throws IOException;
/**
* Begins encoding a new object. Each call to this method must be paired
* with a call to {@link #endObject}.
* Begins encoding a new object. Each call to this method must be paired with a call to {@link
* #endObject}.
*
* @return this writer.
*/
@@ -338,8 +346,8 @@ public abstract class JsonWriter implements Closeable, Flushable {
/**
* Encodes {@code value}.
*
* @param value a finite value. May not be {@linkplain Double#isNaN() NaNs} or
* {@linkplain Double#isInfinite() infinities}.
* @param value a finite value. May not be {@linkplain Double#isNaN() NaNs} or {@linkplain
* Double#isInfinite() infinities}.
* @return this writer.
*/
public abstract JsonWriter value(double value) throws IOException;
@@ -354,15 +362,15 @@ public abstract class JsonWriter implements Closeable, Flushable {
/**
* Encodes {@code value}.
*
* @param value a finite value. May not be {@linkplain Double#isNaN() NaNs} or
* {@linkplain Double#isInfinite() infinities}.
* @param value a finite value. May not be {@linkplain Double#isNaN() NaNs} or {@linkplain
* Double#isInfinite() infinities}.
* @return this writer.
*/
public abstract JsonWriter value(@Nullable Number value) throws IOException;
/**
* Writes {@code source} directly without encoding its contents. Equivalent to
* {@code try (BufferedSink sink = writer.valueSink()) { source.readAll(sink): }}
* Writes {@code source} directly without encoding its contents. Equivalent to {@code try
* (BufferedSink sink = writer.valueSink()) { source.readAll(sink): }}
*
* @see #valueSink()
*/
@@ -379,10 +387,10 @@ public abstract class JsonWriter implements Closeable, Flushable {
/**
* Returns a {@link BufferedSink} into which arbitrary data can be written without any additional
* encoding. You <b>must</b> call {@link BufferedSink#close()} before interacting with this
* {@code JsonWriter} instance again.
* <p>
* Since no validation is performed, options like {@link #setSerializeNulls} and other writer
* encoding. You <b>must</b> call {@link BufferedSink#close()} before interacting with this {@code
* JsonWriter} instance again.
*
* <p>Since no validation is performed, options like {@link #setSerializeNulls} and other writer
* configurations are not respected.
*/
@CheckReturnValue
@@ -400,9 +408,10 @@ public abstract class JsonWriter implements Closeable, Flushable {
for (Map.Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) {
Object key = entry.getKey();
if (!(key instanceof String)) {
throw new IllegalArgumentException(key == null
? "Map keys must be non-null"
: "Map keys must be of type String: " + key.getClass().getName());
throw new IllegalArgumentException(
key == null
? "Map keys must be non-null"
: "Map keys must be of type String: " + key.getClass().getName());
}
name(((String) key));
jsonValue(entry.getValue());
@@ -446,14 +455,14 @@ public abstract class JsonWriter implements Closeable, Flushable {
*
* <p>In this example, calling this method allows two sequential calls to {@link #value(String)}
* to produce the object, {@code {"a": "b"}}.
* <pre> {@code
*
* JsonWriter writer = JsonWriter.of(...);
* writer.beginObject();
* writer.promoteValueToName();
* writer.value("a");
* writer.value("b");
* writer.endObject();
* <pre>{@code
* JsonWriter writer = JsonWriter.of(...);
* writer.beginObject();
* writer.promoteValueToName();
* writer.value("a");
* writer.value("b");
* writer.endObject();
* }</pre>
*/
public final void promoteValueToName() throws IOException {
@@ -472,68 +481,66 @@ public abstract class JsonWriter implements Closeable, Flushable {
* <p>For example, the following creates JSON with nested arrays: {@code [1,[2,3,4],5]}.
*
* <pre>{@code
* JsonAdapter<List<Integer>> integersAdapter = ...
*
* JsonAdapter<List<Integer>> integersAdapter = ...
*
* public void writeNumbers(JsonWriter writer) {
* writer.beginArray();
* writer.value(1);
* integersAdapter.toJson(writer, Arrays.asList(2, 3, 4));
* writer.value(5);
* writer.endArray();
* }
* public void writeNumbers(JsonWriter writer) {
* writer.beginArray();
* writer.value(1);
* integersAdapter.toJson(writer, Arrays.asList(2, 3, 4));
* writer.value(5);
* writer.endArray();
* }
* }</pre>
*
* <p>With flattening we can create JSON with a single array {@code [1,2,3,4,5]}:
*
* <pre>{@code
* JsonAdapter<List<Integer>> integersAdapter = ...
*
* JsonAdapter<List<Integer>> integersAdapter = ...
*
* public void writeNumbers(JsonWriter writer) {
* writer.beginArray();
* int token = writer.beginFlatten();
* writer.value(1);
* integersAdapter.toJson(writer, Arrays.asList(2, 3, 4));
* writer.value(5);
* writer.endFlatten(token);
* writer.endArray();
* }
* public void writeNumbers(JsonWriter writer) {
* writer.beginArray();
* int token = writer.beginFlatten();
* writer.value(1);
* integersAdapter.toJson(writer, Arrays.asList(2, 3, 4));
* writer.value(5);
* writer.endFlatten(token);
* writer.endArray();
* }
* }</pre>
*
* <p>This method flattens arrays within arrays:
*
* <pre>{@code
*
* Emit: [1, [2, 3, 4], 5]
* To produce: [1, 2, 3, 4, 5]
* Emit: [1, [2, 3, 4], 5]
* To produce: [1, 2, 3, 4, 5]
* }</pre>
*
* It also flattens objects within objects. Do not call {@link #name} before writing a flattened
* object.
*
* <pre>{@code
*
* Emit: {"a": 1, {"b": 2}, "c": 3}
* To Produce: {"a": 1, "b": 2, "c": 3}
* Emit: {"a": 1, {"b": 2}, "c": 3}
* To Produce: {"a": 1, "b": 2, "c": 3}
* }</pre>
*
* Other combinations are permitted but do not perform flattening. For example, objects inside of
* arrays are not flattened:
*
* <pre>{@code
*
* Emit: [1, {"b": 2}, 3, [4, 5], 6]
* To Produce: [1, {"b": 2}, 3, 4, 5, 6]
* Emit: [1, {"b": 2}, 3, [4, 5], 6]
* To Produce: [1, {"b": 2}, 3, 4, 5, 6]
* }</pre>
*
* <p>This method returns an opaque token. Callers must match all calls to this method with a call
* to {@link #endFlatten} with the matching token.
*/
@CheckReturnValue public final int beginFlatten() {
@CheckReturnValue
public final int beginFlatten() {
int context = peekScope();
if (context != NONEMPTY_OBJECT && context != EMPTY_OBJECT
&& context != NONEMPTY_ARRAY && context != EMPTY_ARRAY) {
if (context != NONEMPTY_OBJECT
&& context != EMPTY_OBJECT
&& context != NONEMPTY_ARRAY
&& context != EMPTY_ARRAY) {
throw new IllegalStateException("Nesting problem.");
}
int token = flattenStackSize;
@@ -547,10 +554,11 @@ public abstract class JsonWriter implements Closeable, Flushable {
}
/**
* Returns a <a href="http://goessner.net/articles/JsonPath/">JsonPath</a> to
* the current location in the JSON value.
* Returns a <a href="http://goessner.net/articles/JsonPath/">JsonPath</a> to the current location
* in the JSON value.
*/
@CheckReturnValue public final String getPath() {
@CheckReturnValue
public final String getPath() {
return JsonScope.getPath(stackSize, scopes, pathNames, pathIndices);
}
}

View File

@@ -29,20 +29,20 @@ import java.util.NoSuchElementException;
import java.util.Set;
/**
* A map of comparable keys to values. Unlike {@code TreeMap}, this class uses
* insertion order for iteration order. Comparison order is only used as an
* optimization for efficient insertion and removal.
* A map of comparable keys to values. Unlike {@code TreeMap}, this class uses insertion order for
* iteration order. Comparison order is only used as an optimization for efficient insertion and
* removal.
*
* <p>This implementation was derived from Android 4.1's TreeMap and
* LinkedHashMap classes.
* <p>This implementation was derived from Android 4.1's TreeMap and LinkedHashMap classes.
*/
final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Serializable {
@SuppressWarnings({ "unchecked", "rawtypes" }) // to avoid Comparable<Comparable<Comparable<...>>>
private static final Comparator<Comparable> NATURAL_ORDER = new Comparator<Comparable>() {
public int compare(Comparable a, Comparable b) {
return a.compareTo(b);
}
};
@SuppressWarnings({"unchecked", "rawtypes"}) // to avoid Comparable<Comparable<Comparable<...>>>
private static final Comparator<Comparable> NATURAL_ORDER =
new Comparator<Comparable>() {
public int compare(Comparable a, Comparable b) {
return a.compareTo(b);
}
};
Comparator<? super K> comparator;
Node<K, V>[] table;
@@ -51,47 +51,47 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
int modCount = 0;
int threshold;
/**
* Create a natural order, empty tree map whose keys must be mutually
* comparable and non-null.
*/
/** Create a natural order, empty tree map whose keys must be mutually comparable and non-null. */
LinkedHashTreeMap() {
this(null);
}
/**
* Create a tree map ordered by {@code comparator}. This map's keys may only
* be null if {@code comparator} permits.
* Create a tree map ordered by {@code comparator}. This map's keys may only be null if {@code
* comparator} permits.
*
* @param comparator the comparator to order elements with, or {@code null} to
* use the natural ordering.
* @param comparator the comparator to order elements with, or {@code null} to use the natural
* ordering.
*/
@SuppressWarnings({
"unchecked", "rawtypes" // Unsafe! if comparator is null, this assumes K is comparable.
"unchecked",
"rawtypes" // Unsafe! if comparator is null, this assumes K is comparable.
})
LinkedHashTreeMap(Comparator<? super K> comparator) {
this.comparator = comparator != null
? comparator
: (Comparator) NATURAL_ORDER;
this.comparator = comparator != null ? comparator : (Comparator) NATURAL_ORDER;
this.header = new Node<>();
this.table = new Node[16]; // TODO: sizing/resizing policies
this.threshold = (table.length / 2) + (table.length / 4); // 3/4 capacity
}
@Override public int size() {
@Override
public int size() {
return size;
}
@Override public V get(Object key) {
@Override
public V get(Object key) {
Node<K, V> node = findByObject(key);
return node != null ? node.value : null;
}
@Override public boolean containsKey(Object key) {
@Override
public boolean containsKey(Object key) {
return findByObject(key) != null;
}
@Override public V put(K key, V value) {
@Override
public V put(K key, V value) {
if (key == null) {
throw new NullPointerException("key == null");
}
@@ -101,7 +101,8 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
return result;
}
@Override public void clear() {
@Override
public void clear() {
Arrays.fill(table, null);
size = 0;
modCount++;
@@ -117,7 +118,8 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
header.next = header.prev = header;
}
@Override public V remove(Object key) {
@Override
public V remove(Object key) {
Node<K, V> node = removeInternalByKey(key);
return node != null ? node.value : null;
}
@@ -125,8 +127,7 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
/**
* Returns the node at or adjacent to the given key, creating it if requested.
*
* @throws ClassCastException if {@code key} and the tree's keys aren't
* mutually comparable.
* @throws ClassCastException if {@code key} and the tree's keys aren't mutually comparable.
*/
Node<K, V> find(K key, boolean create) {
Comparator<? super K> comparator = this.comparator;
@@ -139,14 +140,14 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
if (nearest != null) {
// Micro-optimization: avoid polymorphic calls to Comparator.compare().
@SuppressWarnings("unchecked") // Throws a ClassCastException below if there's trouble.
Comparable<Object> comparableKey = (comparator == NATURAL_ORDER)
? (Comparable<Object>) key
: null;
Comparable<Object> comparableKey =
(comparator == NATURAL_ORDER) ? (Comparable<Object>) key : null;
while (true) {
comparison = (comparableKey != null)
? comparableKey.compareTo(nearest.key)
: comparator.compare(key, nearest.key);
comparison =
(comparableKey != null)
? comparableKey.compareTo(nearest.key)
: comparator.compare(key, nearest.key);
// We found the requested key.
if (comparison == 0) {
@@ -206,13 +207,12 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
}
/**
* Returns this map's entry that has the same key and value as {@code
* entry}, or null if this map has no such entry.
* Returns this map's entry that has the same key and value as {@code entry}, or null if this map
* has no such entry.
*
* <p>This method uses the comparator for key equality rather than {@code
* equals}. If this map's comparator isn't consistent with equals (such as
* {@code String.CASE_INSENSITIVE_ORDER}), then {@code remove()} and {@code
* contains()} will violate the collections API.
* <p>This method uses the comparator for key equality rather than {@code equals}. If this map's
* comparator isn't consistent with equals (such as {@code String.CASE_INSENSITIVE_ORDER}), then
* {@code remove()} and {@code contains()} will violate the collections API.
*/
Node<K, V> findByEntry(Entry<?, ?> entry) {
Node<K, V> mine = findByObject(entry.getKey());
@@ -225,10 +225,9 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
}
/**
* Applies a supplemental hash function to a given hashCode, which defends
* against poor quality hash functions. This is critical because HashMap
* uses power-of-two length hash tables, that otherwise encounter collisions
* for hashCodes that do not differ in lower or upper bits.
* Applies a supplemental hash function to a given hashCode, which defends against poor quality
* hash functions. This is critical because HashMap uses power-of-two length hash tables, that
* otherwise encounter collisions for hashCodes that do not differ in lower or upper bits.
*/
private static int secondaryHash(int h) {
// Doug Lea's supplemental hash function
@@ -237,8 +236,7 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
}
/**
* Removes {@code node} from this tree, rearranging the tree's structure as
* necessary.
* Removes {@code node} from this tree, rearranging the tree's structure as necessary.
*
* @param unlink true to also unlink this node from the iteration linked list.
*/
@@ -329,11 +327,10 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
}
/**
* Rebalances the tree by making any AVL rotations necessary between the
* newly-unbalanced node and the tree's root.
* Rebalances the tree by making any AVL rotations necessary between the newly-unbalanced node and
* the tree's root.
*
* @param insert true if the node was unbalanced by an insert; false if it
* was by a removal.
* @param insert true if the node was unbalanced by an insert; false if it was by a removal.
*/
private void rebalance(Node<K, V> unbalanced, boolean insert) {
for (Node<K, V> node = unbalanced; node != null; node = node.parent) {
@@ -395,9 +392,7 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
}
}
/**
* Rotates the subtree so that its root's right child is the new root.
*/
/** Rotates the subtree so that its root's right child is the new root. */
private void rotateLeft(Node<K, V> root) {
Node<K, V> left = root.left;
Node<K, V> pivot = root.right;
@@ -417,15 +412,12 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
root.parent = pivot;
// fix heights
root.height = Math.max(left != null ? left.height : 0,
pivotLeft != null ? pivotLeft.height : 0) + 1;
pivot.height = Math.max(root.height,
pivotRight != null ? pivotRight.height : 0) + 1;
root.height =
Math.max(left != null ? left.height : 0, pivotLeft != null ? pivotLeft.height : 0) + 1;
pivot.height = Math.max(root.height, pivotRight != null ? pivotRight.height : 0) + 1;
}
/**
* Rotates the subtree so that its root's left child is the new root.
*/
/** Rotates the subtree so that its root's left child is the new root. */
private void rotateRight(Node<K, V> root) {
Node<K, V> pivot = root.left;
Node<K, V> right = root.right;
@@ -445,21 +437,22 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
root.parent = pivot;
// fixup heights
root.height = Math.max(right != null ? right.height : 0,
pivotRight != null ? pivotRight.height : 0) + 1;
pivot.height = Math.max(root.height,
pivotLeft != null ? pivotLeft.height : 0) + 1;
root.height =
Math.max(right != null ? right.height : 0, pivotRight != null ? pivotRight.height : 0) + 1;
pivot.height = Math.max(root.height, pivotLeft != null ? pivotLeft.height : 0) + 1;
}
private EntrySet entrySet;
private KeySet keySet;
@Override public Set<Entry<K, V>> entrySet() {
@Override
public Set<Entry<K, V>> entrySet() {
EntrySet result = entrySet;
return result != null ? result : (entrySet = new EntrySet());
}
@Override public Set<K> keySet() {
@Override
public Set<K> keySet() {
KeySet result = keySet;
return result != null ? result : (keySet = new KeySet());
}
@@ -509,7 +502,8 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
}
@SuppressWarnings("rawtypes")
@Override public boolean equals(Object o) {
@Override
public boolean equals(Object o) {
if (o instanceof Entry) {
Entry other = (Entry) o;
return (key == null ? other.getKey() == null : key.equals(other.getKey()))
@@ -518,18 +512,17 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
return false;
}
@Override public int hashCode() {
return (key == null ? 0 : key.hashCode())
^ (value == null ? 0 : value.hashCode());
@Override
public int hashCode() {
return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
}
@Override public String toString() {
@Override
public String toString() {
return key + "=" + value;
}
/**
* Returns the first node in this subtree.
*/
/** Returns the first node in this subtree. */
public Node<K, V> first() {
Node<K, V> node = this;
Node<K, V> child = node.left;
@@ -540,9 +533,7 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
return node;
}
/**
* Returns the last node in this subtree.
*/
/** Returns the last node in this subtree. */
public Node<K, V> last() {
Node<K, V> node = this;
Node<K, V> child = node.right;
@@ -560,14 +551,14 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
}
/**
* Returns a new array containing the same nodes as {@code oldTable}, but with
* twice as many trees, each of (approximately) half the previous size.
* Returns a new array containing the same nodes as {@code oldTable}, but with twice as many
* trees, each of (approximately) half the previous size.
*/
static <K, V> Node<K, V>[] doubleCapacity(Node<K, V>[] oldTable) {
// TODO: don't do anything if we're already at MAX_CAPACITY
int oldCapacity = oldTable.length;
@SuppressWarnings("unchecked") // Arrays and generics don't get along.
Node<K, V>[] newTable = new Node[oldCapacity * 2];
Node<K, V>[] newTable = new Node[oldCapacity * 2];
AvlIterator<K, V> iterator = new AvlIterator<>();
AvlBuilder<K, V> leftBuilder = new AvlBuilder<>();
AvlBuilder<K, V> rightBuilder = new AvlBuilder<>();
@@ -611,13 +602,12 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
}
/**
* Walks an AVL tree in iteration order. Once a node has been returned, its
* left, right and parent links are <strong>no longer used</strong>. For this
* reason it is safe to transform these links as you walk a tree.
* Walks an AVL tree in iteration order. Once a node has been returned, its left, right and parent
* links are <strong>no longer used</strong>. For this reason it is safe to transform these links
* as you walk a tree.
*
* <p><strong>Warning:</strong> this iterator is destructive. It clears the
* parent node of all nodes in the tree. It is an error to make a partial
* iteration of a tree.
* <p><strong>Warning:</strong> this iterator is destructive. It clears the parent node of all
* nodes in the tree. It is an error to make a partial iteration of a tree.
*/
static class AvlIterator<K, V> {
/** This stack is a singly linked list, linked by the 'parent' field. */
@@ -650,26 +640,25 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
}
/**
* Builds AVL trees of a predetermined size by accepting nodes of increasing
* value. To use:
* Builds AVL trees of a predetermined size by accepting nodes of increasing value. To use:
*
* <ol>
* <li>Call {@link #reset} to initialize the target size <i>size</i>.
* <li>Call {@link #add} <i>size</i> times with increasing values.
* <li>Call {@link #root} to get the root of the balanced tree.
* </ol>
*
* <p>The returned tree will satisfy the AVL constraint: for every node
* <i>N</i>, the height of <i>N.left</i> and <i>N.right</i> is different by at
* most 1. It accomplishes this by omitting deepest-level leaf nodes when
* building trees whose size isn't a power of 2 minus 1.
* <p>The returned tree will satisfy the AVL constraint: for every node <i>N</i>, the height of
* <i>N.left</i> and <i>N.right</i> is different by at most 1. It accomplishes this by omitting
* deepest-level leaf nodes when building trees whose size isn't a power of 2 minus 1.
*
* <p>Unlike rebuilding a tree from scratch, this approach requires no value
* comparisons. Using this class to create a tree of size <i>S</i> is
* {@code O(S)}.
* <p>Unlike rebuilding a tree from scratch, this approach requires no value comparisons. Using
* this class to create a tree of size <i>S</i> is {@code O(S)}.
*/
static final class AvlBuilder<K, V> {
/** This stack is a singly linked list, linked by the 'parent' field. */
private Node<K, V> stack;
private int leavesToSkip;
private int leavesSkipped;
private int size;
@@ -789,11 +778,13 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
}
final class EntrySet extends AbstractSet<Entry<K, V>> {
@Override public int size() {
@Override
public int size() {
return size;
}
@Override public Iterator<Entry<K, V>> iterator() {
@Override
public Iterator<Entry<K, V>> iterator() {
return new LinkedTreeMapIterator<Entry<K, V>>() {
public Entry<K, V> next() {
return nextNode();
@@ -801,11 +792,13 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
};
}
@Override public boolean contains(Object o) {
@Override
public boolean contains(Object o) {
return o instanceof Entry && findByEntry((Entry<?, ?>) o) != null;
}
@Override public boolean remove(Object o) {
@Override
public boolean remove(Object o) {
if (!(o instanceof Entry)) {
return false;
}
@@ -818,17 +811,20 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
return true;
}
@Override public void clear() {
@Override
public void clear() {
LinkedHashTreeMap.this.clear();
}
}
final class KeySet extends AbstractSet<K> {
@Override public int size() {
@Override
public int size() {
return size;
}
@Override public Iterator<K> iterator() {
@Override
public Iterator<K> iterator() {
return new LinkedTreeMapIterator<K>() {
public K next() {
return nextNode().key;
@@ -836,24 +832,26 @@ final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Seriali
};
}
@Override public boolean contains(Object o) {
@Override
public boolean contains(Object o) {
return containsKey(o);
}
@Override public boolean remove(Object key) {
@Override
public boolean remove(Object key) {
return removeInternalByKey(key) != null;
}
@Override public void clear() {
@Override
public void clear() {
LinkedHashTreeMap.this.clear();
}
}
/**
* If somebody is unlucky enough to have to serialize one of these, serialize
* it as a LinkedHashMap so that they won't need Gson on the other side to
* deserialize it. Using serialization defeats our DoS defence, so most apps
* shouldn't use it.
* If somebody is unlucky enough to have to serialize one of these, serialize it as a
* LinkedHashMap so that they won't need Gson on the other side to deserialize it. Using
* serialization defeats our DoS defence, so most apps shouldn't use it.
*/
private Object writeReplace() throws ObjectStreamException {
return new LinkedHashMap<>(this);

View File

@@ -25,19 +25,21 @@ import javax.annotation.Nullable;
/**
* Converts maps with string keys to JSON objects.
*
* TODO: support maps with other key types and convert to/from strings.
* <p>TODO: support maps with other key types and convert to/from strings.
*/
final class MapJsonAdapter<K, V> extends JsonAdapter<Map<K, V>> {
public static final Factory FACTORY = new Factory() {
@Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (!annotations.isEmpty()) return null;
Class<?> rawType = Types.getRawType(type);
if (rawType != Map.class) return null;
Type[] keyAndValue = Types.mapKeyAndValueTypes(type, rawType);
return new MapJsonAdapter<>(moshi, keyAndValue[0], keyAndValue[1]).nullSafe();
}
};
public static final Factory FACTORY =
new Factory() {
@Override
public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (!annotations.isEmpty()) return null;
Class<?> rawType = Types.getRawType(type);
if (rawType != Map.class) return null;
Type[] keyAndValue = Types.mapKeyAndValueTypes(type, rawType);
return new MapJsonAdapter<>(moshi, keyAndValue[0], keyAndValue[1]).nullSafe();
}
};
private final JsonAdapter<K> keyAdapter;
private final JsonAdapter<V> valueAdapter;
@@ -47,7 +49,8 @@ final class MapJsonAdapter<K, V> extends JsonAdapter<Map<K, V>> {
this.valueAdapter = moshi.adapter(valueType);
}
@Override public void toJson(JsonWriter writer, Map<K, V> map) throws IOException {
@Override
public void toJson(JsonWriter writer, Map<K, V> map) throws IOException {
writer.beginObject();
for (Map.Entry<K, V> entry : map.entrySet()) {
if (entry.getKey() == null) {
@@ -60,7 +63,8 @@ final class MapJsonAdapter<K, V> extends JsonAdapter<Map<K, V>> {
writer.endObject();
}
@Override public Map<K, V> fromJson(JsonReader reader) throws IOException {
@Override
public Map<K, V> fromJson(JsonReader reader) throws IOException {
LinkedHashTreeMap<K, V> result = new LinkedHashTreeMap<>();
reader.beginObject();
while (reader.hasNext()) {
@@ -69,15 +73,23 @@ final class MapJsonAdapter<K, V> extends JsonAdapter<Map<K, V>> {
V value = valueAdapter.fromJson(reader);
V replaced = result.put(name, value);
if (replaced != null) {
throw new JsonDataException("Map key '" + name + "' has multiple values at path "
+ reader.getPath() + ": " + replaced + " and " + value);
throw new JsonDataException(
"Map key '"
+ name
+ "' has multiple values at path "
+ reader.getPath()
+ ": "
+ replaced
+ " and "
+ value);
}
}
reader.endObject();
return result;
}
@Override public String toString() {
@Override
public String toString() {
return "JsonAdapter(" + keyAdapter + "=" + valueAdapter + ")";
}
}

View File

@@ -15,6 +15,10 @@
*/
package com.squareup.moshi;
import static com.squareup.moshi.internal.Util.canonicalize;
import static com.squareup.moshi.internal.Util.removeSubtypeWildcard;
import static com.squareup.moshi.internal.Util.typeAnnotatedWithAnnotations;
import com.squareup.moshi.internal.Util;
import java.io.IOException;
import java.lang.annotation.Annotation;
@@ -33,10 +37,6 @@ import java.util.Set;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable;
import static com.squareup.moshi.internal.Util.canonicalize;
import static com.squareup.moshi.internal.Util.removeSubtypeWildcard;
import static com.squareup.moshi.internal.Util.typeAnnotatedWithAnnotations;
/**
* Coordinates binding between JSON values and Java objects.
*
@@ -59,19 +59,21 @@ public final class Moshi {
private final Map<Object, JsonAdapter<?>> adapterCache = new LinkedHashMap<>();
Moshi(Builder builder) {
List<JsonAdapter.Factory> factories = new ArrayList<>(
builder.factories.size() + BUILT_IN_FACTORIES.size());
List<JsonAdapter.Factory> factories =
new ArrayList<>(builder.factories.size() + BUILT_IN_FACTORIES.size());
factories.addAll(builder.factories);
factories.addAll(BUILT_IN_FACTORIES);
this.factories = Collections.unmodifiableList(factories);
}
/** Returns a JSON adapter for {@code type}, creating it if necessary. */
@CheckReturnValue public <T> JsonAdapter<T> adapter(Type type) {
@CheckReturnValue
public <T> JsonAdapter<T> adapter(Type type) {
return adapter(type, Util.NO_ANNOTATIONS);
}
@CheckReturnValue public <T> JsonAdapter<T> adapter(Class<T> type) {
@CheckReturnValue
public <T> JsonAdapter<T> adapter(Class<T> type) {
return adapter(type, Util.NO_ANNOTATIONS);
}
@@ -80,8 +82,8 @@ public final class Moshi {
if (annotationType == null) {
throw new NullPointerException("annotationType == null");
}
return adapter(type,
Collections.singleton(Types.createJsonQualifierImplementation(annotationType)));
return adapter(
type, Collections.singleton(Types.createJsonQualifierImplementation(annotationType)));
}
@CheckReturnValue
@@ -103,12 +105,12 @@ public final class Moshi {
/**
* @param fieldName An optional field name associated with this type. The field name is used as a
* hint for better adapter lookup error messages for nested structures.
* hint for better adapter lookup error messages for nested structures.
*/
@CheckReturnValue
@SuppressWarnings("unchecked") // Factories are required to return only matching JsonAdapters.
public <T> JsonAdapter<T> adapter(Type type, Set<? extends Annotation> annotations,
@Nullable String fieldName) {
public <T> JsonAdapter<T> adapter(
Type type, Set<? extends Annotation> annotations, @Nullable String fieldName) {
if (type == null) {
throw new NullPointerException("type == null");
}
@@ -158,8 +160,8 @@ public final class Moshi {
@CheckReturnValue
@SuppressWarnings("unchecked") // Factories are required to return only matching JsonAdapters.
public <T> JsonAdapter<T> nextAdapter(JsonAdapter.Factory skipPast, Type type,
Set<? extends Annotation> annotations) {
public <T> JsonAdapter<T> nextAdapter(
JsonAdapter.Factory skipPast, Type type, Set<? extends Annotation> annotations) {
if (annotations == null) throw new NullPointerException("annotations == null");
type = removeSubtypeWildcard(canonicalize(type));
@@ -172,12 +174,13 @@ public final class Moshi {
JsonAdapter<T> result = (JsonAdapter<T>) factories.get(i).create(type, annotations, this);
if (result != null) return result;
}
throw new IllegalArgumentException("No next JsonAdapter for "
+ typeAnnotatedWithAnnotations(type, annotations));
throw new IllegalArgumentException(
"No next JsonAdapter for " + typeAnnotatedWithAnnotations(type, annotations));
}
/** Returns a new builder containing all custom factories used by the current instance. */
@CheckReturnValue public Moshi.Builder newBuilder() {
@CheckReturnValue
public Moshi.Builder newBuilder() {
int fullSize = factories.size();
int tailSize = BUILT_IN_FACTORIES.size();
List<JsonAdapter.Factory> customFactories = factories.subList(0, fullSize - tailSize);
@@ -197,15 +200,21 @@ public final class Moshi {
if (type == null) throw new IllegalArgumentException("type == null");
if (jsonAdapter == null) throw new IllegalArgumentException("jsonAdapter == null");
return add(new JsonAdapter.Factory() {
@Override public @Nullable JsonAdapter<?> create(
Type targetType, Set<? extends Annotation> annotations, Moshi moshi) {
return annotations.isEmpty() && Util.typesMatch(type, targetType) ? jsonAdapter : null;
}
});
return add(
new JsonAdapter.Factory() {
@Override
public @Nullable JsonAdapter<?> create(
Type targetType, Set<? extends Annotation> annotations, Moshi moshi) {
return annotations.isEmpty() && Util.typesMatch(type, targetType)
? jsonAdapter
: null;
}
});
}
public <T> Builder add(final Type type, final Class<? extends Annotation> annotation,
public <T> Builder add(
final Type type,
final Class<? extends Annotation> annotation,
final JsonAdapter<T> jsonAdapter) {
if (type == null) throw new IllegalArgumentException("type == null");
if (annotation == null) throw new IllegalArgumentException("annotation == null");
@@ -217,17 +226,19 @@ public final class Moshi {
throw new IllegalArgumentException("Use JsonAdapter.Factory for annotations with elements");
}
return add(new JsonAdapter.Factory() {
@Override public @Nullable JsonAdapter<?> create(
Type targetType, Set<? extends Annotation> annotations, Moshi moshi) {
if (Util.typesMatch(type, targetType)
&& annotations.size() == 1
&& Util.isAnnotationPresent(annotations, annotation)) {
return jsonAdapter;
}
return null;
}
});
return add(
new JsonAdapter.Factory() {
@Override
public @Nullable JsonAdapter<?> create(
Type targetType, Set<? extends Annotation> annotations, Moshi moshi) {
if (Util.typesMatch(type, targetType)
&& annotations.size() == 1
&& Util.isAnnotationPresent(annotations, annotation)) {
return jsonAdapter;
}
return null;
}
});
}
public Builder add(JsonAdapter.Factory factory) {
@@ -246,7 +257,8 @@ public final class Moshi {
return this;
}
@CheckReturnValue public Moshi build() {
@CheckReturnValue
public Moshi build() {
return new Moshi(this);
}
}
@@ -260,10 +272,10 @@ public final class Moshi {
* with all of the lookups.
*
* <p>Sometimes a JSON adapter factory depends on its own product; either directly or indirectly.
* To make this work, we offer a JSON adapter stub while the final adapter is being computed.
* When it is ready, we wire the stub to that finished adapter. This is necessary in
* self-referential object models, such as an {@code Employee} class that has a {@code
* List<Employee>} field for an organization's management hierarchy.
* To make this work, we offer a JSON adapter stub while the final adapter is being computed. When
* it is ready, we wire the stub to that finished adapter. This is necessary in self-referential
* object models, such as an {@code Employee} class that has a {@code List<Employee>} field for an
* organization's management hierarchy.
*
* <p>This class defers putting any JSON adapters in the cache until the topmost JSON adapter has
* successfully been computed. That way we don't pollute the cache with incomplete stubs, or
@@ -339,13 +351,9 @@ public final class Moshi {
StringBuilder errorMessageBuilder = new StringBuilder(e.getMessage());
for (Iterator<Lookup<?>> i = stack.descendingIterator(); i.hasNext(); ) {
Lookup<?> lookup = i.next();
errorMessageBuilder
.append("\nfor ")
.append(lookup.type);
errorMessageBuilder.append("\nfor ").append(lookup.type);
if (lookup.fieldName != null) {
errorMessageBuilder
.append(' ')
.append(lookup.fieldName);
errorMessageBuilder.append(' ').append(lookup.fieldName);
}
}
@@ -366,17 +374,20 @@ public final class Moshi {
this.cacheKey = cacheKey;
}
@Override public T fromJson(JsonReader reader) throws IOException {
@Override
public T fromJson(JsonReader reader) throws IOException {
if (adapter == null) throw new IllegalStateException("JsonAdapter isn't ready");
return adapter.fromJson(reader);
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
@Override
public void toJson(JsonWriter writer, T value) throws IOException {
if (adapter == null) throw new IllegalStateException("JsonAdapter isn't ready");
adapter.toJson(writer, value);
}
@Override public String toString() {
@Override
public String toString() {
return adapter != null ? adapter.toString() : super.toString();
}
}

View File

@@ -15,6 +15,8 @@
*/
package com.squareup.moshi;
import static com.squareup.moshi.internal.Util.generatedAdapter;
import com.squareup.moshi.internal.Util;
import java.io.IOException;
import java.lang.annotation.Annotation;
@@ -26,49 +28,48 @@ import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import static com.squareup.moshi.internal.Util.generatedAdapter;
final class StandardJsonAdapters {
private StandardJsonAdapters() {
}
private StandardJsonAdapters() {}
public static final JsonAdapter.Factory FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (!annotations.isEmpty()) return null;
if (type == boolean.class) return BOOLEAN_JSON_ADAPTER;
if (type == byte.class) return BYTE_JSON_ADAPTER;
if (type == char.class) return CHARACTER_JSON_ADAPTER;
if (type == double.class) return DOUBLE_JSON_ADAPTER;
if (type == float.class) return FLOAT_JSON_ADAPTER;
if (type == int.class) return INTEGER_JSON_ADAPTER;
if (type == long.class) return LONG_JSON_ADAPTER;
if (type == short.class) return SHORT_JSON_ADAPTER;
if (type == Boolean.class) return BOOLEAN_JSON_ADAPTER.nullSafe();
if (type == Byte.class) return BYTE_JSON_ADAPTER.nullSafe();
if (type == Character.class) return CHARACTER_JSON_ADAPTER.nullSafe();
if (type == Double.class) return DOUBLE_JSON_ADAPTER.nullSafe();
if (type == Float.class) return FLOAT_JSON_ADAPTER.nullSafe();
if (type == Integer.class) return INTEGER_JSON_ADAPTER.nullSafe();
if (type == Long.class) return LONG_JSON_ADAPTER.nullSafe();
if (type == Short.class) return SHORT_JSON_ADAPTER.nullSafe();
if (type == String.class) return STRING_JSON_ADAPTER.nullSafe();
if (type == Object.class) return new ObjectJsonAdapter(moshi).nullSafe();
public static final JsonAdapter.Factory FACTORY =
new JsonAdapter.Factory() {
@Override
public JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (!annotations.isEmpty()) return null;
if (type == boolean.class) return BOOLEAN_JSON_ADAPTER;
if (type == byte.class) return BYTE_JSON_ADAPTER;
if (type == char.class) return CHARACTER_JSON_ADAPTER;
if (type == double.class) return DOUBLE_JSON_ADAPTER;
if (type == float.class) return FLOAT_JSON_ADAPTER;
if (type == int.class) return INTEGER_JSON_ADAPTER;
if (type == long.class) return LONG_JSON_ADAPTER;
if (type == short.class) return SHORT_JSON_ADAPTER;
if (type == Boolean.class) return BOOLEAN_JSON_ADAPTER.nullSafe();
if (type == Byte.class) return BYTE_JSON_ADAPTER.nullSafe();
if (type == Character.class) return CHARACTER_JSON_ADAPTER.nullSafe();
if (type == Double.class) return DOUBLE_JSON_ADAPTER.nullSafe();
if (type == Float.class) return FLOAT_JSON_ADAPTER.nullSafe();
if (type == Integer.class) return INTEGER_JSON_ADAPTER.nullSafe();
if (type == Long.class) return LONG_JSON_ADAPTER.nullSafe();
if (type == Short.class) return SHORT_JSON_ADAPTER.nullSafe();
if (type == String.class) return STRING_JSON_ADAPTER.nullSafe();
if (type == Object.class) return new ObjectJsonAdapter(moshi).nullSafe();
Class<?> rawType = Types.getRawType(type);
Class<?> rawType = Types.getRawType(type);
@Nullable JsonAdapter<?> generatedAdapter = generatedAdapter(moshi, type, rawType);
if (generatedAdapter != null) {
return generatedAdapter;
}
@Nullable JsonAdapter<?> generatedAdapter = generatedAdapter(moshi, type, rawType);
if (generatedAdapter != null) {
return generatedAdapter;
}
if (rawType.isEnum()) {
//noinspection unchecked
return new EnumJsonAdapter<>((Class<? extends Enum>) rawType).nullSafe();
}
return null;
}
};
if (rawType.isEnum()) {
//noinspection unchecked
return new EnumJsonAdapter<>((Class<? extends Enum>) rawType).nullSafe();
}
return null;
}
};
private static final String ERROR_FORMAT = "Expected %s but was %s at path %s";
@@ -82,147 +83,183 @@ final class StandardJsonAdapters {
return value;
}
static final JsonAdapter<Boolean> BOOLEAN_JSON_ADAPTER = new JsonAdapter<Boolean>() {
@Override public Boolean fromJson(JsonReader reader) throws IOException {
return reader.nextBoolean();
}
static final JsonAdapter<Boolean> BOOLEAN_JSON_ADAPTER =
new JsonAdapter<Boolean>() {
@Override
public Boolean fromJson(JsonReader reader) throws IOException {
return reader.nextBoolean();
}
@Override public void toJson(JsonWriter writer, Boolean value) throws IOException {
writer.value(value.booleanValue());
}
@Override
public void toJson(JsonWriter writer, Boolean value) throws IOException {
writer.value(value.booleanValue());
}
@Override public String toString() {
return "JsonAdapter(Boolean)";
}
};
@Override
public String toString() {
return "JsonAdapter(Boolean)";
}
};
static final JsonAdapter<Byte> BYTE_JSON_ADAPTER = new JsonAdapter<Byte>() {
@Override public Byte fromJson(JsonReader reader) throws IOException {
return (byte) rangeCheckNextInt(reader, "a byte", Byte.MIN_VALUE, 0xff);
}
static final JsonAdapter<Byte> BYTE_JSON_ADAPTER =
new JsonAdapter<Byte>() {
@Override
public Byte fromJson(JsonReader reader) throws IOException {
return (byte) rangeCheckNextInt(reader, "a byte", Byte.MIN_VALUE, 0xff);
}
@Override public void toJson(JsonWriter writer, Byte value) throws IOException {
writer.value(value.intValue() & 0xff);
}
@Override
public void toJson(JsonWriter writer, Byte value) throws IOException {
writer.value(value.intValue() & 0xff);
}
@Override public String toString() {
return "JsonAdapter(Byte)";
}
};
@Override
public String toString() {
return "JsonAdapter(Byte)";
}
};
static final JsonAdapter<Character> CHARACTER_JSON_ADAPTER = new JsonAdapter<Character>() {
@Override public Character fromJson(JsonReader reader) throws IOException {
String value = reader.nextString();
if (value.length() > 1) {
throw new JsonDataException(
String.format(ERROR_FORMAT, "a char", '"' + value + '"', reader.getPath()));
}
return value.charAt(0);
}
static final JsonAdapter<Character> CHARACTER_JSON_ADAPTER =
new JsonAdapter<Character>() {
@Override
public Character fromJson(JsonReader reader) throws IOException {
String value = reader.nextString();
if (value.length() > 1) {
throw new JsonDataException(
String.format(ERROR_FORMAT, "a char", '"' + value + '"', reader.getPath()));
}
return value.charAt(0);
}
@Override public void toJson(JsonWriter writer, Character value) throws IOException {
writer.value(value.toString());
}
@Override
public void toJson(JsonWriter writer, Character value) throws IOException {
writer.value(value.toString());
}
@Override public String toString() {
return "JsonAdapter(Character)";
}
};
@Override
public String toString() {
return "JsonAdapter(Character)";
}
};
static final JsonAdapter<Double> DOUBLE_JSON_ADAPTER = new JsonAdapter<Double>() {
@Override public Double fromJson(JsonReader reader) throws IOException {
return reader.nextDouble();
}
static final JsonAdapter<Double> DOUBLE_JSON_ADAPTER =
new JsonAdapter<Double>() {
@Override
public Double fromJson(JsonReader reader) throws IOException {
return reader.nextDouble();
}
@Override public void toJson(JsonWriter writer, Double value) throws IOException {
writer.value(value.doubleValue());
}
@Override
public void toJson(JsonWriter writer, Double value) throws IOException {
writer.value(value.doubleValue());
}
@Override public String toString() {
return "JsonAdapter(Double)";
}
};
@Override
public String toString() {
return "JsonAdapter(Double)";
}
};
static final JsonAdapter<Float> FLOAT_JSON_ADAPTER = new JsonAdapter<Float>() {
@Override public Float fromJson(JsonReader reader) throws IOException {
float value = (float) reader.nextDouble();
// Double check for infinity after float conversion; many doubles > Float.MAX
if (!reader.isLenient() && Float.isInfinite(value)) {
throw new JsonDataException("JSON forbids NaN and infinities: " + value
+ " at path " + reader.getPath());
}
return value;
}
static final JsonAdapter<Float> FLOAT_JSON_ADAPTER =
new JsonAdapter<Float>() {
@Override
public Float fromJson(JsonReader reader) throws IOException {
float value = (float) reader.nextDouble();
// Double check for infinity after float conversion; many doubles > Float.MAX
if (!reader.isLenient() && Float.isInfinite(value)) {
throw new JsonDataException(
"JSON forbids NaN and infinities: " + value + " at path " + reader.getPath());
}
return value;
}
@Override public void toJson(JsonWriter writer, Float value) throws IOException {
// Manual null pointer check for the float.class adapter.
if (value == null) {
throw new NullPointerException();
}
// Use the Number overload so we write out float precision instead of double precision.
writer.value(value);
}
@Override
public void toJson(JsonWriter writer, Float value) throws IOException {
// Manual null pointer check for the float.class adapter.
if (value == null) {
throw new NullPointerException();
}
// Use the Number overload so we write out float precision instead of double precision.
writer.value(value);
}
@Override public String toString() {
return "JsonAdapter(Float)";
}
};
@Override
public String toString() {
return "JsonAdapter(Float)";
}
};
static final JsonAdapter<Integer> INTEGER_JSON_ADAPTER = new JsonAdapter<Integer>() {
@Override public Integer fromJson(JsonReader reader) throws IOException {
return reader.nextInt();
}
static final JsonAdapter<Integer> INTEGER_JSON_ADAPTER =
new JsonAdapter<Integer>() {
@Override
public Integer fromJson(JsonReader reader) throws IOException {
return reader.nextInt();
}
@Override public void toJson(JsonWriter writer, Integer value) throws IOException {
writer.value(value.intValue());
}
@Override
public void toJson(JsonWriter writer, Integer value) throws IOException {
writer.value(value.intValue());
}
@Override public String toString() {
return "JsonAdapter(Integer)";
}
};
@Override
public String toString() {
return "JsonAdapter(Integer)";
}
};
static final JsonAdapter<Long> LONG_JSON_ADAPTER = new JsonAdapter<Long>() {
@Override public Long fromJson(JsonReader reader) throws IOException {
return reader.nextLong();
}
static final JsonAdapter<Long> LONG_JSON_ADAPTER =
new JsonAdapter<Long>() {
@Override
public Long fromJson(JsonReader reader) throws IOException {
return reader.nextLong();
}
@Override public void toJson(JsonWriter writer, Long value) throws IOException {
writer.value(value.longValue());
}
@Override
public void toJson(JsonWriter writer, Long value) throws IOException {
writer.value(value.longValue());
}
@Override public String toString() {
return "JsonAdapter(Long)";
}
};
@Override
public String toString() {
return "JsonAdapter(Long)";
}
};
static final JsonAdapter<Short> SHORT_JSON_ADAPTER = new JsonAdapter<Short>() {
@Override public Short fromJson(JsonReader reader) throws IOException {
return (short) rangeCheckNextInt(reader, "a short", Short.MIN_VALUE, Short.MAX_VALUE);
}
static final JsonAdapter<Short> SHORT_JSON_ADAPTER =
new JsonAdapter<Short>() {
@Override
public Short fromJson(JsonReader reader) throws IOException {
return (short) rangeCheckNextInt(reader, "a short", Short.MIN_VALUE, Short.MAX_VALUE);
}
@Override public void toJson(JsonWriter writer, Short value) throws IOException {
writer.value(value.intValue());
}
@Override
public void toJson(JsonWriter writer, Short value) throws IOException {
writer.value(value.intValue());
}
@Override public String toString() {
return "JsonAdapter(Short)";
}
};
@Override
public String toString() {
return "JsonAdapter(Short)";
}
};
static final JsonAdapter<String> STRING_JSON_ADAPTER = new JsonAdapter<String>() {
@Override public String fromJson(JsonReader reader) throws IOException {
return reader.nextString();
}
static final JsonAdapter<String> STRING_JSON_ADAPTER =
new JsonAdapter<String>() {
@Override
public String fromJson(JsonReader reader) throws IOException {
return reader.nextString();
}
@Override public void toJson(JsonWriter writer, String value) throws IOException {
writer.value(value);
}
@Override
public void toJson(JsonWriter writer, String value) throws IOException {
writer.value(value);
}
@Override public String toString() {
return "JsonAdapter(String)";
}
};
@Override
public String toString() {
return "JsonAdapter(String)";
}
};
static final class EnumJsonAdapter<T extends Enum<T>> extends JsonAdapter<T> {
private final Class<T> enumType;
@@ -247,22 +284,30 @@ final class StandardJsonAdapters {
}
}
@Override public T fromJson(JsonReader reader) throws IOException {
@Override
public T fromJson(JsonReader reader) throws IOException {
int index = reader.selectString(options);
if (index != -1) return constants[index];
// We can consume the string safely, we are terminating anyway.
String path = reader.getPath();
String name = reader.nextString();
throw new JsonDataException("Expected one of "
+ Arrays.asList(nameStrings) + " but was " + name + " at path " + path);
throw new JsonDataException(
"Expected one of "
+ Arrays.asList(nameStrings)
+ " but was "
+ name
+ " at path "
+ path);
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
@Override
public void toJson(JsonWriter writer, T value) throws IOException {
writer.value(nameStrings[value.ordinal()]);
}
@Override public String toString() {
@Override
public String toString() {
return "JsonAdapter(" + enumType.getName() + ")";
}
}
@@ -292,7 +337,8 @@ final class StandardJsonAdapters {
this.booleanAdapter = moshi.adapter(Boolean.class);
}
@Override public Object fromJson(JsonReader reader) throws IOException {
@Override
public Object fromJson(JsonReader reader) throws IOException {
switch (reader.peek()) {
case BEGIN_ARRAY:
return listJsonAdapter.fromJson(reader);
@@ -318,7 +364,8 @@ final class StandardJsonAdapters {
}
}
@Override public void toJson(JsonWriter writer, Object value) throws IOException {
@Override
public void toJson(JsonWriter writer, Object value) throws IOException {
Class<?> valueClass = value.getClass();
if (valueClass == Object.class) {
// Don't recurse infinitely when the runtime type is also Object.class.
@@ -342,7 +389,8 @@ final class StandardJsonAdapters {
return valueClass;
}
@Override public String toString() {
@Override
public String toString() {
return "JsonAdapter(Object)";
}
}

View File

@@ -22,5 +22,4 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ToJson {
}
public @interface ToJson {}

View File

@@ -15,6 +15,10 @@
*/
package com.squareup.moshi;
import static com.squareup.moshi.internal.Util.EMPTY_TYPE_ARRAY;
import static com.squareup.moshi.internal.Util.getGenericSupertype;
import static com.squareup.moshi.internal.Util.resolve;
import com.squareup.moshi.internal.Util.GenericArrayTypeImpl;
import com.squareup.moshi.internal.Util.ParameterizedTypeImpl;
import com.squareup.moshi.internal.Util.WildcardTypeImpl;
@@ -39,25 +43,20 @@ import java.util.Set;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable;
import static com.squareup.moshi.internal.Util.EMPTY_TYPE_ARRAY;
import static com.squareup.moshi.internal.Util.getGenericSupertype;
import static com.squareup.moshi.internal.Util.resolve;
/** Factory methods for types. */
@CheckReturnValue
public final class Types {
private Types() {
}
private Types() {}
/**
* Resolves the generated {@link JsonAdapter} fully qualified class name for a given
* {@link JsonClass JsonClass-annotated} {@code clazz}. This is the same lookup logic used by
* both the Moshi code generation as well as lookup for any JsonClass-annotated classes. This can
* be useful if generating your own JsonAdapters without using Moshi's first party code gen.
* Resolves the generated {@link JsonAdapter} fully qualified class name for a given {@link
* JsonClass JsonClass-annotated} {@code clazz}. This is the same lookup logic used by both the
* Moshi code generation as well as lookup for any JsonClass-annotated classes. This can be useful
* if generating your own JsonAdapters without using Moshi's first party code gen.
*
* @param clazz the class to calculate a generated JsonAdapter name for.
* @return the resolved fully qualified class name to the expected generated JsonAdapter class.
* Note that this name will always be a top-level class name and not a nested class.
* Note that this name will always be a top-level class name and not a nested class.
*/
public static String generatedJsonAdapterName(Class<?> clazz) {
if (clazz.getAnnotation(JsonClass.class) == null) {
@@ -67,27 +66,26 @@ public final class Types {
}
/**
* Resolves the generated {@link JsonAdapter} fully qualified class name for a given
* {@link JsonClass JsonClass-annotated} {@code className}. This is the same lookup logic used by
* both the Moshi code generation as well as lookup for any JsonClass-annotated classes. This can
* be useful if generating your own JsonAdapters without using Moshi's first party code gen.
* Resolves the generated {@link JsonAdapter} fully qualified class name for a given {@link
* JsonClass JsonClass-annotated} {@code className}. This is the same lookup logic used by both
* the Moshi code generation as well as lookup for any JsonClass-annotated classes. This can be
* useful if generating your own JsonAdapters without using Moshi's first party code gen.
*
* @param className the fully qualified class to calculate a generated JsonAdapter name for.
* @return the resolved fully qualified class name to the expected generated JsonAdapter class.
* Note that this name will always be a top-level class name and not a nested class.
* Note that this name will always be a top-level class name and not a nested class.
*/
public static String generatedJsonAdapterName(String className) {
return className.replace("$", "_") + "JsonAdapter";
}
/**
* Checks if {@code annotations} contains {@code jsonQualifier}.
* Returns the subset of {@code annotations} without {@code jsonQualifier}, or null if {@code
* annotations} does not contain {@code jsonQualifier}.
* Checks if {@code annotations} contains {@code jsonQualifier}. Returns the subset of {@code
* annotations} without {@code jsonQualifier}, or null if {@code annotations} does not contain
* {@code jsonQualifier}.
*/
public static @Nullable Set<? extends Annotation> nextAnnotations(
Set<? extends Annotation> annotations,
Class<? extends Annotation> jsonQualifier) {
Set<? extends Annotation> annotations, Class<? extends Annotation> jsonQualifier) {
if (!jsonQualifier.isAnnotationPresent(JsonQualifier.class)) {
throw new IllegalArgumentException(jsonQualifier + " is not a JsonQualifier.");
}
@@ -135,15 +133,15 @@ public final class Types {
/**
* Returns a type that represents an unknown type that extends {@code bound}. For example, if
* {@code bound} is {@code CharSequence.class}, this returns {@code ? extends CharSequence}. If
* {@code bound} is {@code Object.class}, this returns {@code ?}, which is shorthand for {@code
* ? extends Object}.
* {@code bound} is {@code Object.class}, this returns {@code ?}, which is shorthand for {@code ?
* extends Object}.
*/
public static WildcardType subtypeOf(Type bound) {
Type[] upperBounds;
if (bound instanceof WildcardType) {
upperBounds = ((WildcardType) bound).getUpperBounds();
} else {
upperBounds = new Type[] { bound };
upperBounds = new Type[] {bound};
}
return new WildcardTypeImpl(upperBounds, EMPTY_TYPE_ARRAY);
}
@@ -157,9 +155,9 @@ public final class Types {
if (bound instanceof WildcardType) {
lowerBounds = ((WildcardType) bound).getLowerBounds();
} else {
lowerBounds = new Type[] { bound };
lowerBounds = new Type[] {bound};
}
return new WildcardTypeImpl(new Type[] { Object.class }, lowerBounds);
return new WildcardTypeImpl(new Type[] {Object.class}, lowerBounds);
}
public static Class<?> getRawType(Type type) {
@@ -189,13 +187,18 @@ public final class Types {
} else {
String className = type == null ? "null" : type.getClass().getName();
throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
+ "GenericArrayType, but <" + type + "> is of type " + className);
throw new IllegalArgumentException(
"Expected a Class, ParameterizedType, or "
+ "GenericArrayType, but <"
+ type
+ "> is of type "
+ className);
}
}
/**
* Returns the element type of this collection type.
*
* @throws IllegalArgumentException if this type is not a collection.
*/
public static Type collectionElementType(Type context, Class<?> contextRawType) {
@@ -217,8 +220,8 @@ public final class Types {
} else if (a instanceof Class) {
if (b instanceof GenericArrayType) {
return equals(((Class) a).getComponentType(),
((GenericArrayType) b).getGenericComponentType());
return equals(
((Class) a).getComponentType(), ((GenericArrayType) b).getGenericComponentType());
}
return a.equals(b); // Class already specifies equals().
@@ -226,20 +229,22 @@ public final class Types {
if (!(b instanceof ParameterizedType)) return false;
ParameterizedType pa = (ParameterizedType) a;
ParameterizedType pb = (ParameterizedType) b;
Type[] aTypeArguments = pa instanceof ParameterizedTypeImpl
? ((ParameterizedTypeImpl) pa).typeArguments
: pa.getActualTypeArguments();
Type[] bTypeArguments = pb instanceof ParameterizedTypeImpl
? ((ParameterizedTypeImpl) pb).typeArguments
: pb.getActualTypeArguments();
Type[] aTypeArguments =
pa instanceof ParameterizedTypeImpl
? ((ParameterizedTypeImpl) pa).typeArguments
: pa.getActualTypeArguments();
Type[] bTypeArguments =
pb instanceof ParameterizedTypeImpl
? ((ParameterizedTypeImpl) pb).typeArguments
: pb.getActualTypeArguments();
return equals(pa.getOwnerType(), pb.getOwnerType())
&& pa.getRawType().equals(pb.getRawType())
&& Arrays.equals(aTypeArguments, bTypeArguments);
} else if (a instanceof GenericArrayType) {
if (b instanceof Class) {
return equals(((Class) b).getComponentType(),
((GenericArrayType) a).getGenericComponentType());
return equals(
((Class) b).getComponentType(), ((GenericArrayType) a).getGenericComponentType());
}
if (!(b instanceof GenericArrayType)) return false;
GenericArrayType ga = (GenericArrayType) a;
@@ -270,10 +275,10 @@ public final class Types {
* @param clazz the target class to read the {@code fieldName} field annotations from.
* @param fieldName the target field name on {@code clazz}.
* @return a set of {@link JsonQualifier}-annotated {@link Annotation} instances retrieved from
* the targeted field. Can be empty if none are found.
* the targeted field. Can be empty if none are found.
*/
public static Set<? extends Annotation> getFieldJsonQualifierAnnotations(Class<?> clazz,
String fieldName) {
public static Set<? extends Annotation> getFieldJsonQualifierAnnotations(
Class<?> clazz, String fieldName) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
@@ -286,11 +291,8 @@ public final class Types {
}
return Collections.unmodifiableSet(annotations);
} catch (NoSuchFieldException e) {
throw new IllegalArgumentException("Could not access field "
+ fieldName
+ " on class "
+ clazz.getCanonicalName(),
e);
throw new IllegalArgumentException(
"Could not access field " + fieldName + " on class " + clazz.getCanonicalName(), e);
}
}
@@ -305,26 +307,29 @@ public final class Types {
if (annotationType.getDeclaredMethods().length != 0) {
throw new IllegalArgumentException(annotationType + " must not declare methods.");
}
return (T) Proxy.newProxyInstance(annotationType.getClassLoader(),
new Class<?>[] { annotationType }, new InvocationHandler() {
@Override public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
switch (methodName) {
case "annotationType":
return annotationType;
case "equals":
Object o = args[0];
return annotationType.isInstance(o);
case "hashCode":
return 0;
case "toString":
return "@" + annotationType.getName() + "()";
default:
return method.invoke(proxy, args);
}
}
});
return (T)
Proxy.newProxyInstance(
annotationType.getClassLoader(),
new Class<?>[] {annotationType},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
switch (methodName) {
case "annotationType":
return annotationType;
case "equals":
Object o = args[0];
return annotationType.isInstance(o);
case "hashCode":
return 0;
case "toString":
return "@" + annotationType.getName() + "()";
default:
return method.invoke(proxy, args);
}
}
});
}
/**
@@ -334,14 +339,14 @@ public final class Types {
static Type[] mapKeyAndValueTypes(Type context, Class<?> contextRawType) {
// Work around a problem with the declaration of java.util.Properties. That class should extend
// Hashtable<String, String>, but it's declared to extend Hashtable<Object, Object>.
if (context == Properties.class) return new Type[] { String.class, String.class };
if (context == Properties.class) return new Type[] {String.class, String.class};
Type mapType = getSupertype(context, contextRawType, Map.class);
if (mapType instanceof ParameterizedType) {
ParameterizedType mapParameterizedType = (ParameterizedType) mapType;
return mapParameterizedType.getActualTypeArguments();
}
return new Type[] { Object.class, Object.class };
return new Type[] {Object.class, Object.class};
}
/**
@@ -353,8 +358,8 @@ public final class Types {
*/
static Type getSupertype(Type context, Class<?> contextRawType, Class<?> supertype) {
if (!supertype.isAssignableFrom(contextRawType)) throw new IllegalArgumentException();
return resolve(context, contextRawType,
getGenericSupertype(context, contextRawType, supertype));
return resolve(
context, contextRawType, getGenericSupertype(context, contextRawType, supertype));
}
static Type getGenericSuperclass(Type type) {
@@ -363,8 +368,8 @@ public final class Types {
}
/**
* Returns the element type of {@code type} if it is an array type, or null if it is not an
* array type.
* Returns the element type of {@code type} if it is an array type, or null if it is not an array
* type.
*/
static Type arrayComponentType(Type type) {
if (type instanceof GenericArrayType) {

View File

@@ -33,7 +33,8 @@ public final class NullSafeJsonAdapter<T> extends JsonAdapter<T> {
return delegate;
}
@Override public @Nullable T fromJson(JsonReader reader) throws IOException {
@Override
public @Nullable T fromJson(JsonReader reader) throws IOException {
if (reader.peek() == JsonReader.Token.NULL) {
return reader.nextNull();
} else {
@@ -41,7 +42,8 @@ public final class NullSafeJsonAdapter<T> extends JsonAdapter<T> {
}
}
@Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
@Override
public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
if (value == null) {
writer.nullValue();
} else {
@@ -49,7 +51,8 @@ public final class NullSafeJsonAdapter<T> extends JsonAdapter<T> {
}
}
@Override public String toString() {
@Override
public String toString() {
return delegate + ".nullSafe()";
}
}

View File

@@ -15,6 +15,10 @@
*/
package com.squareup.moshi.internal;
import static com.squareup.moshi.Types.arrayOf;
import static com.squareup.moshi.Types.subtypeOf;
import static com.squareup.moshi.Types.supertypeOf;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonClass;
import com.squareup.moshi.JsonDataException;
@@ -40,10 +44,6 @@ import java.util.NoSuchElementException;
import java.util.Set;
import javax.annotation.Nullable;
import static com.squareup.moshi.Types.arrayOf;
import static com.squareup.moshi.Types.subtypeOf;
import static com.squareup.moshi.Types.supertypeOf;
public final class Util {
public static final Set<Annotation> NO_ANNOTATIONS = Collections.emptySet();
public static final Type[] EMPTY_TYPE_ARRAY = new Type[] {};
@@ -74,8 +74,7 @@ public final class Util {
return "kotlin.Metadata";
}
private Util() {
}
private Util() {}
public static boolean typesMatch(Type pattern, Type candidate) {
// TODO: permit raw types (like Set.class) to match non-raw candidates (like Set<Long>).
@@ -151,8 +150,8 @@ public final class Util {
} else if (type instanceof ParameterizedType) {
if (type instanceof ParameterizedTypeImpl) return type;
ParameterizedType p = (ParameterizedType) type;
return new ParameterizedTypeImpl(p.getOwnerType(),
p.getRawType(), p.getActualTypeArguments());
return new ParameterizedTypeImpl(
p.getOwnerType(), p.getRawType(), p.getActualTypeArguments());
} else if (type instanceof GenericArrayType) {
if (type instanceof GenericArrayTypeImpl) return type;
@@ -169,9 +168,7 @@ public final class Util {
}
}
/**
* If type is a "? extends X" wildcard, returns X; otherwise returns type unchanged.
*/
/** If type is a "? extends X" wildcard, returns X; otherwise returns type unchanged. */
public static Type removeSubtypeWildcard(Type type) {
if (!(type instanceof WildcardType)) return type;
@@ -188,7 +185,10 @@ public final class Util {
return resolve(context, contextRawType, toResolve, new LinkedHashSet<TypeVariable>());
}
private static Type resolve(Type context, Class<?> contextRawType, Type toResolve,
private static Type resolve(
Type context,
Class<?> contextRawType,
Type toResolve,
Collection<TypeVariable> visitedTypeVariables) {
// This implementation is made a little more complicated in an attempt to avoid object-creation.
while (true) {
@@ -206,20 +206,16 @@ public final class Util {
} else if (toResolve instanceof Class && ((Class<?>) toResolve).isArray()) {
Class<?> original = (Class<?>) toResolve;
Type componentType = original.getComponentType();
Type newComponentType = resolve(context, contextRawType, componentType,
visitedTypeVariables);
return componentType == newComponentType
? original
: arrayOf(newComponentType);
Type newComponentType =
resolve(context, contextRawType, componentType, visitedTypeVariables);
return componentType == newComponentType ? original : arrayOf(newComponentType);
} else if (toResolve instanceof GenericArrayType) {
GenericArrayType original = (GenericArrayType) toResolve;
Type componentType = original.getGenericComponentType();
Type newComponentType = resolve(context, contextRawType, componentType,
visitedTypeVariables);
return componentType == newComponentType
? original
: arrayOf(newComponentType);
Type newComponentType =
resolve(context, contextRawType, componentType, visitedTypeVariables);
return componentType == newComponentType ? original : arrayOf(newComponentType);
} else if (toResolve instanceof ParameterizedType) {
ParameterizedType original = (ParameterizedType) toResolve;
@@ -229,8 +225,8 @@ public final class Util {
Type[] args = original.getActualTypeArguments();
for (int t = 0, length = args.length; t < length; t++) {
Type resolvedTypeArgument = resolve(context, contextRawType, args[t],
visitedTypeVariables);
Type resolvedTypeArgument =
resolve(context, contextRawType, args[t], visitedTypeVariables);
if (resolvedTypeArgument != args[t]) {
if (!changed) {
args = args.clone();
@@ -250,14 +246,14 @@ public final class Util {
Type[] originalUpperBound = original.getUpperBounds();
if (originalLowerBound.length == 1) {
Type lowerBound = resolve(context, contextRawType, originalLowerBound[0],
visitedTypeVariables);
Type lowerBound =
resolve(context, contextRawType, originalLowerBound[0], visitedTypeVariables);
if (lowerBound != originalLowerBound[0]) {
return supertypeOf(lowerBound);
}
} else if (originalUpperBound.length == 1) {
Type upperBound = resolve(context, contextRawType, originalUpperBound[0],
visitedTypeVariables);
Type upperBound =
resolve(context, contextRawType, originalUpperBound[0], visitedTypeVariables);
if (upperBound != originalUpperBound[0]) {
return subtypeOf(upperBound);
}
@@ -369,8 +365,7 @@ public final class Util {
"unexpected owner type for " + rawType + ": " + ownerType);
}
} else if (enclosingClass != null) {
throw new IllegalArgumentException(
"unexpected owner type for " + rawType + ": null");
throw new IllegalArgumentException("unexpected owner type for " + rawType + ": null");
}
}
@@ -384,30 +379,33 @@ public final class Util {
}
}
@Override public Type[] getActualTypeArguments() {
@Override
public Type[] getActualTypeArguments() {
return typeArguments.clone();
}
@Override public Type getRawType() {
@Override
public Type getRawType() {
return rawType;
}
@Override public @Nullable Type getOwnerType() {
@Override
public @Nullable Type getOwnerType() {
return ownerType;
}
@Override public boolean equals(Object other) {
return other instanceof ParameterizedType
&& Types.equals(this, (ParameterizedType) other);
@Override
public boolean equals(Object other) {
return other instanceof ParameterizedType && Types.equals(this, (ParameterizedType) other);
}
@Override public int hashCode() {
return Arrays.hashCode(typeArguments)
^ rawType.hashCode()
^ hashCodeOrZero(ownerType);
@Override
public int hashCode() {
return Arrays.hashCode(typeArguments) ^ rawType.hashCode() ^ hashCodeOrZero(ownerType);
}
@Override public String toString() {
@Override
public String toString() {
StringBuilder result = new StringBuilder(30 * (typeArguments.length + 1));
result.append(typeToString(rawType));
@@ -430,20 +428,23 @@ public final class Util {
this.componentType = canonicalize(componentType);
}
@Override public Type getGenericComponentType() {
@Override
public Type getGenericComponentType() {
return componentType;
}
@Override public boolean equals(Object o) {
return o instanceof GenericArrayType
&& Types.equals(this, (GenericArrayType) o);
@Override
public boolean equals(Object o) {
return o instanceof GenericArrayType && Types.equals(this, (GenericArrayType) o);
}
@Override public int hashCode() {
@Override
public int hashCode() {
return componentType.hashCode();
}
@Override public String toString() {
@Override
public String toString() {
return typeToString(componentType) + "[]";
}
}
@@ -476,26 +477,29 @@ public final class Util {
}
}
@Override public Type[] getUpperBounds() {
return new Type[] { upperBound };
@Override
public Type[] getUpperBounds() {
return new Type[] {upperBound};
}
@Override public Type[] getLowerBounds() {
return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY;
@Override
public Type[] getLowerBounds() {
return lowerBound != null ? new Type[] {lowerBound} : EMPTY_TYPE_ARRAY;
}
@Override public boolean equals(Object other) {
return other instanceof WildcardType
&& Types.equals(this, (WildcardType) other);
@Override
public boolean equals(Object other) {
return other instanceof WildcardType && Types.equals(this, (WildcardType) other);
}
@Override public int hashCode() {
@Override
public int hashCode() {
// This equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds()).
return (lowerBound != null ? 31 + lowerBound.hashCode() : 1)
^ (31 + upperBound.hashCode());
return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) ^ (31 + upperBound.hashCode());
}
@Override public String toString() {
@Override
public String toString() {
if (lowerBound != null) {
return "? super " + typeToString(lowerBound);
} else if (upperBound == Object.class) {
@@ -506,8 +510,8 @@ public final class Util {
}
}
public static String typeAnnotatedWithAnnotations(Type type,
Set<? extends Annotation> annotations) {
public static String typeAnnotatedWithAnnotations(
Type type, Set<? extends Annotation> annotations) {
return type + (annotations.isEmpty() ? " (with no annotations)" : " annotated " + annotations);
}
@@ -515,8 +519,8 @@ public final class Util {
* Loads the generated JsonAdapter for classes annotated {@link JsonClass}. This works because it
* uses the same naming conventions as {@code JsonClassCodeGenProcessor}.
*/
public static @Nullable JsonAdapter<?> generatedAdapter(Moshi moshi, Type type,
Class<?> rawType) {
public static @Nullable JsonAdapter<?> generatedAdapter(
Moshi moshi, Type type, Class<?> rawType) {
JsonClass jsonClass = rawType.getAnnotation(JsonClass.class);
if (jsonClass == null || !jsonClass.generateAdapter()) {
return null;
@@ -525,8 +529,9 @@ public final class Util {
Class<? extends JsonAdapter<?>> adapterClass = null;
try {
//noinspection unchecked - We generate types to match.
adapterClass = (Class<? extends JsonAdapter<?>>)
Class.forName(adapterClassName, true, rawType.getClassLoader());
adapterClass =
(Class<? extends JsonAdapter<?>>)
Class.forName(adapterClassName, true, rawType.getClassLoader());
Constructor<? extends JsonAdapter<?>> constructor;
Object[] args;
if (type instanceof ParameterizedType) {
@@ -534,16 +539,16 @@ public final class Util {
try {
// Common case first
constructor = adapterClass.getDeclaredConstructor(Moshi.class, Type[].class);
args = new Object[] { moshi, typeArgs };
args = new Object[] {moshi, typeArgs};
} catch (NoSuchMethodException e) {
constructor = adapterClass.getDeclaredConstructor(Type[].class);
args = new Object[] { typeArgs };
args = new Object[] {typeArgs};
}
} else {
try {
// Common case first
constructor = adapterClass.getDeclaredConstructor(Moshi.class);
args = new Object[] { moshi };
args = new Object[] {moshi};
} catch (NoSuchMethodException e) {
constructor = adapterClass.getDeclaredConstructor();
args = new Object[0];
@@ -552,25 +557,25 @@ public final class Util {
constructor.setAccessible(true);
return constructor.newInstance(args).nullSafe();
} catch (ClassNotFoundException e) {
throw new RuntimeException(
"Failed to find the generated JsonAdapter class for " + type, e);
throw new RuntimeException("Failed to find the generated JsonAdapter class for " + type, e);
} catch (NoSuchMethodException e) {
if (!(type instanceof ParameterizedType) && adapterClass.getTypeParameters().length != 0) {
throw new RuntimeException(
"Failed to find the generated JsonAdapter constructor for '" + type
+ "'. Suspiciously, the type was not parameterized but the target class '"
+ adapterClass.getCanonicalName() + "' is generic. Consider using "
+ "Types#newParameterizedType() to define these missing type variables.", e);
"Failed to find the generated JsonAdapter constructor for '"
+ type
+ "'. Suspiciously, the type was not parameterized but the target class '"
+ adapterClass.getCanonicalName()
+ "' is generic. Consider using "
+ "Types#newParameterizedType() to define these missing type variables.",
e);
} else {
throw new RuntimeException(
"Failed to find the generated JsonAdapter constructor for " + type, e);
}
} catch (IllegalAccessException e) {
throw new RuntimeException(
"Failed to access the generated JsonAdapter for " + type, e);
throw new RuntimeException("Failed to access the generated JsonAdapter for " + type, e);
} catch (InstantiationException e) {
throw new RuntimeException(
"Failed to instantiate the generated JsonAdapter for " + type, e);
throw new RuntimeException("Failed to instantiate the generated JsonAdapter for " + type, e);
} catch (InvocationTargetException e) {
throw rethrowCause(e);
}
@@ -589,8 +594,9 @@ public final class Util {
*/
public static <T> Constructor<T> lookupDefaultsConstructor(Class<T> targetClass) {
if (DEFAULT_CONSTRUCTOR_MARKER == null) {
throw new IllegalStateException("DefaultConstructorMarker not on classpath. Make sure the "
+ "Kotlin stdlib is on the classpath.");
throw new IllegalStateException(
"DefaultConstructorMarker not on classpath. Make sure the "
+ "Kotlin stdlib is on the classpath.");
}
Constructor<T> defaultConstructor = findConstructor(targetClass);
defaultConstructor.setAccessible(true);
@@ -611,33 +617,29 @@ public final class Util {
}
public static JsonDataException missingProperty(
String propertyName,
String jsonName,
JsonReader reader
) {
String propertyName, String jsonName, JsonReader reader) {
String path = reader.getPath();
String message;
if (jsonName.equals(propertyName)) {
message = String.format("Required value '%s' missing at %s", propertyName, path);
} else {
message = String.format("Required value '%s' (JSON name '%s') missing at %s",
propertyName, jsonName, path);
message =
String.format(
"Required value '%s' (JSON name '%s') missing at %s", propertyName, jsonName, path);
}
return new JsonDataException(message);
}
public static JsonDataException unexpectedNull(
String propertyName,
String jsonName,
JsonReader reader
) {
String propertyName, String jsonName, JsonReader reader) {
String path = reader.getPath();
String message;
if (jsonName.equals(propertyName)) {
message = String.format("Non-null value '%s' was null at %s", propertyName, path);
} else {
message = String.format("Non-null value '%s' (JSON name '%s') was null at %s",
propertyName, jsonName, path);
message =
String.format(
"Non-null value '%s' (JSON name '%s') was null at %s", propertyName, jsonName, path);
}
return new JsonDataException(message);
}

View File

@@ -1,4 +1,3 @@
package android.util;
public final class Pair {
}
public final class Pair {}

View File

@@ -15,6 +15,9 @@
*/
package com.squareup.moshi;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.assertj.core.api.Assertions.assertThat;
import com.squareup.moshi.internal.Util;
import java.io.IOException;
import java.lang.annotation.Annotation;
@@ -23,9 +26,6 @@ import java.lang.reflect.Type;
import java.util.Set;
import org.junit.Test;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.assertj.core.api.Assertions.assertThat;
public final class CircularAdaptersTest {
static class Team {
final String lead;
@@ -47,17 +47,22 @@ public final class CircularAdaptersTest {
}
}
@Test public void circularAdapters() throws Exception {
@Test
public void circularAdapters() throws Exception {
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Team> teamAdapter = moshi.adapter(Team.class);
Team team = new Team("Alice", new Project("King", new Team("Charlie",
new Project("Delivery", null))));
assertThat(teamAdapter.toJson(team)).isEqualTo("{\"lead\":\"Alice\",\"projects\":[{\"name\":"
+ "\"King\",\"teams\":[{\"lead\":\"Charlie\",\"projects\":[{\"name\":\"Delivery\"}]}]}]}");
Team team =
new Team("Alice", new Project("King", new Team("Charlie", new Project("Delivery", null))));
assertThat(teamAdapter.toJson(team))
.isEqualTo(
"{\"lead\":\"Alice\",\"projects\":[{\"name\":"
+ "\"King\",\"teams\":[{\"lead\":\"Charlie\",\"projects\":[{\"name\":\"Delivery\"}]}]}]}");
Team fromJson = teamAdapter.fromJson("{\"lead\":\"Alice\",\"projects\":[{\"name\":"
+ "\"King\",\"teams\":[{\"lead\":\"Charlie\",\"projects\":[{\"name\":\"Delivery\"}]}]}]}");
Team fromJson =
teamAdapter.fromJson(
"{\"lead\":\"Alice\",\"projects\":[{\"name\":"
+ "\"King\",\"teams\":[{\"lead\":\"Charlie\",\"projects\":[{\"name\":\"Delivery\"}]}]}]}");
assertThat(fromJson.lead).isEqualTo("Alice");
assertThat(fromJson.projects[0].name).isEqualTo("King");
assertThat(fromJson.projects[0].teams[0].lead).isEqualTo("Charlie");
@@ -66,13 +71,11 @@ public final class CircularAdaptersTest {
@Retention(RUNTIME)
@JsonQualifier
public @interface Left {
}
public @interface Left {}
@Retention(RUNTIME)
@JsonQualifier
public @interface Right {
}
public @interface Right {}
static class Node {
final String name;
@@ -101,8 +104,8 @@ public final class CircularAdaptersTest {
* work.
*/
static class PrefixingNodeFactory implements JsonAdapter.Factory {
@Override public JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
@Override
public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (type != Node.class) return null;
final String prefix;
@@ -117,11 +120,13 @@ public final class CircularAdaptersTest {
final JsonAdapter<Node> delegate = moshi.nextAdapter(this, Node.class, Util.NO_ANNOTATIONS);
return new JsonAdapter<Node>() {
@Override public void toJson(JsonWriter writer, Node value) throws IOException {
@Override
public void toJson(JsonWriter writer, Node value) throws IOException {
delegate.toJson(writer, value.plusPrefix(prefix));
}
@Override public Node fromJson(JsonReader reader) throws IOException {
@Override
public Node fromJson(JsonReader reader) throws IOException {
Node result = delegate.fromJson(reader);
return result.minusPrefix(prefix);
}
@@ -129,26 +134,31 @@ public final class CircularAdaptersTest {
}
}
@Test public void circularAdaptersAndAnnotations() throws Exception {
Moshi moshi = new Moshi.Builder()
.add(new PrefixingNodeFactory())
.build();
@Test
public void circularAdaptersAndAnnotations() throws Exception {
Moshi moshi = new Moshi.Builder().add(new PrefixingNodeFactory()).build();
JsonAdapter<Node> nodeAdapter = moshi.adapter(Node.class);
Node tree = new Node("C",
new Node("A", null, new Node("B", null, null)),
new Node("D", null, new Node("E", null, null)));
assertThat(nodeAdapter.toJson(tree)).isEqualTo("{"
+ "\"left\":{\"name\":\"L A\",\"right\":{\"name\":\"R B\"}},"
+ "\"name\":\"C\","
+ "\"right\":{\"name\":\"R D\",\"right\":{\"name\":\"R E\"}}"
+ "}");
Node tree =
new Node(
"C",
new Node("A", null, new Node("B", null, null)),
new Node("D", null, new Node("E", null, null)));
assertThat(nodeAdapter.toJson(tree))
.isEqualTo(
"{"
+ "\"left\":{\"name\":\"L A\",\"right\":{\"name\":\"R B\"}},"
+ "\"name\":\"C\","
+ "\"right\":{\"name\":\"R D\",\"right\":{\"name\":\"R E\"}}"
+ "}");
Node fromJson = nodeAdapter.fromJson("{"
+ "\"left\":{\"name\":\"L A\",\"right\":{\"name\":\"R B\"}},"
+ "\"name\":\"C\","
+ "\"right\":{\"name\":\"R D\",\"right\":{\"name\":\"R E\"}}"
+ "}");
Node fromJson =
nodeAdapter.fromJson(
"{"
+ "\"left\":{\"name\":\"L A\",\"right\":{\"name\":\"R B\"}},"
+ "\"name\":\"C\","
+ "\"right\":{\"name\":\"R D\",\"right\":{\"name\":\"R E\"}}"
+ "}");
assertThat(fromJson.name).isEqualTo("C");
assertThat(fromJson.left.name).isEqualTo("A");
assertThat(fromJson.left.right.name).isEqualTo("B");

View File

@@ -15,6 +15,11 @@
*/
package com.squareup.moshi;
import static com.squareup.moshi.TestUtil.newReader;
import static com.squareup.moshi.internal.Util.NO_ANNOTATIONS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
@@ -24,11 +29,6 @@ import java.util.SimpleTimeZone;
import okio.Buffer;
import org.junit.Test;
import static com.squareup.moshi.TestUtil.newReader;
import static com.squareup.moshi.internal.Util.NO_ANNOTATIONS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
public final class ClassJsonAdapterTest {
private final Moshi moshi = new Moshi.Builder().build();
@@ -37,7 +37,8 @@ public final class ClassJsonAdapterTest {
boolean extraCheese;
}
@Test public void basicClassAdapter() throws Exception {
@Test
public void basicClassAdapter() throws Exception {
BasicPizza value = new BasicPizza();
value.diameter = 13;
value.extraCheese = true;
@@ -53,14 +54,15 @@ public final class ClassJsonAdapterTest {
private String secretIngredient;
}
@Test public void privateFields() throws Exception {
@Test
public void privateFields() throws Exception {
PrivateFieldsPizza value = new PrivateFieldsPizza();
value.secretIngredient = "vodka";
String toJson = toJson(PrivateFieldsPizza.class, value);
assertThat(toJson).isEqualTo("{\"secretIngredient\":\"vodka\"}");
PrivateFieldsPizza fromJson = fromJson(
PrivateFieldsPizza.class, "{\"secretIngredient\":\"vodka\"}");
PrivateFieldsPizza fromJson =
fromJson(PrivateFieldsPizza.class, "{\"secretIngredient\":\"vodka\"}");
assertThat(fromJson.secretIngredient).isEqualTo("vodka");
}
@@ -72,7 +74,8 @@ public final class ClassJsonAdapterTest {
boolean chocolate;
}
@Test public void typeHierarchy() throws Exception {
@Test
public void typeHierarchy() throws Exception {
DessertPizza value = new DessertPizza();
value.diameter = 13;
value.chocolate = true;
@@ -95,7 +98,8 @@ public final class ClassJsonAdapterTest {
int e;
}
@Test public void fieldsAreAlphabeticalAcrossFlattenedHierarchy() throws Exception {
@Test
public void fieldsAreAlphabeticalAcrossFlattenedHierarchy() throws Exception {
ExtendsBaseAbcde value = new ExtendsBaseAbcde();
value.a = 4;
value.b = 5;
@@ -105,8 +109,8 @@ public final class ClassJsonAdapterTest {
String toJson = toJson(ExtendsBaseAbcde.class, value);
assertThat(toJson).isEqualTo("{\"a\":4,\"b\":5,\"c\":6,\"d\":7,\"e\":8}");
ExtendsBaseAbcde fromJson = fromJson(
ExtendsBaseAbcde.class, "{\"a\":4,\"b\":5,\"c\":6,\"d\":7,\"e\":8}");
ExtendsBaseAbcde fromJson =
fromJson(ExtendsBaseAbcde.class, "{\"a\":4,\"b\":5,\"c\":6,\"d\":7,\"e\":8}");
assertThat(fromJson.a).isEqualTo(4);
assertThat(fromJson.b).isEqualTo(5);
assertThat(fromJson.c).isEqualTo(6);
@@ -119,7 +123,8 @@ public final class ClassJsonAdapterTest {
int b;
}
@Test public void staticFieldsOmitted() throws Exception {
@Test
public void staticFieldsOmitted() throws Exception {
StaticFields value = new StaticFields();
value.b = 12;
String toJson = toJson(StaticFields.class, value);
@@ -135,7 +140,8 @@ public final class ClassJsonAdapterTest {
int b;
}
@Test public void transientFieldsOmitted() throws Exception {
@Test
public void transientFieldsOmitted() throws Exception {
TransientFields value = new TransientFields();
value.a = 11;
value.b = 12;
@@ -155,30 +161,38 @@ public final class ClassJsonAdapterTest {
int a;
}
@Test public void fieldNameCollision() throws Exception {
@Test
public void fieldNameCollision() throws Exception {
try {
ClassJsonAdapter.FACTORY.create(ExtendsBaseA.class, NO_ANNOTATIONS, moshi);
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Conflicting fields:\n"
+ " int com.squareup.moshi.ClassJsonAdapterTest$ExtendsBaseA.a\n"
+ " int com.squareup.moshi.ClassJsonAdapterTest$BaseA.a");
assertThat(expected)
.hasMessage(
"Conflicting fields:\n"
+ " int com.squareup.moshi.ClassJsonAdapterTest$ExtendsBaseA.a\n"
+ " int com.squareup.moshi.ClassJsonAdapterTest$BaseA.a");
}
}
static class NameCollision {
String foo;
@Json(name = "foo") String bar;
@Json(name = "foo")
String bar;
}
@Test public void jsonAnnotationNameCollision() throws Exception {
@Test
public void jsonAnnotationNameCollision() throws Exception {
try {
ClassJsonAdapter.FACTORY.create(NameCollision.class, NO_ANNOTATIONS, moshi);
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Conflicting fields:\n"
+ " java.lang.String com.squareup.moshi.ClassJsonAdapterTest$NameCollision.foo\n"
+ " java.lang.String com.squareup.moshi.ClassJsonAdapterTest$NameCollision.bar");
assertThat(expected)
.hasMessage(
"Conflicting fields:\n"
+ " java.lang.String com.squareup.moshi.ClassJsonAdapterTest$NameCollision.foo\n"
+ " java.lang.String com.squareup.moshi.ClassJsonAdapterTest$NameCollision.bar");
}
}
@@ -190,7 +204,8 @@ public final class ClassJsonAdapterTest {
int a;
}
@Test public void fieldNameCollisionWithTransientFieldIsOkay() throws Exception {
@Test
public void fieldNameCollisionWithTransientFieldIsOkay() throws Exception {
ExtendsTransientBaseA value = new ExtendsTransientBaseA();
value.a = 11;
((TransientBaseA) value).a = 12;
@@ -211,7 +226,8 @@ public final class ClassJsonAdapterTest {
}
}
@Test public void noArgConstructor() throws Exception {
@Test
public void noArgConstructor() throws Exception {
NoArgConstructor fromJson = fromJson(NoArgConstructor.class, "{\"b\":8}");
assertThat(fromJson.a).isEqualTo(5);
assertThat(fromJson.b).isEqualTo(8);
@@ -223,7 +239,8 @@ public final class ClassJsonAdapterTest {
}
}
@Test public void noArgConstructorThrowsCheckedException() throws Exception {
@Test
public void noArgConstructorThrowsCheckedException() throws Exception {
try {
fromJson(NoArgConstructorThrowsCheckedException.class, "{}");
fail();
@@ -238,7 +255,8 @@ public final class ClassJsonAdapterTest {
}
}
@Test public void noArgConstructorThrowsUncheckedException() throws Exception {
@Test
public void noArgConstructorThrowsUncheckedException() throws Exception {
try {
fromJson(NoArgConstructorThrowsUncheckedException.class, "{}");
fail();
@@ -252,9 +270,10 @@ public final class ClassJsonAdapterTest {
int b;
}
@Test public void noArgConstructorFieldDefaultsHonored() throws Exception {
NoArgConstructorWithDefaultField fromJson = fromJson(
NoArgConstructorWithDefaultField.class, "{\"b\":8}");
@Test
public void noArgConstructorFieldDefaultsHonored() throws Exception {
NoArgConstructorWithDefaultField fromJson =
fromJson(NoArgConstructorWithDefaultField.class, "{\"b\":8}");
assertThat(fromJson.a).isEqualTo(5);
assertThat(fromJson.b).isEqualTo(8);
}
@@ -267,7 +286,8 @@ public final class ClassJsonAdapterTest {
}
}
@Test public void magicConstructor() throws Exception {
@Test
public void magicConstructor() throws Exception {
MagicConstructor fromJson = fromJson(MagicConstructor.class, "{\"a\":8}");
assertThat(fromJson.a).isEqualTo(8);
}
@@ -281,9 +301,10 @@ public final class ClassJsonAdapterTest {
}
}
@Test public void magicConstructorFieldDefaultsNotHonored() throws Exception {
MagicConstructorWithDefaultField fromJson = fromJson(
MagicConstructorWithDefaultField.class, "{\"b\":3}");
@Test
public void magicConstructorFieldDefaultsNotHonored() throws Exception {
MagicConstructorWithDefaultField fromJson =
fromJson(MagicConstructorWithDefaultField.class, "{\"b\":3}");
assertThat(fromJson.a).isEqualTo(0); // Surprising! No value is assigned.
assertThat(fromJson.b).isEqualTo(3);
}
@@ -292,7 +313,8 @@ public final class ClassJsonAdapterTest {
int a;
}
@Test public void nullRootObject() throws Exception {
@Test
public void nullRootObject() throws Exception {
String toJson = toJson(PrivateFieldsPizza.class, null);
assertThat(toJson).isEqualTo("null");
@@ -304,7 +326,8 @@ public final class ClassJsonAdapterTest {
String a = "not null";
}
@Test public void nullFieldValues() throws Exception {
@Test
public void nullFieldValues() throws Exception {
NullFieldValue value = new NullFieldValue();
value.a = null;
String toJson = toJson(NullFieldValue.class, value);
@@ -314,25 +337,30 @@ public final class ClassJsonAdapterTest {
assertThat(fromJson.a).isNull();
}
class NonStatic {
}
class NonStatic {}
@Test public void nonStaticNestedClassNotSupported() throws Exception {
@Test
public void nonStaticNestedClassNotSupported() throws Exception {
try {
ClassJsonAdapter.FACTORY.create(NonStatic.class, NO_ANNOTATIONS, moshi);
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Cannot serialize non-static nested class "
+ "com.squareup.moshi.ClassJsonAdapterTest$NonStatic");
assertThat(expected)
.hasMessage(
"Cannot serialize non-static nested class "
+ "com.squareup.moshi.ClassJsonAdapterTest$NonStatic");
}
}
@Test public void anonymousClassNotSupported() throws Exception {
Comparator<Object> c = new Comparator<Object>() {
@Override public int compare(Object a, Object b) {
return 0;
}
};
@Test
public void anonymousClassNotSupported() throws Exception {
Comparator<Object> c =
new Comparator<Object>() {
@Override
public int compare(Object a, Object b) {
return 0;
}
};
try {
ClassJsonAdapter.FACTORY.create(c.getClass(), NO_ANNOTATIONS, moshi);
fail();
@@ -341,35 +369,38 @@ public final class ClassJsonAdapterTest {
}
}
@Test public void localClassNotSupported() throws Exception {
class Local {
}
@Test
public void localClassNotSupported() throws Exception {
class Local {}
try {
ClassJsonAdapter.FACTORY.create(Local.class, NO_ANNOTATIONS, moshi);
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Cannot serialize local class "
+ "com.squareup.moshi.ClassJsonAdapterTest$1Local");
assertThat(expected)
.hasMessage(
"Cannot serialize local class " + "com.squareup.moshi.ClassJsonAdapterTest$1Local");
}
}
interface Interface {
}
interface Interface {}
@Test public void interfaceNotSupported() throws Exception {
@Test
public void interfaceNotSupported() throws Exception {
assertThat(ClassJsonAdapter.FACTORY.create(Interface.class, NO_ANNOTATIONS, moshi)).isNull();
}
static abstract class Abstract {
}
abstract static class Abstract {}
@Test public void abstractClassNotSupported() throws Exception {
@Test
public void abstractClassNotSupported() throws Exception {
try {
ClassJsonAdapter.FACTORY.create(Abstract.class, NO_ANNOTATIONS, moshi);
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("Cannot serialize abstract class "
+ "com.squareup.moshi.ClassJsonAdapterTest$Abstract");
assertThat(expected)
.hasMessage(
"Cannot serialize abstract class "
+ "com.squareup.moshi.ClassJsonAdapterTest$Abstract");
}
}
@@ -381,14 +412,15 @@ public final class ClassJsonAdapterTest {
}
}
@Test public void platformSuperclassPrivateFieldIsExcluded() throws Exception {
@Test
public void platformSuperclassPrivateFieldIsExcluded() throws Exception {
ExtendsPlatformClassWithPrivateField value = new ExtendsPlatformClassWithPrivateField();
value.a = 4;
String toJson = toJson(ExtendsPlatformClassWithPrivateField.class, value);
assertThat(toJson).isEqualTo("{\"a\":4}");
ExtendsPlatformClassWithPrivateField fromJson = fromJson(
ExtendsPlatformClassWithPrivateField.class, "{\"a\":4,\"ID\":\"BAR\"}");
ExtendsPlatformClassWithPrivateField fromJson =
fromJson(ExtendsPlatformClassWithPrivateField.class, "{\"a\":4,\"ID\":\"BAR\"}");
assertThat(fromJson.a).isEqualTo(4);
assertThat(fromJson.getID()).isEqualTo("FOO");
}
@@ -401,7 +433,8 @@ public final class ClassJsonAdapterTest {
}
}
@Test public void platformSuperclassProtectedFieldIsIncluded() throws Exception {
@Test
public void platformSuperclassProtectedFieldIsIncluded() throws Exception {
ExtendsPlatformClassWithProtectedField value = new ExtendsPlatformClassWithProtectedField();
value.a = 4;
value.write(5);
@@ -409,36 +442,48 @@ public final class ClassJsonAdapterTest {
String toJson = toJson(ExtendsPlatformClassWithProtectedField.class, value);
assertThat(toJson).isEqualTo("{\"a\":4,\"buf\":[5,6],\"count\":2}");
ExtendsPlatformClassWithProtectedField fromJson = fromJson(
ExtendsPlatformClassWithProtectedField.class, "{\"a\":4,\"buf\":[5,6],\"count\":2}");
ExtendsPlatformClassWithProtectedField fromJson =
fromJson(
ExtendsPlatformClassWithProtectedField.class, "{\"a\":4,\"buf\":[5,6],\"count\":2}");
assertThat(fromJson.a).isEqualTo(4);
assertThat(fromJson.toByteArray()).contains((byte) 5, (byte) 6);
}
static class NamedFields {
@Json(name = "#") List<String> phoneNumbers;
@Json(name = "@") String emailAddress;
@Json(name = "zip code") String zipCode;
@Json(name = "#")
List<String> phoneNumbers;
@Json(name = "@")
String emailAddress;
@Json(name = "zip code")
String zipCode;
}
@Test public void jsonAnnotationHonored() throws Exception {
@Test
public void jsonAnnotationHonored() throws Exception {
NamedFields value = new NamedFields();
value.phoneNumbers = Arrays.asList("8005553333", "8005554444");
value.emailAddress = "cash@square.com";
value.zipCode = "94043";
String toJson = toJson(NamedFields.class, value);
assertThat(toJson).isEqualTo("{"
+ "\"#\":[\"8005553333\",\"8005554444\"],"
+ "\"@\":\"cash@square.com\","
+ "\"zip code\":\"94043\""
+ "}");
assertThat(toJson)
.isEqualTo(
"{"
+ "\"#\":[\"8005553333\",\"8005554444\"],"
+ "\"@\":\"cash@square.com\","
+ "\"zip code\":\"94043\""
+ "}");
NamedFields fromJson = fromJson(NamedFields.class, "{"
+ "\"#\":[\"8005553333\",\"8005554444\"],"
+ "\"@\":\"cash@square.com\","
+ "\"zip code\":\"94043\""
+ "}");
NamedFields fromJson =
fromJson(
NamedFields.class,
"{"
+ "\"#\":[\"8005553333\",\"8005554444\"],"
+ "\"@\":\"cash@square.com\","
+ "\"zip code\":\"94043\""
+ "}");
assertThat(fromJson.phoneNumbers).isEqualTo(Arrays.asList("8005553333", "8005554444"));
assertThat(fromJson.emailAddress).isEqualTo("cash@square.com");
assertThat(fromJson.zipCode).isEqualTo("94043");
@@ -452,19 +497,24 @@ public final class ClassJsonAdapterTest {
}
}
@Test public void parameterizedType() throws Exception {
@Test
public void parameterizedType() throws Exception {
@SuppressWarnings("unchecked")
JsonAdapter<Box<Integer>> adapter = (JsonAdapter<Box<Integer>>) ClassJsonAdapter.FACTORY.create(
Types.newParameterizedTypeWithOwner(ClassJsonAdapterTest.class, Box.class, Integer.class),
NO_ANNOTATIONS, moshi);
JsonAdapter<Box<Integer>> adapter =
(JsonAdapter<Box<Integer>>)
ClassJsonAdapter.FACTORY.create(
Types.newParameterizedTypeWithOwner(
ClassJsonAdapterTest.class, Box.class, Integer.class),
NO_ANNOTATIONS,
moshi);
assertThat(adapter.fromJson("{\"data\":5}").data).isEqualTo(5);
assertThat(adapter.toJson(new Box<>(5))).isEqualTo("{\"data\":5}");
}
private <T> String toJson(Class<T> type, T value) throws IOException {
@SuppressWarnings("unchecked") // Factory.create returns an adapter that matches its argument.
JsonAdapter<T> jsonAdapter = (JsonAdapter<T>) ClassJsonAdapter.FACTORY.create(
type, NO_ANNOTATIONS, moshi);
JsonAdapter<T> jsonAdapter =
(JsonAdapter<T>) ClassJsonAdapter.FACTORY.create(type, NO_ANNOTATIONS, moshi);
// Wrap in an array to avoid top-level object warnings without going completely lenient.
Buffer buffer = new Buffer();
@@ -481,8 +531,8 @@ public final class ClassJsonAdapterTest {
private <T> T fromJson(Class<T> type, String json) throws IOException {
@SuppressWarnings("unchecked") // Factory.create returns an adapter that matches its argument.
JsonAdapter<T> jsonAdapter = (JsonAdapter<T>) ClassJsonAdapter.FACTORY.create(
type, NO_ANNOTATIONS, moshi);
JsonAdapter<T> jsonAdapter =
(JsonAdapter<T>) ClassJsonAdapter.FACTORY.create(type, NO_ANNOTATIONS, moshi);
// Wrap in an array to avoid top-level object warnings without going completely lenient.
JsonReader jsonReader = newReader("[" + json + "]");
jsonReader.beginArray();

View File

@@ -15,6 +15,8 @@
*/
package com.squareup.moshi;
import static org.assertj.core.api.Assertions.assertThat;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.ArrayList;
@@ -27,8 +29,6 @@ import java.util.concurrent.Future;
import javax.annotation.Nullable;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public final class DeferredAdapterTest {
/**
* When a type's JsonAdapter is circularly-dependent, Moshi creates a 'deferred adapter' to make
@@ -36,41 +36,43 @@ public final class DeferredAdapterTest {
* leak out until it's ready.
*
* <p>This test sets up a circular dependency [BlueNode -> GreenNode -> BlueNode] and then tries
* to use a GreenNode JSON adapter before the BlueNode JSON adapter is built. It creates a
* similar cycle [BlueNode -> RedNode -> BlueNode] so the order adapters are retrieved is
* insignificant.
* to use a GreenNode JSON adapter before the BlueNode JSON adapter is built. It creates a similar
* cycle [BlueNode -> RedNode -> BlueNode] so the order adapters are retrieved is insignificant.
*
* <p>This used to trigger a crash because we'd incorrectly put the GreenNode JSON adapter in the
* cache even though it depended upon an incomplete BlueNode JSON adapter.
*/
@Test public void concurrentSafe() {
@Test
public void concurrentSafe() {
final List<Throwable> failures = new ArrayList<>();
JsonAdapter.Factory factory = new JsonAdapter.Factory() {
int redAndGreenCount = 0;
JsonAdapter.Factory factory =
new JsonAdapter.Factory() {
int redAndGreenCount = 0;
@Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, final Moshi moshi) {
if ((type == RedNode.class || type == GreenNode.class) && redAndGreenCount++ == 1) {
doInAnotherThread(new Runnable() {
@Override public void run() {
GreenNode greenBlue = new GreenNode(new BlueNode(null, null));
assertThat(moshi.adapter(GreenNode.class).toJson(greenBlue))
.isEqualTo("{\"blue\":{}}");
@Override
public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, final Moshi moshi) {
if ((type == RedNode.class || type == GreenNode.class) && redAndGreenCount++ == 1) {
doInAnotherThread(
new Runnable() {
@Override
public void run() {
GreenNode greenBlue = new GreenNode(new BlueNode(null, null));
assertThat(moshi.adapter(GreenNode.class).toJson(greenBlue))
.isEqualTo("{\"blue\":{}}");
RedNode redBlue = new RedNode(new BlueNode(null, null));
assertThat(moshi.adapter(RedNode.class).toJson(redBlue))
.isEqualTo("{\"blue\":{}}");
RedNode redBlue = new RedNode(new BlueNode(null, null));
assertThat(moshi.adapter(RedNode.class).toJson(redBlue))
.isEqualTo("{\"blue\":{}}");
}
});
}
});
}
return null;
}
};
return null;
}
};
Moshi moshi = new Moshi.Builder()
.add(factory)
.build();
Moshi moshi = new Moshi.Builder().add(factory).build();
JsonAdapter<BlueNode> jsonAdapter = moshi.adapter(BlueNode.class);
assertThat(jsonAdapter.toJson(new BlueNode(new GreenNode(new BlueNode(null, null)), null)))

View File

@@ -15,6 +15,9 @@
*/
package com.squareup.moshi;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
@@ -23,9 +26,6 @@ import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
/** Note that this test makes heavy use of nested blocks, but these are for readability only. */
@RunWith(Parameterized.class)
public final class FlattenTest {
@@ -36,7 +36,8 @@ public final class FlattenTest {
return JsonCodecFactory.factories();
}
@Test public void flattenExample() throws Exception {
@Test
public void flattenExample() throws Exception {
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<List<Integer>> integersAdapter =
moshi.adapter(Types.newParameterizedType(List.class, Integer.class));
@@ -53,7 +54,8 @@ public final class FlattenTest {
assertThat(factory.json()).isEqualTo("[1,2,3,4,5]");
}
@Test public void flattenObject() throws Exception {
@Test
public void flattenObject() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginObject();
{
@@ -76,7 +78,8 @@ public final class FlattenTest {
assertThat(factory.json()).isEqualTo("{\"a\":\"aaa\",\"b\":\"bbb\",\"c\":\"ccc\"}");
}
@Test public void flattenArray() throws Exception {
@Test
public void flattenArray() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginArray();
{
@@ -96,7 +99,8 @@ public final class FlattenTest {
assertThat(factory.json()).isEqualTo("[\"a\",\"b\",\"c\"]");
}
@Test public void recursiveFlatten() throws Exception {
@Test
public void recursiveFlatten() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginArray();
{
@@ -126,7 +130,8 @@ public final class FlattenTest {
assertThat(factory.json()).isEqualTo("[\"a\",\"b\",\"c\",\"d\",\"e\"]");
}
@Test public void flattenMultipleNested() throws Exception {
@Test
public void flattenMultipleNested() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginArray();
{
@@ -151,7 +156,8 @@ public final class FlattenTest {
assertThat(factory.json()).isEqualTo("[\"a\",\"b\",\"c\",\"d\"]");
}
@Test public void flattenIsOnlyOneLevelDeep() throws Exception {
@Test
public void flattenIsOnlyOneLevelDeep() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginArray();
{
@@ -177,7 +183,8 @@ public final class FlattenTest {
assertThat(factory.json()).isEqualTo("[\"a\",\"b\",[\"c\"],\"d\",\"e\"]");
}
@Test public void flattenOnlySomeChildren() throws Exception {
@Test
public void flattenOnlySomeChildren() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginArray();
{
@@ -202,7 +209,8 @@ public final class FlattenTest {
assertThat(factory.json()).isEqualTo("[\"a\",\"b\",[\"c\"],\"d\"]");
}
@Test public void multipleCallsToFlattenSameNesting() throws Exception {
@Test
public void multipleCallsToFlattenSameNesting() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginArray();
{
@@ -236,7 +244,8 @@ public final class FlattenTest {
assertThat(factory.json()).isEqualTo("[\"a\",\"b\",\"c\",\"d\",\"e\"]");
}
@Test public void deepFlatten() throws Exception {
@Test
public void deepFlatten() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginArray();
{
@@ -270,7 +279,8 @@ public final class FlattenTest {
assertThat(factory.json()).isEqualTo("[\"a\"]");
}
@Test public void flattenTopLevel() {
@Test
public void flattenTopLevel() {
JsonWriter writer = factory.newWriter();
try {
writer.beginFlatten();
@@ -280,7 +290,8 @@ public final class FlattenTest {
}
}
@Test public void flattenDoesNotImpactOtherTypesInObjects() throws Exception {
@Test
public void flattenDoesNotImpactOtherTypesInObjects() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginObject();
{
@@ -305,7 +316,8 @@ public final class FlattenTest {
assertThat(factory.json()).isEqualTo("{\"a\":[\"aaa\"],\"b\":\"bbb\",\"c\":[\"ccc\"]}");
}
@Test public void flattenDoesNotImpactOtherTypesInArrays() throws Exception {
@Test
public void flattenDoesNotImpactOtherTypesInArrays() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginArray();
{

View File

@@ -15,6 +15,10 @@
*/
package com.squareup.moshi;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
@@ -28,10 +32,6 @@ import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class)
public final class JsonAdapterTest {
@Parameter public JsonCodecFactory factory;
@@ -41,16 +41,20 @@ public final class JsonAdapterTest {
return JsonCodecFactory.factories();
}
@Test public void lenient() throws Exception {
JsonAdapter<Double> lenient = new JsonAdapter<Double>() {
@Override public Double fromJson(JsonReader reader) throws IOException {
return reader.nextDouble();
}
@Test
public void lenient() throws Exception {
JsonAdapter<Double> lenient =
new JsonAdapter<Double>() {
@Override
public Double fromJson(JsonReader reader) throws IOException {
return reader.nextDouble();
}
@Override public void toJson(JsonWriter writer, Double value) throws IOException {
writer.value(value);
}
}.lenient();
@Override
public void toJson(JsonWriter writer, Double value) throws IOException {
writer.value(value);
}
}.lenient();
JsonReader reader = factory.newReader("[-Infinity, NaN, Infinity]");
reader.beginArray();
@@ -68,16 +72,20 @@ public final class JsonAdapterTest {
assertThat(factory.json()).isEqualTo("[-Infinity,NaN,Infinity]");
}
@Test public void nullSafe() throws Exception {
JsonAdapter<String> toUpperCase = new JsonAdapter<String>() {
@Override public String fromJson(JsonReader reader) throws IOException {
return reader.nextString().toUpperCase(Locale.US);
}
@Test
public void nullSafe() throws Exception {
JsonAdapter<String> toUpperCase =
new JsonAdapter<String>() {
@Override
public String fromJson(JsonReader reader) throws IOException {
return reader.nextString().toUpperCase(Locale.US);
}
@Override public void toJson(JsonWriter writer, String value) throws IOException {
writer.value(value.toUpperCase(Locale.US));
}
}.nullSafe();
@Override
public void toJson(JsonWriter writer, String value) throws IOException {
writer.value(value.toUpperCase(Locale.US));
}
}.nullSafe();
JsonReader reader = factory.newReader("[\"a\", null, \"c\"]");
reader.beginArray();
@@ -95,16 +103,20 @@ public final class JsonAdapterTest {
assertThat(factory.json()).isEqualTo("[\"A\",null,\"C\"]");
}
@Test public void nonNull() throws Exception {
JsonAdapter<String> toUpperCase = new JsonAdapter<String>() {
@Override public String fromJson(JsonReader reader) throws IOException {
return reader.nextString().toUpperCase(Locale.US);
}
@Test
public void nonNull() throws Exception {
JsonAdapter<String> toUpperCase =
new JsonAdapter<String>() {
@Override
public String fromJson(JsonReader reader) throws IOException {
return reader.nextString().toUpperCase(Locale.US);
}
@Override public void toJson(JsonWriter writer, String value) throws IOException {
writer.value(value.toUpperCase(Locale.US));
}
}.nonNull();
@Override
public void toJson(JsonWriter writer, String value) throws IOException {
writer.value(value.toUpperCase(Locale.US));
}
}.nonNull();
JsonReader reader = factory.newReader("[\"a\", null, \"c\"]");
reader.beginArray();
@@ -134,17 +146,21 @@ public final class JsonAdapterTest {
assertThat(factory.json()).isEqualTo("[\"A\",null,\"C\"]");
}
@Test public void failOnUnknown() throws Exception {
JsonAdapter<String> alwaysSkip = new JsonAdapter<String>() {
@Override public String fromJson(JsonReader reader) throws IOException {
reader.skipValue();
throw new AssertionError();
}
@Test
public void failOnUnknown() throws Exception {
JsonAdapter<String> alwaysSkip =
new JsonAdapter<String>() {
@Override
public String fromJson(JsonReader reader) throws IOException {
reader.skipValue();
throw new AssertionError();
}
@Override public void toJson(JsonWriter writer, String value) throws IOException {
throw new AssertionError();
}
}.failOnUnknown();
@Override
public void toJson(JsonWriter writer, String value) throws IOException {
throw new AssertionError();
}
}.failOnUnknown();
JsonReader reader = factory.newReader("[\"a\"]");
reader.beginArray();
@@ -158,43 +174,47 @@ public final class JsonAdapterTest {
reader.endArray();
}
@Test public void indent() throws Exception {
@Test
public void indent() throws Exception {
assumeTrue(factory.encodesToBytes());
JsonAdapter<List<String>> indent = new JsonAdapter<List<String>>() {
@Override public List<String> fromJson(JsonReader reader) throws IOException {
throw new AssertionError();
}
JsonAdapter<List<String>> indent =
new JsonAdapter<List<String>>() {
@Override
public List<String> fromJson(JsonReader reader) throws IOException {
throw new AssertionError();
}
@Override public void toJson(JsonWriter writer, List<String> value) throws IOException {
writer.beginArray();
for (String s : value) {
writer.value(s);
}
writer.endArray();
}
}.indent("\t\t\t");
@Override
public void toJson(JsonWriter writer, List<String> value) throws IOException {
writer.beginArray();
for (String s : value) {
writer.value(s);
}
writer.endArray();
}
}.indent("\t\t\t");
JsonWriter writer = factory.newWriter();
indent.toJson(writer, Arrays.asList("a", "b", "c"));
assertThat(factory.json()).isEqualTo(""
+ "[\n"
+ "\t\t\t\"a\",\n"
+ "\t\t\t\"b\",\n"
+ "\t\t\t\"c\"\n"
+ "]");
assertThat(factory.json())
.isEqualTo("" + "[\n" + "\t\t\t\"a\",\n" + "\t\t\t\"b\",\n" + "\t\t\t\"c\"\n" + "]");
}
@Test public void indentDisallowsNull() throws Exception {
JsonAdapter<Object> adapter = new JsonAdapter<Object>() {
@Override public Object fromJson(JsonReader reader) {
throw new AssertionError();
}
@Test
public void indentDisallowsNull() throws Exception {
JsonAdapter<Object> adapter =
new JsonAdapter<Object>() {
@Override
public Object fromJson(JsonReader reader) {
throw new AssertionError();
}
@Override public void toJson(JsonWriter writer, Object value) {
throw new AssertionError();
}
};
@Override
public void toJson(JsonWriter writer, Object value) {
throw new AssertionError();
}
};
try {
adapter.indent(null);
fail();
@@ -203,36 +223,44 @@ public final class JsonAdapterTest {
}
}
@Test public void serializeNulls() throws Exception {
JsonAdapter<Map<String, String>> serializeNulls = new JsonAdapter<Map<String, String>>() {
@Override public Map<String, String> fromJson(JsonReader reader) throws IOException {
throw new AssertionError();
}
@Test
public void serializeNulls() throws Exception {
JsonAdapter<Map<String, String>> serializeNulls =
new JsonAdapter<Map<String, String>>() {
@Override
public Map<String, String> fromJson(JsonReader reader) throws IOException {
throw new AssertionError();
}
@Override public void toJson(JsonWriter writer, Map<String, String> map) throws IOException {
writer.beginObject();
for (Map.Entry<String, String> entry : map.entrySet()) {
writer.name(entry.getKey()).value(entry.getValue());
}
writer.endObject();
}
}.serializeNulls();
@Override
public void toJson(JsonWriter writer, Map<String, String> map) throws IOException {
writer.beginObject();
for (Map.Entry<String, String> entry : map.entrySet()) {
writer.name(entry.getKey()).value(entry.getValue());
}
writer.endObject();
}
}.serializeNulls();
JsonWriter writer = factory.newWriter();
serializeNulls.toJson(writer, Collections.<String, String>singletonMap("a", null));
assertThat(factory.json()).isEqualTo("{\"a\":null}");
}
@Test public void stringDocumentMustBeFullyConsumed() throws IOException {
JsonAdapter<String> brokenAdapter = new JsonAdapter<String>() {
@Override public String fromJson(JsonReader reader) throws IOException {
return "Forgot to call reader.nextString().";
}
@Test
public void stringDocumentMustBeFullyConsumed() throws IOException {
JsonAdapter<String> brokenAdapter =
new JsonAdapter<String>() {
@Override
public String fromJson(JsonReader reader) throws IOException {
return "Forgot to call reader.nextString().";
}
@Override public void toJson(JsonWriter writer, @Nullable String value) throws IOException {
throw new AssertionError();
}
};
@Override
public void toJson(JsonWriter writer, @Nullable String value) throws IOException {
throw new AssertionError();
}
};
try {
brokenAdapter.fromJson("\"value\"");
fail();
@@ -241,57 +269,71 @@ public final class JsonAdapterTest {
}
}
@Test public void adapterFromJsonStringPeeksAtEnd() throws IOException {
JsonAdapter<Boolean> adapter = new JsonAdapter<Boolean>() {
@Override public Boolean fromJson(JsonReader reader) throws IOException {
return reader.nextBoolean();
}
@Test
public void adapterFromJsonStringPeeksAtEnd() throws IOException {
JsonAdapter<Boolean> adapter =
new JsonAdapter<Boolean>() {
@Override
public Boolean fromJson(JsonReader reader) throws IOException {
return reader.nextBoolean();
}
@Override public void toJson(JsonWriter writer, @Nullable Boolean value) throws IOException {
throw new AssertionError();
}
};
@Override
public void toJson(JsonWriter writer, @Nullable Boolean value) throws IOException {
throw new AssertionError();
}
};
try {
adapter.fromJson("true true");
fail();
} catch (JsonEncodingException e) {
assertThat(e).hasMessage(
"Use JsonReader.setLenient(true) to accept malformed JSON at path $");
assertThat(e)
.hasMessage("Use JsonReader.setLenient(true) to accept malformed JSON at path $");
}
}
@Test public void lenientAdapterFromJsonStringDoesNotPeekAtEnd() throws IOException {
JsonAdapter<Boolean> adapter = new JsonAdapter<Boolean>() {
@Override public Boolean fromJson(JsonReader reader) throws IOException {
return reader.nextBoolean();
}
@Test
public void lenientAdapterFromJsonStringDoesNotPeekAtEnd() throws IOException {
JsonAdapter<Boolean> adapter =
new JsonAdapter<Boolean>() {
@Override
public Boolean fromJson(JsonReader reader) throws IOException {
return reader.nextBoolean();
}
@Override public void toJson(JsonWriter writer, @Nullable Boolean value) throws IOException {
throw new AssertionError();
}
}.lenient();
@Override
public void toJson(JsonWriter writer, @Nullable Boolean value) throws IOException {
throw new AssertionError();
}
}.lenient();
assertThat(adapter.fromJson("true true")).isEqualTo(true);
}
@Test public void adaptersDelegateLeniency() throws IOException {
JsonAdapter<Boolean> adapter = new JsonAdapter<Boolean>() {
@Override public Boolean fromJson(JsonReader reader) throws IOException {
return reader.nextBoolean();
}
@Test
public void adaptersDelegateLeniency() throws IOException {
JsonAdapter<Boolean> adapter =
new JsonAdapter<Boolean>() {
@Override
public Boolean fromJson(JsonReader reader) throws IOException {
return reader.nextBoolean();
}
@Override public void toJson(JsonWriter writer, @Nullable Boolean value) throws IOException {
throw new AssertionError();
}
}.lenient().serializeNulls();
@Override
public void toJson(JsonWriter writer, @Nullable Boolean value) throws IOException {
throw new AssertionError();
}
}.lenient().serializeNulls();
assertThat(adapter.fromJson("true true")).isEqualTo(true);
}
@Test public void nullSafeDoesntDuplicate() {
@Test
public void nullSafeDoesntDuplicate() {
JsonAdapter<Boolean> adapter = new Moshi.Builder().build().adapter(Boolean.class).nullSafe();
assertThat(adapter.nullSafe()).isSameAs(adapter);
}
@Test public void nonNullDoesntDuplicate() {
@Test
public void nonNullDoesntDuplicate() {
JsonAdapter<Boolean> adapter = new Moshi.Builder().build().adapter(Boolean.class).nonNull();
assertThat(adapter.nonNull()).isSameAs(adapter);
}

View File

@@ -25,109 +25,126 @@ abstract class JsonCodecFactory {
private static final JsonAdapter<Object> OBJECT_ADAPTER = MOSHI.adapter(Object.class);
static List<Object[]> factories() {
final JsonCodecFactory utf8 = new JsonCodecFactory() {
Buffer buffer;
final JsonCodecFactory utf8 =
new JsonCodecFactory() {
Buffer buffer;
@Override public JsonReader newReader(String json) {
Buffer buffer = new Buffer().writeUtf8(json);
return JsonReader.of(buffer);
}
@Override
public JsonReader newReader(String json) {
Buffer buffer = new Buffer().writeUtf8(json);
return JsonReader.of(buffer);
}
@Override JsonWriter newWriter() {
buffer = new Buffer();
return new JsonUtf8Writer(buffer);
}
@Override
JsonWriter newWriter() {
buffer = new Buffer();
return new JsonUtf8Writer(buffer);
}
@Override String json() {
String result = buffer.readUtf8();
buffer = null;
return result;
}
@Override
String json() {
String result = buffer.readUtf8();
buffer = null;
return result;
}
@Override boolean encodesToBytes() {
return true;
}
@Override
boolean encodesToBytes() {
return true;
}
@Override public String toString() {
return "Utf8";
}
};
@Override
public String toString() {
return "Utf8";
}
};
final JsonCodecFactory value = new JsonCodecFactory() {
JsonValueWriter writer;
final JsonCodecFactory value =
new JsonCodecFactory() {
JsonValueWriter writer;
@Override public JsonReader newReader(String json) throws IOException {
Moshi moshi = new Moshi.Builder().build();
Object object = moshi.adapter(Object.class).lenient().fromJson(json);
return new JsonValueReader(object);
}
@Override
public JsonReader newReader(String json) throws IOException {
Moshi moshi = new Moshi.Builder().build();
Object object = moshi.adapter(Object.class).lenient().fromJson(json);
return new JsonValueReader(object);
}
// TODO(jwilson): fix precision checks and delete his method.
@Override boolean implementsStrictPrecision() {
return false;
}
// TODO(jwilson): fix precision checks and delete his method.
@Override
boolean implementsStrictPrecision() {
return false;
}
@Override JsonWriter newWriter() {
writer = new JsonValueWriter();
return writer;
}
@Override
JsonWriter newWriter() {
writer = new JsonValueWriter();
return writer;
}
@Override String json() {
// This writer writes a DOM. Use other Moshi features to serialize it as a string.
try {
Buffer buffer = new Buffer();
JsonWriter bufferedSinkWriter = JsonWriter.of(buffer);
bufferedSinkWriter.setSerializeNulls(true);
bufferedSinkWriter.setLenient(true);
OBJECT_ADAPTER.toJson(bufferedSinkWriter, writer.root());
return buffer.readUtf8();
} catch (IOException e) {
throw new AssertionError();
}
}
@Override
String json() {
// This writer writes a DOM. Use other Moshi features to serialize it as a string.
try {
Buffer buffer = new Buffer();
JsonWriter bufferedSinkWriter = JsonWriter.of(buffer);
bufferedSinkWriter.setSerializeNulls(true);
bufferedSinkWriter.setLenient(true);
OBJECT_ADAPTER.toJson(bufferedSinkWriter, writer.root());
return buffer.readUtf8();
} catch (IOException e) {
throw new AssertionError();
}
}
// TODO(jwilson): support BigDecimal and BigInteger and delete his method.
@Override boolean supportsBigNumbers() {
return false;
}
// TODO(jwilson): support BigDecimal and BigInteger and delete his method.
@Override
boolean supportsBigNumbers() {
return false;
}
@Override public String toString() {
return "Value";
}
};
@Override
public String toString() {
return "Value";
}
};
final JsonCodecFactory valuePeek = new JsonCodecFactory() {
@Override public JsonReader newReader(String json) throws IOException {
return value.newReader(json).peekJson();
}
final JsonCodecFactory valuePeek =
new JsonCodecFactory() {
@Override
public JsonReader newReader(String json) throws IOException {
return value.newReader(json).peekJson();
}
// TODO(jwilson): fix precision checks and delete his method.
@Override boolean implementsStrictPrecision() {
return false;
}
// TODO(jwilson): fix precision checks and delete his method.
@Override
boolean implementsStrictPrecision() {
return false;
}
@Override JsonWriter newWriter() {
return value.newWriter();
}
@Override
JsonWriter newWriter() {
return value.newWriter();
}
@Override String json() {
return value.json();
}
@Override
String json() {
return value.json();
}
// TODO(jwilson): support BigDecimal and BigInteger and delete his method.
@Override boolean supportsBigNumbers() {
return false;
}
// TODO(jwilson): support BigDecimal and BigInteger and delete his method.
@Override
boolean supportsBigNumbers() {
return false;
}
@Override public String toString() {
return "ValuePeek";
}
};
@Override
public String toString() {
return "ValuePeek";
}
};
return Arrays.asList(
new Object[] { utf8 },
new Object[] { value },
new Object[] { valuePeek });
return Arrays.asList(new Object[] {utf8}, new Object[] {value}, new Object[] {valuePeek});
}
abstract JsonReader newReader(String json) throws IOException;

View File

@@ -15,20 +15,19 @@
*/
package com.squareup.moshi;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.util.Date;
import org.junit.Test;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
public final class JsonQualifiersTest {
@Test public void builtInTypes() throws Exception {
Moshi moshi = new Moshi.Builder()
.add(new BuiltInTypesJsonAdapter())
.build();
@Test
public void builtInTypes() throws Exception {
Moshi moshi = new Moshi.Builder().add(new BuiltInTypesJsonAdapter()).build();
JsonAdapter<StringAndFooString> adapter = moshi.adapter(StringAndFooString.class);
StringAndFooString v1 = new StringAndFooString();
@@ -42,20 +41,22 @@ public final class JsonQualifiersTest {
}
static class BuiltInTypesJsonAdapter {
@ToJson String fooPrefixStringToString(@FooPrefix String s) {
@ToJson
String fooPrefixStringToString(@FooPrefix String s) {
return "foo" + s;
}
@FromJson @FooPrefix String fooPrefixStringFromString(String s) throws Exception {
@FromJson
@FooPrefix
String fooPrefixStringFromString(String s) throws Exception {
if (!s.startsWith("foo")) throw new JsonDataException();
return s.substring(3);
}
}
@Test public void readerWriterJsonAdapter() throws Exception {
Moshi moshi = new Moshi.Builder()
.add(new ReaderWriterJsonAdapter())
.build();
@Test
public void readerWriterJsonAdapter() throws Exception {
Moshi moshi = new Moshi.Builder().add(new ReaderWriterJsonAdapter()).build();
JsonAdapter<StringAndFooString> adapter = moshi.adapter(StringAndFooString.class);
StringAndFooString v1 = new StringAndFooString();
@@ -69,12 +70,14 @@ public final class JsonQualifiersTest {
}
static class ReaderWriterJsonAdapter {
@ToJson void fooPrefixStringToString(JsonWriter jsonWriter, @FooPrefix String s)
throws IOException {
@ToJson
void fooPrefixStringToString(JsonWriter jsonWriter, @FooPrefix String s) throws IOException {
jsonWriter.value("foo" + s);
}
@FromJson @FooPrefix String fooPrefixStringFromString(JsonReader reader) throws Exception {
@FromJson
@FooPrefix
String fooPrefixStringFromString(JsonReader reader) throws Exception {
String s = reader.nextString();
if (!s.startsWith("foo")) throw new JsonDataException();
return s.substring(3);
@@ -84,14 +87,12 @@ public final class JsonQualifiersTest {
/** Fields with this annotation get "foo" as a prefix in the JSON. */
@Retention(RUNTIME)
@JsonQualifier
public @interface FooPrefix {
}
public @interface FooPrefix {}
/** Fields with this annotation get "baz" as a suffix in the JSON. */
@Retention(RUNTIME)
@JsonQualifier
public @interface BazSuffix {
}
public @interface BazSuffix {}
static class StringAndFooString {
String a;
@@ -103,10 +104,10 @@ public final class JsonQualifiersTest {
@FooPrefix @BazSuffix String b;
}
@Test public void builtInTypesWithMultipleAnnotations() throws Exception {
Moshi moshi = new Moshi.Builder()
.add(new BuiltInTypesWithMultipleAnnotationsJsonAdapter())
.build();
@Test
public void builtInTypesWithMultipleAnnotations() throws Exception {
Moshi moshi =
new Moshi.Builder().add(new BuiltInTypesWithMultipleAnnotationsJsonAdapter()).build();
JsonAdapter<StringAndFooBazString> adapter = moshi.adapter(StringAndFooBazString.class);
StringAndFooBazString v1 = new StringAndFooBazString();
@@ -120,22 +121,25 @@ public final class JsonQualifiersTest {
}
static class BuiltInTypesWithMultipleAnnotationsJsonAdapter {
@ToJson String fooPrefixAndBazSuffixStringToString(@FooPrefix @BazSuffix String s) {
@ToJson
String fooPrefixAndBazSuffixStringToString(@FooPrefix @BazSuffix String s) {
return "foo" + s + "baz";
}
@FromJson @FooPrefix @BazSuffix String fooPrefixAndBazSuffixStringFromString(
String s) throws Exception {
@FromJson
@FooPrefix
@BazSuffix
String fooPrefixAndBazSuffixStringFromString(String s) throws Exception {
if (!s.startsWith("foo")) throw new JsonDataException();
if (!s.endsWith("baz")) throw new JsonDataException();
return s.substring(3, s.length() - 3);
}
}
@Test public void readerWriterWithMultipleAnnotations() throws Exception {
Moshi moshi = new Moshi.Builder()
.add(new ReaderWriterWithMultipleAnnotationsJsonAdapter())
.build();
@Test
public void readerWriterWithMultipleAnnotations() throws Exception {
Moshi moshi =
new Moshi.Builder().add(new ReaderWriterWithMultipleAnnotationsJsonAdapter()).build();
JsonAdapter<StringAndFooBazString> adapter = moshi.adapter(StringAndFooBazString.class);
StringAndFooBazString v1 = new StringAndFooBazString();
@@ -149,13 +153,16 @@ public final class JsonQualifiersTest {
}
static class ReaderWriterWithMultipleAnnotationsJsonAdapter {
@ToJson void fooPrefixAndBazSuffixStringToString(
JsonWriter jsonWriter, @FooPrefix @BazSuffix String s) throws IOException {
@ToJson
void fooPrefixAndBazSuffixStringToString(JsonWriter jsonWriter, @FooPrefix @BazSuffix String s)
throws IOException {
jsonWriter.value("foo" + s + "baz");
}
@FromJson @FooPrefix @BazSuffix String fooPrefixAndBazSuffixStringFromString(
JsonReader reader) throws Exception {
@FromJson
@FooPrefix
@BazSuffix
String fooPrefixAndBazSuffixStringFromString(JsonReader reader) throws Exception {
String s = reader.nextString();
if (!s.startsWith("foo")) throw new JsonDataException();
if (!s.endsWith("baz")) throw new JsonDataException();
@@ -163,11 +170,13 @@ public final class JsonQualifiersTest {
}
}
@Test public void basicTypesAnnotationDelegating() throws Exception {
Moshi moshi = new Moshi.Builder()
.add(new BuiltInTypesDelegatingJsonAdapter())
.add(new BuiltInTypesJsonAdapter())
.build();
@Test
public void basicTypesAnnotationDelegating() throws Exception {
Moshi moshi =
new Moshi.Builder()
.add(new BuiltInTypesDelegatingJsonAdapter())
.add(new BuiltInTypesJsonAdapter())
.build();
JsonAdapter<StringAndFooBazString> adapter = moshi.adapter(StringAndFooBazString.class);
StringAndFooBazString v1 = new StringAndFooBazString();
@@ -181,22 +190,28 @@ public final class JsonQualifiersTest {
}
static class BuiltInTypesDelegatingJsonAdapter {
@ToJson @FooPrefix String fooPrefixAndBazSuffixStringToString(@FooPrefix @BazSuffix String s) {
@ToJson
@FooPrefix
String fooPrefixAndBazSuffixStringToString(@FooPrefix @BazSuffix String s) {
return s + "baz";
}
@FromJson @FooPrefix @BazSuffix String fooPrefixAndBazSuffixStringFromString(
@FooPrefix String s) throws Exception {
@FromJson
@FooPrefix
@BazSuffix
String fooPrefixAndBazSuffixStringFromString(@FooPrefix String s) throws Exception {
if (!s.endsWith("baz")) throw new JsonDataException();
return s.substring(0, s.length() - 3);
}
}
@Test public void readerWriterAnnotationDelegating() throws Exception {
Moshi moshi = new Moshi.Builder()
.add(new BuiltInTypesDelegatingJsonAdapter())
.add(new ReaderWriterJsonAdapter())
.build();
@Test
public void readerWriterAnnotationDelegating() throws Exception {
Moshi moshi =
new Moshi.Builder()
.add(new BuiltInTypesDelegatingJsonAdapter())
.add(new ReaderWriterJsonAdapter())
.build();
JsonAdapter<StringAndFooBazString> adapter = moshi.adapter(StringAndFooBazString.class);
StringAndFooBazString v1 = new StringAndFooBazString();
@@ -209,22 +224,24 @@ public final class JsonQualifiersTest {
assertThat(v2.b).isEqualTo("bar");
}
@Test public void manualJsonAdapter() throws Exception {
JsonAdapter<String> fooPrefixAdapter = new JsonAdapter<String>() {
@Override public String fromJson(JsonReader reader) throws IOException {
String s = reader.nextString();
if (!s.startsWith("foo")) throw new JsonDataException();
return s.substring(3);
}
@Test
public void manualJsonAdapter() throws Exception {
JsonAdapter<String> fooPrefixAdapter =
new JsonAdapter<String>() {
@Override
public String fromJson(JsonReader reader) throws IOException {
String s = reader.nextString();
if (!s.startsWith("foo")) throw new JsonDataException();
return s.substring(3);
}
@Override public void toJson(JsonWriter writer, String value) throws IOException {
writer.value("foo" + value);
}
};
@Override
public void toJson(JsonWriter writer, String value) throws IOException {
writer.value("foo" + value);
}
};
Moshi moshi = new Moshi.Builder()
.add(String.class, FooPrefix.class, fooPrefixAdapter)
.build();
Moshi moshi = new Moshi.Builder().add(String.class, FooPrefix.class, fooPrefixAdapter).build();
JsonAdapter<StringAndFooString> adapter = moshi.adapter(StringAndFooString.class);
StringAndFooString v1 = new StringAndFooString();
@@ -237,7 +254,8 @@ public final class JsonQualifiersTest {
assertThat(v2.b).isEqualTo("bar");
}
@Test public void noJsonAdapterForAnnotatedType() throws Exception {
@Test
public void noJsonAdapterForAnnotatedType() throws Exception {
Moshi moshi = new Moshi.Builder().build();
try {
moshi.adapter(StringAndFooString.class);
@@ -246,10 +264,9 @@ public final class JsonQualifiersTest {
}
}
@Test public void annotationWithoutJsonQualifierIsIgnoredByAdapterMethods() throws Exception {
Moshi moshi = new Moshi.Builder()
.add(new MissingJsonQualifierJsonAdapter())
.build();
@Test
public void annotationWithoutJsonQualifierIsIgnoredByAdapterMethods() throws Exception {
Moshi moshi = new Moshi.Builder().add(new MissingJsonQualifierJsonAdapter()).build();
JsonAdapter<DateAndMillisDate> adapter = moshi.adapter(DateAndMillisDate.class);
DateAndMillisDate v1 = new DateAndMillisDate();
@@ -264,46 +281,55 @@ public final class JsonQualifiersTest {
/** Despite the fact that these methods are annotated, they match all dates. */
static class MissingJsonQualifierJsonAdapter {
@ToJson long dateToJson(@Millis Date d) {
@ToJson
long dateToJson(@Millis Date d) {
return d.getTime();
}
@FromJson @Millis Date jsonToDate(long value) throws Exception {
@FromJson
@Millis
Date jsonToDate(long value) throws Exception {
return new Date(value);
}
}
/** This annotation does nothing. */
@Retention(RUNTIME)
public @interface Millis {
}
public @interface Millis {}
static class DateAndMillisDate {
Date a;
@Millis Date b;
}
@Test public void annotationWithoutJsonQualifierIsRejectedOnRegistration() throws Exception {
JsonAdapter<Date> jsonAdapter = new JsonAdapter<Date>() {
@Override public Date fromJson(JsonReader reader) throws IOException {
throw new AssertionError();
}
@Test
public void annotationWithoutJsonQualifierIsRejectedOnRegistration() throws Exception {
JsonAdapter<Date> jsonAdapter =
new JsonAdapter<Date>() {
@Override
public Date fromJson(JsonReader reader) throws IOException {
throw new AssertionError();
}
@Override public void toJson(JsonWriter writer, Date value) throws IOException {
throw new AssertionError();
}
};
@Override
public void toJson(JsonWriter writer, Date value) throws IOException {
throw new AssertionError();
}
};
try {
new Moshi.Builder().add(Date.class, Millis.class, jsonAdapter);
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("interface com.squareup.moshi.JsonQualifiersTest$Millis "
+ "does not have @JsonQualifier");
assertThat(expected)
.hasMessage(
"interface com.squareup.moshi.JsonQualifiersTest$Millis "
+ "does not have @JsonQualifier");
}
}
@Test public void annotationsConflict() throws Exception {
@Test
public void annotationsConflict() throws Exception {
try {
new Moshi.Builder().add(new AnnotationsConflictJsonAdapter());
fail();
@@ -313,69 +339,84 @@ public final class JsonQualifiersTest {
}
static class AnnotationsConflictJsonAdapter {
@ToJson String fooPrefixStringToString(@FooPrefix String s) {
@ToJson
String fooPrefixStringToString(@FooPrefix String s) {
return "foo" + s;
}
@ToJson String fooPrefixStringToString2(@FooPrefix String s) {
@ToJson
String fooPrefixStringToString2(@FooPrefix String s) {
return "foo" + s;
}
}
@Test public void toButNoFromJson() throws Exception {
@Test
public void toButNoFromJson() throws Exception {
// Building it is okay.
Moshi moshi = new Moshi.Builder()
.add(new ToButNoFromJsonAdapter())
.build();
Moshi moshi = new Moshi.Builder().add(new ToButNoFromJsonAdapter()).build();
try {
moshi.adapter(StringAndFooString.class);
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("No @FromJson adapter for class java.lang.String annotated "
+ "[@com.squareup.moshi.JsonQualifiersTest$FooPrefix()]"
+ "\nfor class java.lang.String b"
+ "\nfor class com.squareup.moshi.JsonQualifiersTest$StringAndFooString");
assertThat(expected)
.hasMessage(
"No @FromJson adapter for class java.lang.String annotated "
+ "[@com.squareup.moshi.JsonQualifiersTest$FooPrefix()]"
+ "\nfor class java.lang.String b"
+ "\nfor class com.squareup.moshi.JsonQualifiersTest$StringAndFooString");
assertThat(expected).hasCauseExactlyInstanceOf(IllegalArgumentException.class);
assertThat(expected.getCause()).hasMessage("No @FromJson adapter for class java.lang.String "
+ "annotated [@com.squareup.moshi.JsonQualifiersTest$FooPrefix()]");
assertThat(expected.getCause())
.hasMessage(
"No @FromJson adapter for class java.lang.String "
+ "annotated [@com.squareup.moshi.JsonQualifiersTest$FooPrefix()]");
assertThat(expected.getCause()).hasCauseExactlyInstanceOf(IllegalArgumentException.class);
assertThat(expected.getCause().getCause()).hasMessage("No next JsonAdapter for class "
+ "java.lang.String annotated [@com.squareup.moshi.JsonQualifiersTest$FooPrefix()]");
assertThat(expected.getCause().getCause())
.hasMessage(
"No next JsonAdapter for class "
+ "java.lang.String annotated [@com.squareup.moshi.JsonQualifiersTest$FooPrefix()]");
}
}
static class ToButNoFromJsonAdapter {
@ToJson String fooPrefixStringToString(@FooPrefix String s) {
@ToJson
String fooPrefixStringToString(@FooPrefix String s) {
return "foo" + s;
}
}
@Test public void fromButNoToJson() throws Exception {
@Test
public void fromButNoToJson() throws Exception {
// Building it is okay.
Moshi moshi = new Moshi.Builder()
.add(new FromButNoToJsonAdapter())
.build();
Moshi moshi = new Moshi.Builder().add(new FromButNoToJsonAdapter()).build();
try {
moshi.adapter(StringAndFooString.class);
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage("No @ToJson adapter for class java.lang.String annotated "
+ "[@com.squareup.moshi.JsonQualifiersTest$FooPrefix()]"
+ "\nfor class java.lang.String b"
+ "\nfor class com.squareup.moshi.JsonQualifiersTest$StringAndFooString");
assertThat(expected)
.hasMessage(
"No @ToJson adapter for class java.lang.String annotated "
+ "[@com.squareup.moshi.JsonQualifiersTest$FooPrefix()]"
+ "\nfor class java.lang.String b"
+ "\nfor class com.squareup.moshi.JsonQualifiersTest$StringAndFooString");
assertThat(expected).hasCauseExactlyInstanceOf(IllegalArgumentException.class);
assertThat(expected.getCause()).hasMessage("No @ToJson adapter for class java.lang.String "
+ "annotated [@com.squareup.moshi.JsonQualifiersTest$FooPrefix()]");
assertThat(expected.getCause())
.hasMessage(
"No @ToJson adapter for class java.lang.String "
+ "annotated [@com.squareup.moshi.JsonQualifiersTest$FooPrefix()]");
assertThat(expected.getCause()).hasCauseExactlyInstanceOf(IllegalArgumentException.class);
assertThat(expected.getCause().getCause()).hasMessage("No next JsonAdapter for class "
+ "java.lang.String annotated [@com.squareup.moshi.JsonQualifiersTest$FooPrefix()]");
assertThat(expected.getCause().getCause())
.hasMessage(
"No next JsonAdapter for class "
+ "java.lang.String annotated [@com.squareup.moshi.JsonQualifiersTest$FooPrefix()]");
}
}
static class FromButNoToJsonAdapter {
@FromJson @FooPrefix String fooPrefixStringFromString(String s) throws Exception {
@FromJson
@FooPrefix
String fooPrefixStringFromString(String s) throws Exception {
if (!s.startsWith("foo")) throw new JsonDataException();
return s.substring(3);
}

View File

@@ -15,6 +15,9 @@
*/
package com.squareup.moshi;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assume.assumeTrue;
import java.io.IOException;
import java.util.List;
import org.junit.Test;
@@ -23,9 +26,6 @@ import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class)
public final class JsonReaderPathTest {
@Parameter public JsonCodecFactory factory;
@@ -36,7 +36,8 @@ public final class JsonReaderPathTest {
}
@SuppressWarnings("CheckReturnValue")
@Test public void path() throws IOException {
@Test
public void path() throws IOException {
JsonReader reader = factory.newReader("{\"a\":[2,true,false,null,\"b\",{\"c\":\"d\"},[3]]}");
assertThat(reader.getPath()).isEqualTo("$");
reader.beginObject();
@@ -75,7 +76,8 @@ public final class JsonReaderPathTest {
assertThat(reader.getPath()).isEqualTo("$");
}
@Test public void arrayOfObjects() throws IOException {
@Test
public void arrayOfObjects() throws IOException {
JsonReader reader = factory.newReader("[{},{},{}]");
reader.beginArray();
assertThat(reader.getPath()).isEqualTo("$[0]");
@@ -95,7 +97,8 @@ public final class JsonReaderPathTest {
assertThat(reader.getPath()).isEqualTo("$");
}
@Test public void arrayOfArrays() throws IOException {
@Test
public void arrayOfArrays() throws IOException {
JsonReader reader = factory.newReader("[[],[],[]]");
reader.beginArray();
assertThat(reader.getPath()).isEqualTo("$[0]");
@@ -116,7 +119,8 @@ public final class JsonReaderPathTest {
}
@SuppressWarnings("CheckReturnValue")
@Test public void objectPath() throws IOException {
@Test
public void objectPath() throws IOException {
JsonReader reader = factory.newReader("{\"a\":1,\"b\":2}");
assertThat(reader.getPath()).isEqualTo("$");
@@ -157,7 +161,8 @@ public final class JsonReaderPathTest {
}
@SuppressWarnings("CheckReturnValue")
@Test public void arrayPath() throws IOException {
@Test
public void arrayPath() throws IOException {
JsonReader reader = factory.newReader("[1,2]");
assertThat(reader.getPath()).isEqualTo("$");
@@ -187,7 +192,8 @@ public final class JsonReaderPathTest {
assertThat(reader.getPath()).isEqualTo("$");
}
@Test public void multipleTopLevelValuesInOneDocument() throws IOException {
@Test
public void multipleTopLevelValuesInOneDocument() throws IOException {
assumeTrue(factory.encodesToBytes());
JsonReader reader = factory.newReader("[][]");
@@ -200,7 +206,8 @@ public final class JsonReaderPathTest {
assertThat(reader.getPath()).isEqualTo("$");
}
@Test public void skipArrayElements() throws IOException {
@Test
public void skipArrayElements() throws IOException {
JsonReader reader = factory.newReader("[1,2,3]");
reader.beginArray();
reader.skipValue();
@@ -208,7 +215,8 @@ public final class JsonReaderPathTest {
assertThat(reader.getPath()).isEqualTo("$[2]");
}
@Test public void skipObjectNames() throws IOException {
@Test
public void skipObjectNames() throws IOException {
JsonReader reader = factory.newReader("{\"a\":1}");
reader.beginObject();
reader.skipValue();
@@ -216,7 +224,8 @@ public final class JsonReaderPathTest {
}
@SuppressWarnings("CheckReturnValue")
@Test public void skipObjectValues() throws IOException {
@Test
public void skipObjectValues() throws IOException {
JsonReader reader = factory.newReader("{\"a\":1,\"b\":2}");
reader.beginObject();
reader.nextName();
@@ -226,7 +235,8 @@ public final class JsonReaderPathTest {
assertThat(reader.getPath()).isEqualTo("$.b");
}
@Test public void skipNestedStructures() throws IOException {
@Test
public void skipNestedStructures() throws IOException {
JsonReader reader = factory.newReader("[[1,2,3],4]");
reader.beginArray();
reader.skipValue();

View File

@@ -15,17 +15,6 @@
*/
package com.squareup.moshi;
import java.io.EOFException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import static com.squareup.moshi.JsonReader.Token.BEGIN_ARRAY;
import static com.squareup.moshi.JsonReader.Token.BEGIN_OBJECT;
import static com.squareup.moshi.JsonReader.Token.NAME;
@@ -37,6 +26,17 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import java.io.EOFException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
@SuppressWarnings("CheckReturnValue")
public final class JsonReaderTest {
@@ -51,7 +51,8 @@ public final class JsonReaderTest {
return factory.newReader(json);
}
@Test public void readArray() throws IOException {
@Test
public void readArray() throws IOException {
JsonReader reader = newReader("[true, true]");
reader.beginArray();
assertThat(reader.nextBoolean()).isTrue();
@@ -60,7 +61,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void readEmptyArray() throws IOException {
@Test
public void readEmptyArray() throws IOException {
JsonReader reader = newReader("[]");
reader.beginArray();
assertThat(reader.hasNext()).isFalse();
@@ -68,7 +70,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void readObject() throws IOException {
@Test
public void readObject() throws IOException {
JsonReader reader = newReader("{\"a\": \"android\", \"b\": \"banana\"}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
@@ -79,7 +82,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void readEmptyObject() throws IOException {
@Test
public void readEmptyObject() throws IOException {
JsonReader reader = newReader("{}");
reader.beginObject();
assertThat(reader.hasNext()).isFalse();
@@ -87,7 +91,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipArray() throws IOException {
@Test
public void skipArray() throws IOException {
JsonReader reader = newReader("{\"a\": [\"one\", \"two\", \"three\"], \"b\": 123}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
@@ -98,7 +103,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipArrayAfterPeek() throws Exception {
@Test
public void skipArrayAfterPeek() throws Exception {
JsonReader reader = newReader("{\"a\": [\"one\", \"two\", \"three\"], \"b\": 123}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
@@ -110,15 +116,17 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipTopLevelObject() throws Exception {
@Test
public void skipTopLevelObject() throws Exception {
JsonReader reader = newReader("{\"a\": [\"one\", \"two\", \"three\"], \"b\": 123}");
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipObject() throws IOException {
JsonReader reader = newReader(
"{\"a\": { \"c\": [], \"d\": [true, true, {}] }, \"b\": \"banana\"}");
@Test
public void skipObject() throws IOException {
JsonReader reader =
newReader("{\"a\": { \"c\": [], \"d\": [true, true, {}] }, \"b\": \"banana\"}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
reader.skipValue();
@@ -128,9 +136,14 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipObjectAfterPeek() throws Exception {
String json = "{" + " \"one\": { \"num\": 1 }"
+ ", \"two\": { \"num\": 2 }" + ", \"three\": { \"num\": 3 }" + "}";
@Test
public void skipObjectAfterPeek() throws Exception {
String json =
"{"
+ " \"one\": { \"num\": 1 }"
+ ", \"two\": { \"num\": 2 }"
+ ", \"three\": { \"num\": 3 }"
+ "}";
JsonReader reader = newReader(json);
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("one");
@@ -145,7 +158,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipInteger() throws IOException {
@Test
public void skipInteger() throws IOException {
JsonReader reader = newReader("{\"a\":123456789,\"b\":-123456789}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
@@ -156,7 +170,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipDouble() throws IOException {
@Test
public void skipDouble() throws IOException {
JsonReader reader = newReader("{\"a\":-123.456e-789,\"b\":123456789.0}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
@@ -167,7 +182,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void failOnUnknownFailsOnUnknownObjectValue() throws IOException {
@Test
public void failOnUnknownFailsOnUnknownObjectValue() throws IOException {
JsonReader reader = newReader("{\"a\": 123}");
reader.setFailOnUnknown(true);
reader.beginObject();
@@ -185,7 +201,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void failOnUnknownFailsOnUnknownArrayElement() throws IOException {
@Test
public void failOnUnknownFailsOnUnknownArrayElement() throws IOException {
JsonReader reader = newReader("[\"a\", 123]");
reader.setFailOnUnknown(true);
reader.beginArray();
@@ -203,11 +220,9 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void helloWorld() throws IOException {
String json = "{\n" +
" \"hello\": true,\n" +
" \"foo\": [\"world\"]\n" +
"}";
@Test
public void helloWorld() throws IOException {
String json = "{\n" + " \"hello\": true,\n" + " \"foo\": [\"world\"]\n" + "}";
JsonReader reader = newReader(json);
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("hello");
@@ -220,7 +235,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void emptyString() throws Exception {
@Test
public void emptyString() throws Exception {
try {
newReader("").beginArray();
fail();
@@ -233,27 +249,29 @@ public final class JsonReaderTest {
}
}
@Test public void characterUnescaping() throws IOException {
String json = "[\"a\","
+ "\"a\\\"\","
+ "\"\\\"\","
+ "\":\","
+ "\",\","
+ "\"\\b\","
+ "\"\\f\","
+ "\"\\n\","
+ "\"\\r\","
+ "\"\\t\","
+ "\" \","
+ "\"\\\\\","
+ "\"{\","
+ "\"}\","
+ "\"[\","
+ "\"]\","
+ "\"\\u0000\","
+ "\"\\u0019\","
+ "\"\\u20AC\""
+ "]";
@Test
public void characterUnescaping() throws IOException {
String json =
"[\"a\","
+ "\"a\\\"\","
+ "\"\\\"\","
+ "\":\","
+ "\",\","
+ "\"\\b\","
+ "\"\\f\","
+ "\"\\n\","
+ "\"\\r\","
+ "\"\\t\","
+ "\" \","
+ "\"\\\\\","
+ "\"{\","
+ "\"}\","
+ "\"[\","
+ "\"]\","
+ "\"\\u0000\","
+ "\"\\u0019\","
+ "\"\\u20AC\""
+ "]";
JsonReader reader = newReader(json);
reader.beginArray();
assertThat(reader.nextString()).isEqualTo("a");
@@ -279,7 +297,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void integersWithFractionalPartSpecified() throws IOException {
@Test
public void integersWithFractionalPartSpecified() throws IOException {
JsonReader reader = newReader("[1.0,1.0,1.0]");
reader.beginArray();
assertThat(reader.nextDouble()).isEqualTo(1.0);
@@ -287,16 +306,18 @@ public final class JsonReaderTest {
assertThat(reader.nextLong()).isEqualTo(1L);
}
@Test public void doubles() throws IOException {
String json = "[-0.0,"
+ "1.0,"
+ "1.7976931348623157E308,"
+ "4.9E-324,"
+ "0.0,"
+ "-0.5,"
+ "2.2250738585072014E-308,"
+ "3.141592653589793,"
+ "2.718281828459045]";
@Test
public void doubles() throws IOException {
String json =
"[-0.0,"
+ "1.0,"
+ "1.7976931348623157E308,"
+ "4.9E-324,"
+ "0.0,"
+ "-0.5,"
+ "2.2250738585072014E-308,"
+ "3.141592653589793,"
+ "2.718281828459045]";
JsonReader reader = newReader(json);
reader.beginArray();
assertThat(reader.nextDouble()).isEqualTo(-0.0);
@@ -312,7 +333,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void strictNonFiniteDoubles() throws IOException {
@Test
public void strictNonFiniteDoubles() throws IOException {
String json = "[NaN]";
JsonReader reader = newReader(json);
reader.beginArray();
@@ -323,7 +345,8 @@ public final class JsonReaderTest {
}
}
@Test public void strictQuotedNonFiniteDoubles() throws IOException {
@Test
public void strictQuotedNonFiniteDoubles() throws IOException {
String json = "[\"NaN\"]";
JsonReader reader = newReader(json);
reader.beginArray();
@@ -335,7 +358,8 @@ public final class JsonReaderTest {
}
}
@Test public void lenientNonFiniteDoubles() throws IOException {
@Test
public void lenientNonFiniteDoubles() throws IOException {
String json = "[NaN, -Infinity, Infinity]";
JsonReader reader = newReader(json);
reader.setLenient(true);
@@ -346,7 +370,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void lenientQuotedNonFiniteDoubles() throws IOException {
@Test
public void lenientQuotedNonFiniteDoubles() throws IOException {
String json = "[\"NaN\", \"-Infinity\", \"Infinity\"]";
JsonReader reader = newReader(json);
reader.setLenient(true);
@@ -357,14 +382,12 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void longs() throws IOException {
@Test
public void longs() throws IOException {
assumeTrue(factory.implementsStrictPrecision());
String json = "[0,0,0,"
+ "1,1,1,"
+ "-1,-1,-1,"
+ "-9223372036854775808,"
+ "9223372036854775807]";
String json =
"[0,0,0," + "1,1,1," + "-1,-1,-1," + "-9223372036854775808," + "9223372036854775807]";
JsonReader reader = newReader(json);
reader.beginArray();
assertThat(reader.nextLong()).isEqualTo(0L);
@@ -392,7 +415,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void booleans() throws IOException {
@Test
public void booleans() throws IOException {
JsonReader reader = newReader("[true,false]");
reader.beginArray();
assertThat(reader.nextBoolean()).isTrue();
@@ -401,7 +425,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void nextFailuresDoNotAdvance() throws IOException {
@Test
public void nextFailuresDoNotAdvance() throws IOException {
JsonReader reader = newReader("{\"a\":true}");
reader.beginObject();
try {
@@ -461,7 +486,8 @@ public final class JsonReaderTest {
reader.close();
}
@Test public void integerMismatchWithDoubleDoesNotAdvance() throws IOException {
@Test
public void integerMismatchWithDoubleDoesNotAdvance() throws IOException {
assumeTrue(factory.implementsStrictPrecision());
JsonReader reader = newReader("[1.5]");
@@ -475,7 +501,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void integerMismatchWithLongDoesNotAdvance() throws IOException {
@Test
public void integerMismatchWithLongDoesNotAdvance() throws IOException {
assumeTrue(factory.implementsStrictPrecision());
JsonReader reader = newReader("[9223372036854775807]");
@@ -489,7 +516,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void longMismatchWithDoubleDoesNotAdvance() throws IOException {
@Test
public void longMismatchWithDoubleDoesNotAdvance() throws IOException {
assumeTrue(factory.implementsStrictPrecision());
JsonReader reader = newReader("[1.5]");
@@ -503,7 +531,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void stringNullIsNotNull() throws IOException {
@Test
public void stringNullIsNotNull() throws IOException {
JsonReader reader = newReader("[\"null\"]");
reader.beginArray();
try {
@@ -513,7 +542,8 @@ public final class JsonReaderTest {
}
}
@Test public void nullLiteralIsNotAString() throws IOException {
@Test
public void nullLiteralIsNotAString() throws IOException {
JsonReader reader = newReader("[null]");
reader.beginArray();
try {
@@ -523,7 +553,8 @@ public final class JsonReaderTest {
}
}
@Test public void topLevelValueTypes() throws IOException {
@Test
public void topLevelValueTypes() throws IOException {
JsonReader reader1 = newReader("true");
assertThat(reader1.nextBoolean()).isTrue();
assertThat(reader1.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
@@ -549,26 +580,31 @@ public final class JsonReaderTest {
assertThat(reader6.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void topLevelValueTypeWithSkipValue() throws IOException {
@Test
public void topLevelValueTypeWithSkipValue() throws IOException {
JsonReader reader = newReader("true");
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void deeplyNestedArrays() throws IOException {
@Test
public void deeplyNestedArrays() throws IOException {
JsonReader reader = newReader("[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]");
for (int i = 0; i < 31; i++) {
reader.beginArray();
}
assertThat(reader.getPath()).isEqualTo("$[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]"
+ "[0][0][0][0][0][0][0][0][0][0][0][0][0]");
assertThat(reader.getPath())
.isEqualTo(
"$[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]"
+ "[0][0][0][0][0][0][0][0][0][0][0][0][0]");
for (int i = 0; i < 31; i++) {
reader.endArray();
}
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void deeplyNestedObjects() throws IOException {
@Test
public void deeplyNestedObjects() throws IOException {
// Build a JSON document structured like {"a":{"a":{"a":{"a":true}}}}, but 31 levels deep.
String array = "{\"a\":%s}";
String json = "true";
@@ -590,7 +626,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipVeryLongUnquotedString() throws IOException {
@Test
public void skipVeryLongUnquotedString() throws IOException {
JsonReader reader = newReader("[" + repeat('x', 8192) + "]");
reader.setLenient(true);
reader.beginArray();
@@ -598,49 +635,56 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void skipTopLevelUnquotedString() throws IOException {
@Test
public void skipTopLevelUnquotedString() throws IOException {
JsonReader reader = newReader(repeat('x', 8192));
reader.setLenient(true);
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipVeryLongQuotedString() throws IOException {
@Test
public void skipVeryLongQuotedString() throws IOException {
JsonReader reader = newReader("[\"" + repeat('x', 8192) + "\"]");
reader.beginArray();
reader.skipValue();
reader.endArray();
}
@Test public void skipTopLevelQuotedString() throws IOException {
@Test
public void skipTopLevelQuotedString() throws IOException {
JsonReader reader = newReader("\"" + repeat('x', 8192) + "\"");
reader.setLenient(true);
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void stringAsNumberWithTruncatedExponent() throws IOException {
@Test
public void stringAsNumberWithTruncatedExponent() throws IOException {
JsonReader reader = newReader("[123e]");
reader.setLenient(true);
reader.beginArray();
assertThat(reader.peek()).isEqualTo(STRING);
}
@Test public void stringAsNumberWithDigitAndNonDigitExponent() throws IOException {
@Test
public void stringAsNumberWithDigitAndNonDigitExponent() throws IOException {
JsonReader reader = newReader("[123e4b]");
reader.setLenient(true);
reader.beginArray();
assertThat(reader.peek()).isEqualTo(STRING);
}
@Test public void stringAsNumberWithNonDigitExponent() throws IOException {
@Test
public void stringAsNumberWithNonDigitExponent() throws IOException {
JsonReader reader = newReader("[123eb]");
reader.setLenient(true);
reader.beginArray();
assertThat(reader.peek()).isEqualTo(STRING);
}
@Test public void emptyStringName() throws IOException {
@Test
public void emptyStringName() throws IOException {
JsonReader reader = newReader("{\"\":true}");
reader.setLenient(true);
assertThat(reader.peek()).isEqualTo(BEGIN_OBJECT);
@@ -654,13 +698,15 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void validEscapes() throws IOException {
@Test
public void validEscapes() throws IOException {
JsonReader reader = newReader("[\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"]");
reader.beginArray();
assertThat(reader.nextString()).isEqualTo("\"\\/\b\f\n\r\t");
}
@Test public void selectName() throws IOException {
@Test
public void selectName() throws IOException {
JsonReader.Options abc = JsonReader.Options.of("a", "b", "c");
JsonReader reader = newReader("{\"a\": 5, \"b\": 5, \"c\": 5, \"d\": 5}");
@@ -696,7 +742,8 @@ public final class JsonReaderTest {
}
/** Select does match necessarily escaping. The decoded value is used in the path. */
@Test public void selectNameNecessaryEscaping() throws IOException {
@Test
public void selectNameNecessaryEscaping() throws IOException {
JsonReader.Options options = JsonReader.Options.of("\n", "\u0000", "\"");
JsonReader reader = newReader("{\"\\n\": 5,\"\\u0000\": 5, \"\\\"\": 5}");
@@ -714,7 +761,8 @@ public final class JsonReaderTest {
}
/** Select removes unnecessary escaping from the source JSON. */
@Test public void selectNameUnnecessaryEscaping() throws IOException {
@Test
public void selectNameUnnecessaryEscaping() throws IOException {
JsonReader.Options options = JsonReader.Options.of("coffee", "tea");
JsonReader reader = newReader("{\"cof\\u0066ee\":5, \"\\u0074e\\u0061\":4, \"water\":3}");
@@ -737,7 +785,8 @@ public final class JsonReaderTest {
reader.endObject();
}
@Test public void selectNameUnquoted() throws Exception {
@Test
public void selectNameUnquoted() throws Exception {
JsonReader.Options options = JsonReader.Options.of("a", "b");
JsonReader reader = newReader("{a:2}");
@@ -752,7 +801,8 @@ public final class JsonReaderTest {
reader.endObject();
}
@Test public void selectNameSingleQuoted() throws IOException {
@Test
public void selectNameSingleQuoted() throws IOException {
JsonReader.Options abc = JsonReader.Options.of("a", "b");
JsonReader reader = newReader("{'a':5}");
@@ -767,7 +817,8 @@ public final class JsonReaderTest {
reader.endObject();
}
@Test public void selectString() throws IOException {
@Test
public void selectString() throws IOException {
JsonReader.Options abc = JsonReader.Options.of("a", "b", "c");
JsonReader reader = newReader("[\"a\", \"b\", \"c\", \"d\"]");
@@ -794,7 +845,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void selectStringNecessaryEscaping() throws Exception {
@Test
public void selectStringNecessaryEscaping() throws Exception {
JsonReader.Options options = JsonReader.Options.of("\n", "\u0000", "\"");
JsonReader reader = newReader("[\"\\n\",\"\\u0000\", \"\\\"\"]");
@@ -806,7 +858,8 @@ public final class JsonReaderTest {
}
/** Select strips unnecessarily-escaped strings. */
@Test public void selectStringUnnecessaryEscaping() throws IOException {
@Test
public void selectStringUnnecessaryEscaping() throws IOException {
JsonReader.Options abc = JsonReader.Options.of("a", "b", "c");
JsonReader reader = newReader("[\"\\u0061\", \"b\", \"\\u0063\"]");
@@ -817,7 +870,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void selectStringUnquoted() throws IOException {
@Test
public void selectStringUnquoted() throws IOException {
JsonReader.Options abc = JsonReader.Options.of("a", "b", "c");
JsonReader reader = newReader("[a, \"b\", c]");
@@ -829,7 +883,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void selectStringSingleQuoted() throws IOException {
@Test
public void selectStringSingleQuoted() throws IOException {
JsonReader.Options abc = JsonReader.Options.of("a", "b", "c");
JsonReader reader = newReader("['a', \"b\", c]");
@@ -841,7 +896,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void selectStringMaintainsReaderState() throws IOException {
@Test
public void selectStringMaintainsReaderState() throws IOException {
JsonReader.Options abc = JsonReader.Options.of("a", "b", "c");
JsonReader reader = newReader("[\"\\u0061\", \"42\"]");
@@ -854,7 +910,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void selectStringWithoutString() throws IOException {
@Test
public void selectStringWithoutString() throws IOException {
JsonReader.Options numbers = JsonReader.Options.of("1", "2.0", "true", "4");
JsonReader reader = newReader("[0, 2.0, true, \"4\"]");
@@ -869,7 +926,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void stringToNumberCoersion() throws Exception {
@Test
public void stringToNumberCoersion() throws Exception {
JsonReader reader = newReader("[\"0\", \"9223372036854775807\", \"1.5\"]");
reader.beginArray();
assertThat(reader.nextInt()).isEqualTo(0);
@@ -878,7 +936,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void unnecessaryPrecisionNumberCoersion() throws Exception {
@Test
public void unnecessaryPrecisionNumberCoersion() throws Exception {
JsonReader reader = newReader("[\"0.0\", \"9223372036854775807.0\"]");
reader.beginArray();
assertThat(reader.nextInt()).isEqualTo(0);
@@ -886,7 +945,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void nanInfinityDoubleCoersion() throws Exception {
@Test
public void nanInfinityDoubleCoersion() throws Exception {
JsonReader reader = newReader("[\"NaN\", \"Infinity\", \"-Infinity\"]");
reader.beginArray();
reader.setLenient(true);
@@ -896,7 +956,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void intMismatchWithStringDoesNotAdvance() throws Exception {
@Test
public void intMismatchWithStringDoesNotAdvance() throws Exception {
JsonReader reader = newReader("[\"a\"]");
reader.beginArray();
try {
@@ -908,7 +969,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void longMismatchWithStringDoesNotAdvance() throws Exception {
@Test
public void longMismatchWithStringDoesNotAdvance() throws Exception {
JsonReader reader = newReader("[\"a\"]");
reader.beginArray();
try {
@@ -920,7 +982,8 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void doubleMismatchWithStringDoesNotAdvance() throws Exception {
@Test
public void doubleMismatchWithStringDoesNotAdvance() throws Exception {
JsonReader reader = newReader("[\"a\"]");
reader.beginArray();
try {
@@ -932,38 +995,44 @@ public final class JsonReaderTest {
reader.endArray();
}
@Test public void readJsonValueInt() throws IOException {
@Test
public void readJsonValueInt() throws IOException {
JsonReader reader = newReader("1");
Object value = reader.readJsonValue();
assertThat(value).isEqualTo(1.0);
}
@Test public void readJsonValueMap() throws IOException {
@Test
public void readJsonValueMap() throws IOException {
JsonReader reader = newReader("{\"hello\": \"world\"}");
Object value = reader.readJsonValue();
assertThat(value).isEqualTo(Collections.singletonMap("hello", "world"));
}
@Test public void readJsonValueList() throws IOException {
@Test
public void readJsonValueList() throws IOException {
JsonReader reader = newReader("[\"a\", \"b\"]");
Object value = reader.readJsonValue();
assertThat(value).isEqualTo(Arrays.asList("a", "b"));
}
@Test public void readJsonValueListMultipleTypes() throws IOException {
@Test
public void readJsonValueListMultipleTypes() throws IOException {
JsonReader reader = newReader("[\"a\", 5, false]");
Object value = reader.readJsonValue();
assertThat(value).isEqualTo(Arrays.asList("a", 5.0, false));
}
@Test public void readJsonValueNestedListInMap() throws IOException {
@Test
public void readJsonValueNestedListInMap() throws IOException {
JsonReader reader = newReader("{\"pizzas\": [\"cheese\", \"pepperoni\"]}");
Object value = reader.readJsonValue();
assertThat(value).isEqualTo(
Collections.singletonMap("pizzas", Arrays.asList("cheese", "pepperoni")));
assertThat(value)
.isEqualTo(Collections.singletonMap("pizzas", Arrays.asList("cheese", "pepperoni")));
}
@Test public void skipName() throws IOException {
@Test
public void skipName() throws IOException {
JsonReader reader = newReader("{\"a\":1}");
reader.beginObject();
reader.skipName();
@@ -972,7 +1041,8 @@ public final class JsonReaderTest {
reader.endObject();
}
@Test public void skipNameFailUnknown() throws IOException {
@Test
public void skipNameFailUnknown() throws IOException {
JsonReader reader = newReader("{\"a\":1,\"b\":2}");
reader.setFailOnUnknown(true);
reader.beginObject();
@@ -986,7 +1056,8 @@ public final class JsonReaderTest {
}
}
@Test public void skipNameOnValueFails() throws IOException {
@Test
public void skipNameOnValueFails() throws IOException {
JsonReader reader = newReader("1");
try {
reader.skipName();
@@ -996,13 +1067,15 @@ public final class JsonReaderTest {
assertThat(reader.nextInt()).isEqualTo(1);
}
@Test public void emptyDocumentHasNextReturnsFalse() throws IOException {
@Test
public void emptyDocumentHasNextReturnsFalse() throws IOException {
JsonReader reader = newReader("1");
reader.readJsonValue();
assertThat(reader.hasNext()).isFalse();
}
@Test public void skipValueAtEndOfObjectFails() throws IOException {
@Test
public void skipValueAtEndOfObjectFails() throws IOException {
JsonReader reader = newReader("{}");
reader.beginObject();
try {
@@ -1015,7 +1088,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipValueAtEndOfArrayFails() throws IOException {
@Test
public void skipValueAtEndOfArrayFails() throws IOException {
JsonReader reader = newReader("[]");
reader.beginArray();
try {
@@ -1028,7 +1102,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipValueAtEndOfDocumentFails() throws IOException {
@Test
public void skipValueAtEndOfDocumentFails() throws IOException {
JsonReader reader = newReader("1");
reader.nextInt();
try {
@@ -1040,7 +1115,8 @@ public final class JsonReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void basicPeekJson() throws IOException {
@Test
public void basicPeekJson() throws IOException {
JsonReader reader = newReader("{\"a\":12,\"b\":[34,56],\"c\":78}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
@@ -1072,7 +1148,8 @@ public final class JsonReaderTest {
* reader. Before each of the real readers operations we create a peeking reader and let it read
* the rest of the document.
*/
@Test public void peekJsonReader() throws IOException {
@Test
public void peekJsonReader() throws IOException {
JsonReader reader = newReader("[12,34,{\"a\":56,\"b\":78},90]");
for (int i = 0; i < 12; i++) {
readPeek12Steps(reader.peekJson(), i, 12);
@@ -1138,15 +1215,17 @@ public final class JsonReaderTest {
}
/** Confirm that we can peek in every state of the UTF-8 reader. */
@Test public void peekAfterPeek() throws IOException {
JsonReader reader = newReader(
"[{\"a\":\"aaa\",'b':'bbb',c:c,\"d\":\"d\"},true,false,null,1,2.0]");
@Test
public void peekAfterPeek() throws IOException {
JsonReader reader =
newReader("[{\"a\":\"aaa\",'b':'bbb',c:c,\"d\":\"d\"},true,false,null,1,2.0]");
reader.setLenient(true);
readValue(reader, true);
reader.peekJson();
}
@Test public void peekAfterPromoteNameToValue() throws IOException {
@Test
public void peekAfterPromoteNameToValue() throws IOException {
JsonReader reader = newReader("{\"a\":\"b\"}");
reader.beginObject();
reader.promoteNameToValue();
@@ -1157,7 +1236,8 @@ public final class JsonReaderTest {
reader.endObject();
}
@Test public void promoteStringNameToValue() throws IOException {
@Test
public void promoteStringNameToValue() throws IOException {
JsonReader reader = newReader("{\"a\":\"b\"}");
reader.beginObject();
reader.promoteNameToValue();
@@ -1166,7 +1246,8 @@ public final class JsonReaderTest {
reader.endObject();
}
@Test public void promoteDoubleNameToValue() throws IOException {
@Test
public void promoteDoubleNameToValue() throws IOException {
JsonReader reader = newReader("{\"5\":\"b\"}");
reader.beginObject();
reader.promoteNameToValue();
@@ -1175,7 +1256,8 @@ public final class JsonReaderTest {
reader.endObject();
}
@Test public void promoteLongNameToValue() throws IOException {
@Test
public void promoteLongNameToValue() throws IOException {
JsonReader reader = newReader("{\"5\":\"b\"}");
reader.beginObject();
reader.promoteNameToValue();
@@ -1184,7 +1266,8 @@ public final class JsonReaderTest {
reader.endObject();
}
@Test public void promoteNullNameToValue() throws IOException {
@Test
public void promoteNullNameToValue() throws IOException {
JsonReader reader = newReader("{\"null\":\"b\"}");
reader.beginObject();
reader.promoteNameToValue();
@@ -1196,7 +1279,8 @@ public final class JsonReaderTest {
assertEquals("null", reader.nextString());
}
@Test public void promoteBooleanNameToValue() throws IOException {
@Test
public void promoteBooleanNameToValue() throws IOException {
JsonReader reader = newReader("{\"true\":\"b\"}");
reader.beginObject();
reader.promoteNameToValue();
@@ -1208,7 +1292,8 @@ public final class JsonReaderTest {
assertEquals("true", reader.nextString());
}
@Test public void promoteBooleanNameToValueCannotBeReadAsName() throws IOException {
@Test
public void promoteBooleanNameToValueCannotBeReadAsName() throws IOException {
JsonReader reader = newReader("{\"true\":\"b\"}");
reader.beginObject();
reader.promoteNameToValue();
@@ -1220,7 +1305,8 @@ public final class JsonReaderTest {
assertEquals("true", reader.nextString());
}
@Test public void promoteSkippedNameToValue() throws IOException {
@Test
public void promoteSkippedNameToValue() throws IOException {
JsonReader reader = newReader("{\"true\":\"b\"}");
reader.beginObject();
reader.promoteNameToValue();
@@ -1228,7 +1314,8 @@ public final class JsonReaderTest {
assertEquals("b", reader.nextString());
}
@Test public void promoteNameToValueAtEndOfObject() throws IOException {
@Test
public void promoteNameToValueAtEndOfObject() throws IOException {
JsonReader reader = newReader("{}");
reader.beginObject();
reader.promoteNameToValue();
@@ -1236,8 +1323,9 @@ public final class JsonReaderTest {
reader.endObject();
}
@Test public void optionsStrings() {
String[] options = new String[] { "a", "b", "c" };
@Test
public void optionsStrings() {
String[] options = new String[] {"a", "b", "c"};
JsonReader.Options abc = JsonReader.Options.of("a", "b", "c");
List<String> strings = abc.strings();
assertThat(options).containsExactlyElementsOf(strings);

View File

@@ -15,15 +15,6 @@
*/
package com.squareup.moshi;
import java.io.EOFException;
import java.io.IOException;
import java.util.Arrays;
import okio.Buffer;
import okio.ForwardingSource;
import okio.Okio;
import org.junit.Ignore;
import org.junit.Test;
import static com.squareup.moshi.JsonReader.Token.BEGIN_ARRAY;
import static com.squareup.moshi.JsonReader.Token.BEGIN_OBJECT;
import static com.squareup.moshi.JsonReader.Token.BOOLEAN;
@@ -40,8 +31,18 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.EOFException;
import java.io.IOException;
import java.util.Arrays;
import okio.Buffer;
import okio.ForwardingSource;
import okio.Okio;
import org.junit.Ignore;
import org.junit.Test;
public final class JsonUtf8ReaderTest {
@Test public void readingDoesNotBuffer() throws IOException {
@Test
public void readingDoesNotBuffer() throws IOException {
Buffer buffer = new Buffer().writeUtf8("{}{}");
JsonReader reader1 = JsonReader.of(buffer);
@@ -55,7 +56,8 @@ public final class JsonUtf8ReaderTest {
assertThat(buffer.size()).isEqualTo(0);
}
@Test public void readObjectBuffer() throws IOException {
@Test
public void readObjectBuffer() throws IOException {
Buffer buffer = new Buffer().writeUtf8("{\"a\": \"android\", \"b\": \"banana\"}");
JsonReader reader = JsonReader.of(buffer);
reader.beginObject();
@@ -67,7 +69,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void readObjectSource() throws IOException {
@Test
public void readObjectSource() throws IOException {
Buffer buffer = new Buffer().writeUtf8("{\"a\": \"android\", \"b\": \"banana\"}");
JsonReader reader = JsonReader.of(Okio.buffer(new ForwardingSource(buffer) {}));
reader.beginObject();
@@ -79,7 +82,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void nullSource() {
@Test
public void nullSource() {
try {
JsonReader.of(null);
fail();
@@ -87,7 +91,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void unescapingInvalidCharacters() throws IOException {
@Test
public void unescapingInvalidCharacters() throws IOException {
String json = "[\"\\u000g\"]";
JsonReader reader = newReader(json);
reader.beginArray();
@@ -98,7 +103,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void unescapingTruncatedCharacters() throws IOException {
@Test
public void unescapingTruncatedCharacters() throws IOException {
String json = "[\"\\u000";
JsonReader reader = newReader(json);
reader.beginArray();
@@ -109,7 +115,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void unescapingTruncatedSequence() throws IOException {
@Test
public void unescapingTruncatedSequence() throws IOException {
String json = "[\"\\";
JsonReader reader = newReader(json);
reader.beginArray();
@@ -120,7 +127,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void strictNonFiniteDoublesWithSkipValue() throws IOException {
@Test
public void strictNonFiniteDoublesWithSkipValue() throws IOException {
String json = "[NaN]";
JsonReader reader = newReader(json);
reader.beginArray();
@@ -131,7 +139,9 @@ public final class JsonUtf8ReaderTest {
}
}
@Test @Ignore public void numberWithOctalPrefix() throws IOException {
@Test
@Ignore
public void numberWithOctalPrefix() throws IOException {
String json = "[01]";
JsonReader reader = newReader(json);
reader.beginArray();
@@ -160,7 +170,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void peekingUnquotedStringsPrefixedWithBooleans() throws IOException {
@Test
public void peekingUnquotedStringsPrefixedWithBooleans() throws IOException {
JsonReader reader = newReader("[truey]");
reader.setLenient(true);
reader.beginArray();
@@ -174,7 +185,8 @@ public final class JsonUtf8ReaderTest {
reader.endArray();
}
@Test public void malformedNumbers() throws IOException {
@Test
public void malformedNumbers() throws IOException {
assertNotANumber("-");
assertNotANumber(".");
@@ -220,7 +232,8 @@ public final class JsonUtf8ReaderTest {
reader.endArray();
}
@Test public void peekingUnquotedStringsPrefixedWithIntegers() throws IOException {
@Test
public void peekingUnquotedStringsPrefixedWithIntegers() throws IOException {
JsonReader reader = newReader("[12.34e5x]");
reader.setLenient(true);
reader.beginArray();
@@ -233,7 +246,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.nextString()).isEqualTo("12.34e5x");
}
@Test public void peekLongMinValue() throws IOException {
@Test
public void peekLongMinValue() throws IOException {
JsonReader reader = newReader("[-9223372036854775808]");
reader.setLenient(true);
reader.beginArray();
@@ -241,7 +255,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.nextLong()).isEqualTo(-9223372036854775808L);
}
@Test public void peekLongMaxValue() throws IOException {
@Test
public void peekLongMaxValue() throws IOException {
JsonReader reader = newReader("[9223372036854775807]");
reader.setLenient(true);
reader.beginArray();
@@ -249,7 +264,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.nextLong()).isEqualTo(9223372036854775807L);
}
@Test public void longLargerThanMaxLongThatWrapsAround() throws IOException {
@Test
public void longLargerThanMaxLongThatWrapsAround() throws IOException {
JsonReader reader = newReader("[22233720368547758070]");
reader.setLenient(true);
reader.beginArray();
@@ -261,7 +277,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void longLargerThanMinLongThatWrapsAround() throws IOException {
@Test
public void longLargerThanMinLongThatWrapsAround() throws IOException {
JsonReader reader = newReader("[-22233720368547758070]");
reader.setLenient(true);
reader.beginArray();
@@ -273,7 +290,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void peekLargerThanLongMaxValue() throws IOException {
@Test
public void peekLargerThanLongMaxValue() throws IOException {
JsonReader reader = newReader("[9223372036854775808]");
reader.setLenient(true);
reader.beginArray();
@@ -285,7 +303,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void precisionNotDiscarded() throws IOException {
@Test
public void precisionNotDiscarded() throws IOException {
JsonReader reader = newReader("[9223372036854775806.5]");
reader.setLenient(true);
reader.beginArray();
@@ -297,7 +316,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void peekLargerThanLongMinValue() throws IOException {
@Test
public void peekLargerThanLongMinValue() throws IOException {
JsonReader reader = newReader("[-9223372036854775809]");
reader.setLenient(true);
reader.beginArray();
@@ -310,7 +330,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.nextDouble()).isEqualTo(-9223372036854775809d);
}
@Test public void highPrecisionLong() throws IOException {
@Test
public void highPrecisionLong() throws IOException {
String json = "[9223372036854775806.000]";
JsonReader reader = newReader(json);
reader.beginArray();
@@ -318,7 +339,8 @@ public final class JsonUtf8ReaderTest {
reader.endArray();
}
@Test public void peekMuchLargerThanLongMinValue() throws IOException {
@Test
public void peekMuchLargerThanLongMinValue() throws IOException {
JsonReader reader = newReader("[-92233720368547758080]");
reader.setLenient(true);
reader.beginArray();
@@ -331,13 +353,15 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.nextDouble()).isEqualTo(-92233720368547758080d);
}
@Test public void negativeZeroIsANumber() throws Exception {
@Test
public void negativeZeroIsANumber() throws Exception {
JsonReader reader = newReader("-0");
assertEquals(NUMBER, reader.peek());
assertEquals("-0", reader.nextString());
}
@Test public void numberToStringCoersion() throws Exception {
@Test
public void numberToStringCoersion() throws Exception {
JsonReader reader = newReader("[0, 9223372036854775807, 2.5, 3.010, \"a\", \"5\"]");
reader.beginArray();
assertThat(reader.nextString()).isEqualTo("0");
@@ -349,7 +373,8 @@ public final class JsonUtf8ReaderTest {
reader.endArray();
}
@Test public void quotedNumberWithEscape() throws IOException {
@Test
public void quotedNumberWithEscape() throws IOException {
JsonReader reader = newReader("[\"12\u00334\"]");
reader.setLenient(true);
reader.beginArray();
@@ -357,7 +382,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.nextInt()).isEqualTo(1234);
}
@Test public void mixedCaseLiterals() throws IOException {
@Test
public void mixedCaseLiterals() throws IOException {
JsonReader reader = newReader("[True,TruE,False,FALSE,NULL,nulL]");
reader.beginArray();
assertThat(reader.nextBoolean()).isTrue();
@@ -370,7 +396,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void missingValue() throws IOException {
@Test
public void missingValue() throws IOException {
JsonReader reader = newReader("{\"a\":}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
@@ -381,7 +408,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void prematureEndOfInput() throws IOException {
@Test
public void prematureEndOfInput() throws IOException {
JsonReader reader = newReader("{\"a\":true,");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
@@ -394,7 +422,8 @@ public final class JsonUtf8ReaderTest {
}
@SuppressWarnings("CheckReturnValue")
@Test public void prematurelyClosed() throws IOException {
@Test
public void prematurelyClosed() throws IOException {
try {
JsonReader reader = newReader("{\"a\":[]}");
reader.beginObject();
@@ -424,7 +453,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void strictNameValueSeparator() throws IOException {
@Test
public void strictNameValueSeparator() throws IOException {
JsonReader reader = newReader("{\"a\"=true}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
@@ -444,7 +474,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void lenientNameValueSeparator() throws IOException {
@Test
public void lenientNameValueSeparator() throws IOException {
JsonReader reader = newReader("{\"a\"=true}");
reader.setLenient(true);
reader.beginObject();
@@ -458,7 +489,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.nextBoolean()).isTrue();
}
@Test public void strictNameValueSeparatorWithSkipValue() throws IOException {
@Test
public void strictNameValueSeparatorWithSkipValue() throws IOException {
JsonReader reader = newReader("{\"a\"=true}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
@@ -478,7 +510,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void commentsInStringValue() throws Exception {
@Test
public void commentsInStringValue() throws Exception {
JsonReader reader = newReader("[\"// comment\"]");
reader.beginArray();
assertThat(reader.nextString()).isEqualTo("// comment");
@@ -497,7 +530,8 @@ public final class JsonUtf8ReaderTest {
reader.endObject();
}
@Test public void strictComments() throws IOException {
@Test
public void strictComments() throws IOException {
JsonReader reader = newReader("[// comment \n true]");
reader.beginArray();
try {
@@ -523,7 +557,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void lenientComments() throws IOException {
@Test
public void lenientComments() throws IOException {
JsonReader reader = newReader("[// comment \n true]");
reader.setLenient(true);
reader.beginArray();
@@ -545,7 +580,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void strictCommentsWithSkipValue() throws IOException {
@Test
public void strictCommentsWithSkipValue() throws IOException {
JsonReader reader = newReader("[// comment \n true]");
reader.beginArray();
try {
@@ -571,7 +607,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void strictUnquotedNames() throws IOException {
@Test
public void strictUnquotedNames() throws IOException {
JsonReader reader = newReader("{a:true}");
reader.beginObject();
try {
@@ -581,21 +618,24 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void lenientUnquotedNames() throws IOException {
@Test
public void lenientUnquotedNames() throws IOException {
JsonReader reader = newReader("{a:true}");
reader.setLenient(true);
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
}
@Test public void jsonIsSingleUnquotedString() throws IOException {
@Test
public void jsonIsSingleUnquotedString() throws IOException {
JsonReader reader = newReader("abc");
reader.setLenient(true);
assertThat(reader.nextString()).isEqualTo("abc");
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void strictUnquotedNamesWithSkipValue() throws IOException {
@Test
public void strictUnquotedNamesWithSkipValue() throws IOException {
JsonReader reader = newReader("{a:true}");
reader.beginObject();
try {
@@ -605,7 +645,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void strictSingleQuotedNames() throws IOException {
@Test
public void strictSingleQuotedNames() throws IOException {
JsonReader reader = newReader("{'a':true}");
reader.beginObject();
try {
@@ -615,14 +656,16 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void lenientSingleQuotedNames() throws IOException {
@Test
public void lenientSingleQuotedNames() throws IOException {
JsonReader reader = newReader("{'a':true}");
reader.setLenient(true);
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
}
@Test public void strictSingleQuotedNamesWithSkipValue() throws IOException {
@Test
public void strictSingleQuotedNamesWithSkipValue() throws IOException {
JsonReader reader = newReader("{'a':true}");
reader.beginObject();
try {
@@ -632,7 +675,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void strictUnquotedStrings() throws IOException {
@Test
public void strictUnquotedStrings() throws IOException {
JsonReader reader = newReader("[a]");
reader.beginArray();
try {
@@ -642,7 +686,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void strictUnquotedStringsWithSkipValue() throws IOException {
@Test
public void strictUnquotedStringsWithSkipValue() throws IOException {
JsonReader reader = newReader("[a]");
reader.beginArray();
try {
@@ -652,14 +697,16 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void lenientUnquotedStrings() throws IOException {
@Test
public void lenientUnquotedStrings() throws IOException {
JsonReader reader = newReader("[a]");
reader.setLenient(true);
reader.beginArray();
assertThat(reader.nextString()).isEqualTo("a");
}
@Test public void lenientUnquotedStringsDelimitedByComment() throws IOException {
@Test
public void lenientUnquotedStringsDelimitedByComment() throws IOException {
JsonReader reader = newReader("[a#comment\n]");
reader.setLenient(true);
reader.beginArray();
@@ -667,7 +714,8 @@ public final class JsonUtf8ReaderTest {
reader.endArray();
}
@Test public void strictSingleQuotedStrings() throws IOException {
@Test
public void strictSingleQuotedStrings() throws IOException {
JsonReader reader = newReader("['a']");
reader.beginArray();
try {
@@ -677,14 +725,16 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void lenientSingleQuotedStrings() throws IOException {
@Test
public void lenientSingleQuotedStrings() throws IOException {
JsonReader reader = newReader("['a']");
reader.setLenient(true);
reader.beginArray();
assertThat(reader.nextString()).isEqualTo("a");
}
@Test public void strictSingleQuotedStringsWithSkipValue() throws IOException {
@Test
public void strictSingleQuotedStringsWithSkipValue() throws IOException {
JsonReader reader = newReader("['a']");
reader.beginArray();
try {
@@ -694,7 +744,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void strictSemicolonDelimitedArray() throws IOException {
@Test
public void strictSemicolonDelimitedArray() throws IOException {
JsonReader reader = newReader("[true;true]");
reader.beginArray();
try {
@@ -705,7 +756,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void lenientSemicolonDelimitedArray() throws IOException {
@Test
public void lenientSemicolonDelimitedArray() throws IOException {
JsonReader reader = newReader("[true;true]");
reader.setLenient(true);
reader.beginArray();
@@ -713,7 +765,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.nextBoolean()).isTrue();
}
@Test public void strictSemicolonDelimitedArrayWithSkipValue() throws IOException {
@Test
public void strictSemicolonDelimitedArrayWithSkipValue() throws IOException {
JsonReader reader = newReader("[true;true]");
reader.beginArray();
try {
@@ -724,7 +777,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void strictSemicolonDelimitedNameValuePair() throws IOException {
@Test
public void strictSemicolonDelimitedNameValuePair() throws IOException {
JsonReader reader = newReader("{\"a\":true;\"b\":true}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
@@ -736,7 +790,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void lenientSemicolonDelimitedNameValuePair() throws IOException {
@Test
public void lenientSemicolonDelimitedNameValuePair() throws IOException {
JsonReader reader = newReader("{\"a\":true;\"b\":true}");
reader.setLenient(true);
reader.beginObject();
@@ -745,7 +800,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.nextName()).isEqualTo("b");
}
@Test public void strictSemicolonDelimitedNameValuePairWithSkipValue() throws IOException {
@Test
public void strictSemicolonDelimitedNameValuePairWithSkipValue() throws IOException {
JsonReader reader = newReader("{\"a\":true;\"b\":true}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
@@ -757,7 +813,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void strictUnnecessaryArraySeparators() throws IOException {
@Test
public void strictUnnecessaryArraySeparators() throws IOException {
JsonReader reader = newReader("[true,,true]");
reader.beginArray();
assertThat(reader.nextBoolean()).isTrue();
@@ -793,7 +850,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void lenientUnnecessaryArraySeparators() throws IOException {
@Test
public void lenientUnnecessaryArraySeparators() throws IOException {
JsonReader reader = newReader("[true,,true]");
reader.setLenient(true);
reader.beginArray();
@@ -824,7 +882,8 @@ public final class JsonUtf8ReaderTest {
reader.endArray();
}
@Test public void strictUnnecessaryArraySeparatorsWithSkipValue() throws IOException {
@Test
public void strictUnnecessaryArraySeparatorsWithSkipValue() throws IOException {
JsonReader reader = newReader("[true,,true]");
reader.beginArray();
assertThat(reader.nextBoolean()).isTrue();
@@ -860,7 +919,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void strictMultipleTopLevelValues() throws IOException {
@Test
public void strictMultipleTopLevelValues() throws IOException {
JsonReader reader = newReader("[] []");
reader.beginArray();
reader.endArray();
@@ -871,7 +931,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void lenientMultipleTopLevelValues() throws IOException {
@Test
public void lenientMultipleTopLevelValues() throws IOException {
JsonReader reader = newReader("[] true {}");
reader.setLenient(true);
reader.beginArray();
@@ -882,7 +943,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void strictMultipleTopLevelValuesWithSkipValue() throws IOException {
@Test
public void strictMultipleTopLevelValuesWithSkipValue() throws IOException {
JsonReader reader = newReader("[] []");
reader.beginArray();
reader.endArray();
@@ -893,13 +955,16 @@ public final class JsonUtf8ReaderTest {
}
}
@Test @Ignore public void bomIgnoredAsFirstCharacterOfDocument() throws IOException {
@Test
@Ignore
public void bomIgnoredAsFirstCharacterOfDocument() throws IOException {
JsonReader reader = newReader("\ufeff[]");
reader.beginArray();
reader.endArray();
}
@Test public void bomForbiddenAsOtherCharacterInDocument() throws IOException {
@Test
public void bomForbiddenAsOtherCharacterInDocument() throws IOException {
JsonReader reader = newReader("[\ufeff]");
reader.beginArray();
try {
@@ -909,46 +974,50 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void failWithPosition() throws IOException {
testFailWithPosition("Expected value at path $[1]",
"[\n\n\n\n\n\"a\",}]");
@Test
public void failWithPosition() throws IOException {
testFailWithPosition("Expected value at path $[1]", "[\n\n\n\n\n\"a\",}]");
}
@Test public void failWithPositionGreaterThanBufferSize() throws IOException {
@Test
public void failWithPositionGreaterThanBufferSize() throws IOException {
String spaces = repeat(' ', 8192);
testFailWithPosition("Expected value at path $[1]",
"[\n\n" + spaces + "\n\n\n\"a\",}]");
testFailWithPosition("Expected value at path $[1]", "[\n\n" + spaces + "\n\n\n\"a\",}]");
}
@Test public void failWithPositionOverSlashSlashEndOfLineComment() throws IOException {
testFailWithPosition("Expected value at path $[1]",
"\n// foo\n\n//bar\r\n[\"a\",}");
@Test
public void failWithPositionOverSlashSlashEndOfLineComment() throws IOException {
testFailWithPosition("Expected value at path $[1]", "\n// foo\n\n//bar\r\n[\"a\",}");
}
@Test public void failWithPositionOverHashEndOfLineComment() throws IOException {
testFailWithPosition("Expected value at path $[1]",
"\n# foo\n\n#bar\r\n[\"a\",}");
@Test
public void failWithPositionOverHashEndOfLineComment() throws IOException {
testFailWithPosition("Expected value at path $[1]", "\n# foo\n\n#bar\r\n[\"a\",}");
}
@Test public void failWithPositionOverCStyleComment() throws IOException {
testFailWithPosition("Expected value at path $[1]",
"\n\n/* foo\n*\n*\r\nbar */[\"a\",}");
@Test
public void failWithPositionOverCStyleComment() throws IOException {
testFailWithPosition("Expected value at path $[1]", "\n\n/* foo\n*\n*\r\nbar */[\"a\",}");
}
@Test public void failWithPositionOverQuotedString() throws IOException {
testFailWithPosition("Expected value at path $[1]",
"[\"foo\nbar\r\nbaz\n\",\n }");
@Test
public void failWithPositionOverQuotedString() throws IOException {
testFailWithPosition("Expected value at path $[1]", "[\"foo\nbar\r\nbaz\n\",\n }");
}
@Test public void failWithPositionOverUnquotedString() throws IOException {
@Test
public void failWithPositionOverUnquotedString() throws IOException {
testFailWithPosition("Expected value at path $[1]", "[\n\nabcd\n\n,}");
}
@Test public void failWithEscapedNewlineCharacter() throws IOException {
@Test
public void failWithEscapedNewlineCharacter() throws IOException {
testFailWithPosition("Expected value at path $[1]", "[\n\n\"\\\n\n\",}");
}
@Test @Ignore public void failWithPositionIsOffsetByBom() throws IOException {
@Test
@Ignore
public void failWithPositionIsOffsetByBom() throws IOException {
testFailWithPosition("Expected value at path $[1]", "\ufeff[\"a\",}]");
}
@@ -979,7 +1048,8 @@ public final class JsonUtf8ReaderTest {
}
@SuppressWarnings("CheckReturnValue")
@Test public void failWithPositionDeepPath() throws IOException {
@Test
public void failWithPositionDeepPath() throws IOException {
JsonReader reader = newReader("[1,{\"a\":[2,3,}");
reader.beginArray();
reader.nextInt();
@@ -996,7 +1066,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void failureMessagePathFromSkipName() throws IOException {
@Test
public void failureMessagePathFromSkipName() throws IOException {
JsonReader reader = newReader("{\"a\":[42,}");
reader.beginObject();
reader.skipName();
@@ -1010,7 +1081,9 @@ public final class JsonUtf8ReaderTest {
}
}
@Test @Ignore public void strictVeryLongNumber() throws IOException {
@Test
@Ignore
public void strictVeryLongNumber() throws IOException {
JsonReader reader = newReader("[0." + repeat('9', 8192) + "]");
reader.beginArray();
try {
@@ -1020,7 +1093,9 @@ public final class JsonUtf8ReaderTest {
}
}
@Test @Ignore public void lenientVeryLongNumber() throws IOException {
@Test
@Ignore
public void lenientVeryLongNumber() throws IOException {
JsonReader reader = newReader("[0." + repeat('9', 8192) + "]");
reader.setLenient(true);
reader.beginArray();
@@ -1030,7 +1105,8 @@ public final class JsonUtf8ReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void veryLongUnquotedLiteral() throws IOException {
@Test
public void veryLongUnquotedLiteral() throws IOException {
String literal = "a" + repeat('b', 8192) + "c";
JsonReader reader = newReader("[" + literal + "]");
reader.setLenient(true);
@@ -1039,7 +1115,8 @@ public final class JsonUtf8ReaderTest {
reader.endArray();
}
@Test public void tooDeeplyNestedArrays() throws IOException {
@Test
public void tooDeeplyNestedArrays() throws IOException {
JsonReader reader = newReader(repeat("[", MAX_DEPTH + 1) + repeat("]", MAX_DEPTH + 1));
for (int i = 0; i < MAX_DEPTH; i++) {
reader.beginArray();
@@ -1052,7 +1129,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void tooDeeplyNestedObjects() throws IOException {
@Test
public void tooDeeplyNestedObjects() throws IOException {
// Build a JSON document structured like {"a":{"a":{"a":{"a":true}}}}, but 255 levels deep.
String array = "{\"a\":%s}";
String json = "true";
@@ -1074,7 +1152,8 @@ public final class JsonUtf8ReaderTest {
}
// http://code.google.com/p/google-gson/issues/detail?id=409
@Test public void stringEndingInSlash() throws IOException {
@Test
public void stringEndingInSlash() throws IOException {
JsonReader reader = newReader("/");
reader.setLenient(true);
try {
@@ -1084,7 +1163,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void documentWithCommentEndingInSlash() throws IOException {
@Test
public void documentWithCommentEndingInSlash() throws IOException {
JsonReader reader = newReader("/* foo *//");
reader.setLenient(true);
try {
@@ -1094,7 +1174,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void stringWithLeadingSlash() throws IOException {
@Test
public void stringWithLeadingSlash() throws IOException {
JsonReader reader = newReader("/x");
reader.setLenient(true);
try {
@@ -1104,7 +1185,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void unterminatedObject() throws IOException {
@Test
public void unterminatedObject() throws IOException {
JsonReader reader = newReader("{\"a\":\"android\"x");
reader.setLenient(true);
reader.beginObject();
@@ -1117,7 +1199,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void veryLongQuotedString() throws IOException {
@Test
public void veryLongQuotedString() throws IOException {
char[] stringChars = new char[1024 * 16];
Arrays.fill(stringChars, 'x');
String string = new String(stringChars);
@@ -1128,7 +1211,8 @@ public final class JsonUtf8ReaderTest {
reader.endArray();
}
@Test public void veryLongUnquotedString() throws IOException {
@Test
public void veryLongUnquotedString() throws IOException {
char[] stringChars = new char[1024 * 16];
Arrays.fill(stringChars, 'x');
String string = new String(stringChars);
@@ -1140,7 +1224,8 @@ public final class JsonUtf8ReaderTest {
reader.endArray();
}
@Test public void veryLongUnterminatedString() throws IOException {
@Test
public void veryLongUnterminatedString() throws IOException {
char[] stringChars = new char[1024 * 16];
Arrays.fill(stringChars, 'x');
String string = new String(stringChars);
@@ -1156,7 +1241,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void strictExtraCommasInMaps() throws IOException {
@Test
public void strictExtraCommasInMaps() throws IOException {
JsonReader reader = newReader("{\"a\":\"b\",}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
@@ -1168,7 +1254,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void lenientExtraCommasInMaps() throws IOException {
@Test
public void lenientExtraCommasInMaps() throws IOException {
JsonReader reader = newReader("{\"a\":\"b\",}");
reader.setLenient(true);
reader.beginObject();
@@ -1181,7 +1268,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void malformedDocuments() throws IOException {
@Test
public void malformedDocuments() throws IOException {
assertDocument("{]", BEGIN_OBJECT, JsonEncodingException.class);
assertDocument("{,", BEGIN_OBJECT, JsonEncodingException.class);
assertDocument("{{", BEGIN_OBJECT, JsonEncodingException.class);
@@ -1193,12 +1281,12 @@ public final class JsonUtf8ReaderTest {
assertDocument("{\"name\":,", BEGIN_OBJECT, NAME, JsonEncodingException.class);
assertDocument("{\"name\"=}", BEGIN_OBJECT, NAME, JsonEncodingException.class);
assertDocument("{\"name\"=>}", BEGIN_OBJECT, NAME, JsonEncodingException.class);
assertDocument("{\"name\"=>\"string\":", BEGIN_OBJECT, NAME, STRING,
JsonEncodingException.class);
assertDocument("{\"name\"=>\"string\"=", BEGIN_OBJECT, NAME, STRING,
JsonEncodingException.class);
assertDocument("{\"name\"=>\"string\"=>", BEGIN_OBJECT, NAME, STRING,
JsonEncodingException.class);
assertDocument(
"{\"name\"=>\"string\":", BEGIN_OBJECT, NAME, STRING, JsonEncodingException.class);
assertDocument(
"{\"name\"=>\"string\"=", BEGIN_OBJECT, NAME, STRING, JsonEncodingException.class);
assertDocument(
"{\"name\"=>\"string\"=>", BEGIN_OBJECT, NAME, STRING, JsonEncodingException.class);
assertDocument("{\"name\"=>\"string\",", BEGIN_OBJECT, NAME, STRING, EOFException.class);
assertDocument("{\"name\"=>\"string\",\"name\"", BEGIN_OBJECT, NAME, STRING, NAME);
assertDocument("[}", BEGIN_ARRAY, JsonEncodingException.class);
@@ -1226,10 +1314,11 @@ public final class JsonUtf8ReaderTest {
}
/**
* This test behave slightly differently in Gson 2.2 and earlier. It fails
* during peek rather than during nextString().
* This test behave slightly differently in Gson 2.2 and earlier. It fails during peek rather than
* during nextString().
*/
@Test public void unterminatedStringFailure() throws IOException {
@Test
public void unterminatedStringFailure() throws IOException {
JsonReader reader = newReader("[\"string");
reader.setLenient(true);
reader.beginArray();
@@ -1241,7 +1330,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void invalidEscape() throws IOException {
@Test
public void invalidEscape() throws IOException {
JsonReader reader = newReader("[\"str\\ing\"]");
reader.beginArray();
try {
@@ -1252,7 +1342,8 @@ public final class JsonUtf8ReaderTest {
}
}
@Test public void lenientInvalidEscape() throws IOException {
@Test
public void lenientInvalidEscape() throws IOException {
JsonReader reader = newReader("[\"str\\ing\"]");
reader.setLenient(true);
reader.beginArray();

View File

@@ -15,14 +15,15 @@
*/
package com.squareup.moshi;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import okio.Buffer;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public final class JsonUtf8WriterTest {
@Test public void prettyPrintObject() throws IOException {
@Test
public void prettyPrintObject() throws IOException {
Buffer buffer = new Buffer();
JsonWriter writer = JsonWriter.of(buffer);
writer.setSerializeNulls(true);
@@ -43,24 +44,26 @@ public final class JsonUtf8WriterTest {
writer.endObject();
writer.endObject();
String expected = "{\n"
+ " \"a\": true,\n"
+ " \"b\": false,\n"
+ " \"c\": 5.0,\n"
+ " \"e\": null,\n"
+ " \"f\": [\n"
+ " 6.0,\n"
+ " 7.0\n"
+ " ],\n"
+ " \"g\": {\n"
+ " \"h\": 8.0,\n"
+ " \"i\": 9.0\n"
+ " }\n"
+ "}";
String expected =
"{\n"
+ " \"a\": true,\n"
+ " \"b\": false,\n"
+ " \"c\": 5.0,\n"
+ " \"e\": null,\n"
+ " \"f\": [\n"
+ " 6.0,\n"
+ " 7.0\n"
+ " ],\n"
+ " \"g\": {\n"
+ " \"h\": 8.0,\n"
+ " \"i\": 9.0\n"
+ " }\n"
+ "}";
assertThat(buffer.readUtf8()).isEqualTo(expected);
}
@Test public void prettyPrintArray() throws IOException {
@Test
public void prettyPrintArray() throws IOException {
Buffer buffer = new Buffer();
JsonWriter writer = JsonWriter.of(buffer);
writer.setIndent(" ");
@@ -80,24 +83,26 @@ public final class JsonUtf8WriterTest {
writer.endArray();
writer.endArray();
String expected = "[\n"
+ " true,\n"
+ " false,\n"
+ " 5.0,\n"
+ " null,\n"
+ " {\n"
+ " \"a\": 6.0,\n"
+ " \"b\": 7.0\n"
+ " },\n"
+ " [\n"
+ " 8.0,\n"
+ " 9.0\n"
+ " ]\n"
+ "]";
String expected =
"[\n"
+ " true,\n"
+ " false,\n"
+ " 5.0,\n"
+ " null,\n"
+ " {\n"
+ " \"a\": 6.0,\n"
+ " \"b\": 7.0\n"
+ " },\n"
+ " [\n"
+ " 8.0,\n"
+ " 9.0\n"
+ " ]\n"
+ "]";
assertThat(buffer.readUtf8()).isEqualTo(expected);
}
@Test public void repeatedNameIgnored() throws IOException {
@Test
public void repeatedNameIgnored() throws IOException {
Buffer buffer = new Buffer();
JsonWriter writer = JsonWriter.of(buffer);
writer.beginObject();
@@ -108,7 +113,8 @@ public final class JsonUtf8WriterTest {
assertThat(buffer.readUtf8()).isEqualTo("{\"a\":1,\"a\":2}");
}
@Test public void valueFromSource() throws IOException {
@Test
public void valueFromSource() throws IOException {
Buffer buffer = new Buffer();
JsonWriter writer = JsonUtf8Writer.of(buffer);
writer.beginObject();

View File

@@ -15,6 +15,13 @@
*/
package com.squareup.moshi;
import static com.squareup.moshi.TestUtil.MAX_DEPTH;
import static com.squareup.moshi.TestUtil.repeat;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -24,15 +31,9 @@ import java.util.List;
import java.util.Map;
import org.junit.Test;
import static com.squareup.moshi.TestUtil.MAX_DEPTH;
import static com.squareup.moshi.TestUtil.repeat;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
public final class JsonValueReaderTest {
@Test public void array() throws Exception {
@Test
public void array() throws Exception {
List<Object> root = new ArrayList<>();
root.add("s");
root.add(1.5d);
@@ -67,7 +68,8 @@ public final class JsonValueReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void object() throws Exception {
@Test
public void object() throws Exception {
Map<String, Object> root = new LinkedHashMap<>();
root.put("a", "s");
root.put("b", 1.5d);
@@ -110,9 +112,10 @@ public final class JsonValueReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void nesting() throws Exception {
List<Map<String, List<Map<String, Double>>>> root
= singletonList(singletonMap("a", singletonList(singletonMap("b", 1.5d))));
@Test
public void nesting() throws Exception {
List<Map<String, List<Map<String, Double>>>> root =
singletonList(singletonMap("a", singletonList(singletonMap("b", 1.5d))));
JsonReader reader = new JsonValueReader(root);
assertThat(reader.hasNext()).isTrue();
@@ -162,7 +165,8 @@ public final class JsonValueReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void promoteNameToValue() throws Exception {
@Test
public void promoteNameToValue() throws Exception {
Map<String, String> root = singletonMap("a", "b");
JsonReader reader = new JsonValueReader(root);
@@ -178,7 +182,8 @@ public final class JsonValueReaderTest {
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void endArrayTooEarly() throws Exception {
@Test
public void endArrayTooEarly() throws Exception {
JsonReader reader = new JsonValueReader(singletonList("s"));
reader.beginArray();
@@ -186,12 +191,13 @@ public final class JsonValueReaderTest {
reader.endArray();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected END_ARRAY but was s, a java.lang.String, at path $[0]");
assertThat(expected)
.hasMessage("Expected END_ARRAY but was s, a java.lang.String, at path $[0]");
}
}
@Test public void endObjectTooEarly() throws Exception {
@Test
public void endObjectTooEarly() throws Exception {
JsonReader reader = new JsonValueReader(singletonMap("a", "b"));
reader.beginObject();
@@ -203,7 +209,8 @@ public final class JsonValueReaderTest {
}
}
@Test public void unsupportedType() throws Exception {
@Test
public void unsupportedType() throws Exception {
JsonReader reader = new JsonValueReader(singletonList(new StringBuilder("x")));
reader.beginArray();
@@ -211,12 +218,13 @@ public final class JsonValueReaderTest {
reader.peek();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected a JSON value but was x, a java.lang.StringBuilder, at path $[0]");
assertThat(expected)
.hasMessage("Expected a JSON value but was x, a java.lang.StringBuilder, at path $[0]");
}
}
@Test public void unsupportedKeyType() throws Exception {
@Test
public void unsupportedKeyType() throws Exception {
JsonReader reader = new JsonValueReader(singletonMap(new StringBuilder("x"), "y"));
reader.beginObject();
@@ -224,12 +232,13 @@ public final class JsonValueReaderTest {
reader.nextName();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected NAME but was x, a java.lang.StringBuilder, at path $.");
assertThat(expected)
.hasMessage("Expected NAME but was x, a java.lang.StringBuilder, at path $.");
}
}
@Test public void nullKey() throws Exception {
@Test
public void nullKey() throws Exception {
JsonReader reader = new JsonValueReader(singletonMap(null, "y"));
reader.beginObject();
@@ -241,85 +250,93 @@ public final class JsonValueReaderTest {
}
}
@Test public void unexpectedIntType() throws Exception {
@Test
public void unexpectedIntType() throws Exception {
JsonReader reader = new JsonValueReader(singletonList(new StringBuilder("1")));
reader.beginArray();
try {
reader.nextInt();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected NUMBER but was 1, a java.lang.StringBuilder, at path $[0]");
assertThat(expected)
.hasMessage("Expected NUMBER but was 1, a java.lang.StringBuilder, at path $[0]");
}
}
@Test public void unexpectedLongType() throws Exception {
@Test
public void unexpectedLongType() throws Exception {
JsonReader reader = new JsonValueReader(singletonList(new StringBuilder("1")));
reader.beginArray();
try {
reader.nextLong();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected NUMBER but was 1, a java.lang.StringBuilder, at path $[0]");
assertThat(expected)
.hasMessage("Expected NUMBER but was 1, a java.lang.StringBuilder, at path $[0]");
}
}
@Test public void unexpectedDoubleType() throws Exception {
@Test
public void unexpectedDoubleType() throws Exception {
JsonReader reader = new JsonValueReader(singletonList(new StringBuilder("1")));
reader.beginArray();
try {
reader.nextDouble();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected NUMBER but was 1, a java.lang.StringBuilder, at path $[0]");
assertThat(expected)
.hasMessage("Expected NUMBER but was 1, a java.lang.StringBuilder, at path $[0]");
}
}
@Test public void unexpectedStringType() throws Exception {
@Test
public void unexpectedStringType() throws Exception {
JsonReader reader = new JsonValueReader(singletonList(new StringBuilder("s")));
reader.beginArray();
try {
reader.nextString();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected STRING but was s, a java.lang.StringBuilder, at path $[0]");
assertThat(expected)
.hasMessage("Expected STRING but was s, a java.lang.StringBuilder, at path $[0]");
}
}
@Test public void unexpectedBooleanType() throws Exception {
@Test
public void unexpectedBooleanType() throws Exception {
JsonReader reader = new JsonValueReader(singletonList(new StringBuilder("true")));
reader.beginArray();
try {
reader.nextBoolean();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected BOOLEAN but was true, a java.lang.StringBuilder, at path $[0]");
assertThat(expected)
.hasMessage("Expected BOOLEAN but was true, a java.lang.StringBuilder, at path $[0]");
}
}
@Test public void unexpectedNullType() throws Exception {
@Test
public void unexpectedNullType() throws Exception {
JsonReader reader = new JsonValueReader(singletonList(new StringBuilder("null")));
reader.beginArray();
try {
reader.nextNull();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage(
"Expected NULL but was null, a java.lang.StringBuilder, at path $[0]");
assertThat(expected)
.hasMessage("Expected NULL but was null, a java.lang.StringBuilder, at path $[0]");
}
}
@Test public void skipRoot() throws Exception {
@Test
public void skipRoot() throws Exception {
JsonReader reader = new JsonValueReader(singletonList(new StringBuilder("x")));
reader.skipValue();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_DOCUMENT);
}
@Test public void skipListValue() throws Exception {
@Test
public void skipListValue() throws Exception {
List<Object> root = new ArrayList<>();
root.add("a");
root.add("b");
@@ -340,7 +357,8 @@ public final class JsonValueReaderTest {
reader.endArray();
}
@Test public void skipObjectName() throws Exception {
@Test
public void skipObjectName() throws Exception {
Map<String, Object> root = new LinkedHashMap<>();
root.put("a", "s");
root.put("b", 1.5d);
@@ -367,7 +385,8 @@ public final class JsonValueReaderTest {
reader.endObject();
}
@Test public void skipObjectValue() throws Exception {
@Test
public void skipObjectValue() throws Exception {
Map<String, Object> root = new LinkedHashMap<>();
root.put("a", "s");
root.put("b", 1.5d);
@@ -394,7 +413,8 @@ public final class JsonValueReaderTest {
reader.endObject();
}
@Test public void failOnUnknown() throws Exception {
@Test
public void failOnUnknown() throws Exception {
JsonReader reader = new JsonValueReader(singletonList("a"));
reader.setFailOnUnknown(true);
@@ -407,7 +427,8 @@ public final class JsonValueReaderTest {
}
}
@Test public void close() throws Exception {
@Test
public void close() throws Exception {
try {
JsonReader reader = new JsonValueReader(singletonList("a"));
reader.beginArray();
@@ -426,7 +447,8 @@ public final class JsonValueReaderTest {
}
}
@Test public void numberToStringCoersion() throws Exception {
@Test
public void numberToStringCoersion() throws Exception {
JsonReader reader =
new JsonValueReader(Arrays.asList(0, 9223372036854775807L, 2.5d, 3.01f, "a", "5"));
reader.beginArray();
@@ -439,7 +461,8 @@ public final class JsonValueReaderTest {
reader.endArray();
}
@Test public void tooDeeplyNestedArrays() throws IOException {
@Test
public void tooDeeplyNestedArrays() throws IOException {
Object root = Collections.emptyList();
for (int i = 0; i < MAX_DEPTH + 1; i++) {
root = singletonList(root);
@@ -456,7 +479,8 @@ public final class JsonValueReaderTest {
}
}
@Test public void tooDeeplyNestedObjects() throws IOException {
@Test
public void tooDeeplyNestedObjects() throws IOException {
Object root = Boolean.TRUE;
for (int i = 0; i < MAX_DEPTH + 1; i++) {
root = singletonMap("a", root);

View File

@@ -15,6 +15,10 @@
*/
package com.squareup.moshi;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -27,13 +31,10 @@ import java.util.concurrent.atomic.AtomicLong;
import okio.Buffer;
import org.junit.Test;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
public final class JsonValueWriterTest {
@SuppressWarnings("unchecked")
@Test public void array() throws Exception {
@Test
public void array() throws Exception {
JsonValueWriter writer = new JsonValueWriter();
writer.beginArray();
@@ -46,7 +47,8 @@ public final class JsonValueWriterTest {
assertThat((List<Object>) writer.root()).containsExactly("s", 1.5d, true, null);
}
@Test public void object() throws Exception {
@Test
public void object() throws Exception {
JsonValueWriter writer = new JsonValueWriter();
writer.setSerializeNulls(true);
@@ -57,14 +59,16 @@ public final class JsonValueWriterTest {
writer.name("d").nullValue();
writer.endObject();
assertThat((Map<String, Object>) writer.root()).containsExactly(
new SimpleEntry<String, Object>("a", "s"),
new SimpleEntry<String, Object>("b", 1.5d),
new SimpleEntry<String, Object>("c", true),
new SimpleEntry<String, Object>("d", null));
assertThat((Map<String, Object>) writer.root())
.containsExactly(
new SimpleEntry<String, Object>("a", "s"),
new SimpleEntry<String, Object>("b", 1.5d),
new SimpleEntry<String, Object>("c", true),
new SimpleEntry<String, Object>("d", null));
}
@Test public void repeatedNameThrows() throws IOException {
@Test
public void repeatedNameThrows() throws IOException {
JsonValueWriter writer = new JsonValueWriter();
writer.beginObject();
writer.name("a").value(1L);
@@ -72,12 +76,12 @@ public final class JsonValueWriterTest {
writer.name("a").value(2L);
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage(
"Map key 'a' has multiple values at path $.a: 1 and 2");
assertThat(expected).hasMessage("Map key 'a' has multiple values at path $.a: 1 and 2");
}
}
@Test public void valueLongEmitsLong() throws Exception {
@Test
public void valueLongEmitsLong() throws Exception {
JsonValueWriter writer = new JsonValueWriter();
writer.beginArray();
writer.value(Long.MIN_VALUE);
@@ -87,16 +91,12 @@ public final class JsonValueWriterTest {
writer.value(Long.MAX_VALUE);
writer.endArray();
List<Number> numbers = Arrays.<Number>asList(
Long.MIN_VALUE,
-1L,
0L,
1L,
Long.MAX_VALUE);
List<Number> numbers = Arrays.<Number>asList(Long.MIN_VALUE, -1L, 0L, 1L, Long.MAX_VALUE);
assertThat((List<?>) writer.root()).isEqualTo(numbers);
}
@Test public void valueDoubleEmitsDouble() throws Exception {
@Test
public void valueDoubleEmitsDouble() throws Exception {
JsonValueWriter writer = new JsonValueWriter();
writer.setLenient(true);
writer.beginArray();
@@ -124,33 +124,35 @@ public final class JsonValueWriterTest {
writer.value(Double.NaN);
writer.endArray();
List<Number> numbers = Arrays.<Number>asList(
-2147483649.0d,
-2147483648.0d,
-1.0d,
0.0d,
1.0d,
2147483647.0d,
2147483648.0d,
9007199254740991.0d,
9007199254740992.0d,
9007199254740994.0d,
9223372036854775807.0d,
-0.5d,
-0.0d,
0.5d,
9.22337203685478e18,
Double.NEGATIVE_INFINITY,
Double.MIN_VALUE,
Double.MIN_NORMAL,
-Double.MIN_NORMAL,
Double.MAX_VALUE,
Double.POSITIVE_INFINITY,
Double.NaN);
List<Number> numbers =
Arrays.<Number>asList(
-2147483649.0d,
-2147483648.0d,
-1.0d,
0.0d,
1.0d,
2147483647.0d,
2147483648.0d,
9007199254740991.0d,
9007199254740992.0d,
9007199254740994.0d,
9223372036854775807.0d,
-0.5d,
-0.0d,
0.5d,
9.22337203685478e18,
Double.NEGATIVE_INFINITY,
Double.MIN_VALUE,
Double.MIN_NORMAL,
-Double.MIN_NORMAL,
Double.MAX_VALUE,
Double.POSITIVE_INFINITY,
Double.NaN);
assertThat((List<?>) writer.root()).isEqualTo(numbers);
}
@Test public void primitiveIntegerTypesEmitLong() throws Exception {
@Test
public void primitiveIntegerTypesEmitLong() throws Exception {
JsonValueWriter writer = new JsonValueWriter();
writer.beginArray();
writer.value(new Byte(Byte.MIN_VALUE));
@@ -159,28 +161,25 @@ public final class JsonValueWriterTest {
writer.value(new Long(Long.MIN_VALUE));
writer.endArray();
List<Number> numbers = Arrays.<Number>asList(
-128L,
-32768L,
-2147483648L,
-9223372036854775808L);
List<Number> numbers =
Arrays.<Number>asList(-128L, -32768L, -2147483648L, -9223372036854775808L);
assertThat((List<?>) writer.root()).isEqualTo(numbers);
}
@Test public void primitiveFloatingPointTypesEmitDouble() throws Exception {
@Test
public void primitiveFloatingPointTypesEmitDouble() throws Exception {
JsonValueWriter writer = new JsonValueWriter();
writer.beginArray();
writer.value(new Float(0.5f));
writer.value(new Double(0.5d));
writer.endArray();
List<Number> numbers = Arrays.<Number>asList(
0.5d,
0.5d);
List<Number> numbers = Arrays.<Number>asList(0.5d, 0.5d);
assertThat((List<?>) writer.root()).isEqualTo(numbers);
}
@Test public void otherNumberTypesEmitBigDecimal() throws Exception {
@Test
public void otherNumberTypesEmitBigDecimal() throws Exception {
JsonValueWriter writer = new JsonValueWriter();
writer.beginArray();
writer.value(new AtomicInteger(-2147483648));
@@ -204,30 +203,32 @@ public final class JsonValueWriterTest {
writer.value(new BigDecimal("0.0000100e-10"));
writer.endArray();
List<Number> numbers = Arrays.<Number>asList(
new BigDecimal("-2147483648"),
new BigDecimal("-9223372036854775808"),
new BigDecimal("-9223372036854775808"),
new BigDecimal("-1"),
new BigDecimal("0"),
new BigDecimal("1"),
new BigDecimal("9223372036854775807"),
new BigDecimal("-9223372036854775808"),
new BigDecimal("-1"),
new BigDecimal("0"),
new BigDecimal("1"),
new BigDecimal("9223372036854775807"),
new BigDecimal("-9223372036854775809"),
new BigDecimal("9223372036854775808"),
new BigDecimal("-9223372036854775809"),
new BigDecimal("9223372036854775808"),
new BigDecimal("0.5"),
new BigDecimal("100000e15"),
new BigDecimal("0.0000100e-10"));
List<Number> numbers =
Arrays.<Number>asList(
new BigDecimal("-2147483648"),
new BigDecimal("-9223372036854775808"),
new BigDecimal("-9223372036854775808"),
new BigDecimal("-1"),
new BigDecimal("0"),
new BigDecimal("1"),
new BigDecimal("9223372036854775807"),
new BigDecimal("-9223372036854775808"),
new BigDecimal("-1"),
new BigDecimal("0"),
new BigDecimal("1"),
new BigDecimal("9223372036854775807"),
new BigDecimal("-9223372036854775809"),
new BigDecimal("9223372036854775808"),
new BigDecimal("-9223372036854775809"),
new BigDecimal("9223372036854775808"),
new BigDecimal("0.5"),
new BigDecimal("100000e15"),
new BigDecimal("0.0000100e-10"));
assertThat((List<?>) writer.root()).isEqualTo(numbers);
}
@Test public void valueCustomNumberTypeEmitsLongOrBigDecimal() throws Exception {
@Test
public void valueCustomNumberTypeEmitsLongOrBigDecimal() throws Exception {
JsonValueWriter writer = new JsonValueWriter();
writer.beginArray();
writer.value(stringNumber("-9223372036854775809"));
@@ -236,15 +237,17 @@ public final class JsonValueWriterTest {
writer.value(stringNumber("1.0"));
writer.endArray();
List<Number> numbers = Arrays.<Number>asList(
new BigDecimal("-9223372036854775809"),
new BigDecimal("-9223372036854775808"),
new BigDecimal("0.5"),
new BigDecimal("1.0"));
List<Number> numbers =
Arrays.<Number>asList(
new BigDecimal("-9223372036854775809"),
new BigDecimal("-9223372036854775808"),
new BigDecimal("0.5"),
new BigDecimal("1.0"));
assertThat((List<?>) writer.root()).isEqualTo(numbers);
}
@Test public void valueFromSource() throws IOException {
@Test
public void valueFromSource() throws IOException {
JsonValueWriter writer = new JsonValueWriter();
writer.beginObject();
writer.name("a");
@@ -256,11 +259,12 @@ public final class JsonValueWriterTest {
writer.name("d");
writer.value(new Buffer().writeUtf8("null"));
writer.endObject();
assertThat((Map<String, Object>) writer.root()).containsExactly(
new SimpleEntry<String, Object>("a", singletonList("value")),
new SimpleEntry<String, Object>("b", 2.0d),
new SimpleEntry<String, Object>("c", 3L),
new SimpleEntry<String, Object>("d", null));
assertThat((Map<String, Object>) writer.root())
.containsExactly(
new SimpleEntry<String, Object>("a", singletonList("value")),
new SimpleEntry<String, Object>("b", 2.0d),
new SimpleEntry<String, Object>("c", 3L),
new SimpleEntry<String, Object>("d", null));
}
/**
@@ -270,23 +274,28 @@ public final class JsonValueWriterTest {
*/
private Number stringNumber(final String s) {
return new Number() {
@Override public int intValue() {
@Override
public int intValue() {
throw new AssertionError();
}
@Override public long longValue() {
@Override
public long longValue() {
throw new AssertionError();
}
@Override public float floatValue() {
@Override
public float floatValue() {
throw new AssertionError();
}
@Override public double doubleValue() {
@Override
public double doubleValue() {
throw new AssertionError();
}
@Override public String toString() {
@Override
public String toString() {
return s;
}
};

View File

@@ -15,6 +15,9 @@
*/
package com.squareup.moshi;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assume.assumeTrue;
import java.io.IOException;
import java.math.BigInteger;
import java.util.List;
@@ -24,9 +27,6 @@ import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class)
public final class JsonWriterPathTest {
@Parameter public JsonCodecFactory factory;
@@ -36,7 +36,8 @@ public final class JsonWriterPathTest {
return JsonCodecFactory.factories();
}
@Test public void path() throws IOException {
@Test
public void path() throws IOException {
JsonWriter writer = factory.newWriter();
assertThat(writer.getPath()).isEqualTo("$");
writer.beginObject();
@@ -75,7 +76,8 @@ public final class JsonWriterPathTest {
assertThat(writer.getPath()).isEqualTo("$");
}
@Test public void arrayOfObjects() throws IOException {
@Test
public void arrayOfObjects() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
assertThat(writer.getPath()).isEqualTo("$[0]");
@@ -95,7 +97,8 @@ public final class JsonWriterPathTest {
assertThat(writer.getPath()).isEqualTo("$");
}
@Test public void arrayOfArrays() throws IOException {
@Test
public void arrayOfArrays() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
assertThat(writer.getPath()).isEqualTo("$[0]");
@@ -115,7 +118,8 @@ public final class JsonWriterPathTest {
assertThat(writer.getPath()).isEqualTo("$");
}
@Test public void objectPath() throws IOException {
@Test
public void objectPath() throws IOException {
JsonWriter writer = factory.newWriter();
assertThat(writer.getPath()).isEqualTo("$");
writer.beginObject();
@@ -134,7 +138,8 @@ public final class JsonWriterPathTest {
assertThat(writer.getPath()).isEqualTo("$");
}
@Test public void nestedObjects() throws IOException {
@Test
public void nestedObjects() throws IOException {
JsonWriter writer = factory.newWriter();
assertThat(writer.getPath()).isEqualTo("$");
writer.beginObject();
@@ -159,7 +164,8 @@ public final class JsonWriterPathTest {
assertThat(writer.getPath()).isEqualTo("$");
}
@Test public void arrayPath() throws IOException {
@Test
public void arrayPath() throws IOException {
JsonWriter writer = factory.newWriter();
assertThat(writer.getPath()).isEqualTo("$");
writer.beginArray();
@@ -180,7 +186,8 @@ public final class JsonWriterPathTest {
assertThat(writer.getPath()).isEqualTo("$");
}
@Test public void nestedArrays() throws IOException {
@Test
public void nestedArrays() throws IOException {
JsonWriter writer = factory.newWriter();
assertThat(writer.getPath()).isEqualTo("$");
writer.beginArray();
@@ -201,7 +208,8 @@ public final class JsonWriterPathTest {
assertThat(writer.getPath()).isEqualTo("$");
}
@Test public void multipleTopLevelValuesInOneDocument() throws IOException {
@Test
public void multipleTopLevelValuesInOneDocument() throws IOException {
assumeTrue(factory.encodesToBytes());
JsonWriter writer = factory.newWriter();
@@ -214,7 +222,8 @@ public final class JsonWriterPathTest {
assertThat(writer.getPath()).isEqualTo("$");
}
@Test public void skipNulls() throws IOException {
@Test
public void skipNulls() throws IOException {
JsonWriter writer = factory.newWriter();
writer.setSerializeNulls(false);
assertThat(writer.getPath()).isEqualTo("$");

View File

@@ -15,6 +15,12 @@
*/
package com.squareup.moshi;
import static com.squareup.moshi.TestUtil.MAX_DEPTH;
import static com.squareup.moshi.TestUtil.repeat;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -30,12 +36,6 @@ import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import static com.squareup.moshi.TestUtil.MAX_DEPTH;
import static com.squareup.moshi.TestUtil.repeat;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class)
public final class JsonWriterTest {
@Parameter public JsonCodecFactory factory;
@@ -45,7 +45,8 @@ public final class JsonWriterTest {
return JsonCodecFactory.factories();
}
@Test public void nullsValuesNotSerializedByDefault() throws IOException {
@Test
public void nullsValuesNotSerializedByDefault() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.name("a");
@@ -55,7 +56,8 @@ public final class JsonWriterTest {
assertThat(factory.json()).isEqualTo("{}");
}
@Test public void nullsValuesSerializedWhenConfigured() throws IOException {
@Test
public void nullsValuesSerializedWhenConfigured() throws IOException {
JsonWriter writer = factory.newWriter();
writer.setSerializeNulls(true);
writer.beginObject();
@@ -66,42 +68,48 @@ public final class JsonWriterTest {
assertThat(factory.json()).isEqualTo("{\"a\":null}");
}
@Test public void topLevelBoolean() throws IOException {
@Test
public void topLevelBoolean() throws IOException {
JsonWriter writer = factory.newWriter();
writer.value(true);
writer.close();
assertThat(factory.json()).isEqualTo("true");
}
@Test public void topLevelNull() throws IOException {
@Test
public void topLevelNull() throws IOException {
JsonWriter writer = factory.newWriter();
writer.nullValue();
writer.close();
assertThat(factory.json()).isEqualTo("null");
}
@Test public void topLevelInt() throws IOException {
@Test
public void topLevelInt() throws IOException {
JsonWriter writer = factory.newWriter();
writer.value(123);
writer.close();
assertThat(factory.json()).isEqualTo("123");
}
@Test public void topLevelDouble() throws IOException {
@Test
public void topLevelDouble() throws IOException {
JsonWriter writer = factory.newWriter();
writer.value(123.4);
writer.close();
assertThat(factory.json()).isEqualTo("123.4");
}
@Test public void topLevelString() throws IOException {
@Test
public void topLevelString() throws IOException {
JsonWriter writer = factory.newWriter();
writer.value("a");
writer.close();
assertThat(factory.json()).isEqualTo("\"a\"");
}
@Test public void invalidTopLevelTypes() throws IOException {
@Test
public void invalidTopLevelTypes() throws IOException {
JsonWriter writer = factory.newWriter();
try {
writer.name("hello").value("world");
@@ -110,7 +118,8 @@ public final class JsonWriterTest {
}
}
@Test public void twoNames() throws IOException {
@Test
public void twoNames() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.name("a");
@@ -121,7 +130,8 @@ public final class JsonWriterTest {
}
}
@Test public void nameWithoutValue() throws IOException {
@Test
public void nameWithoutValue() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.name("a");
@@ -132,7 +142,8 @@ public final class JsonWriterTest {
}
}
@Test public void valueWithoutName() throws IOException {
@Test
public void valueWithoutName() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
try {
@@ -142,7 +153,8 @@ public final class JsonWriterTest {
}
}
@Test public void multipleTopLevelValues() throws IOException {
@Test
public void multipleTopLevelValues() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray().endArray();
try {
@@ -152,7 +164,8 @@ public final class JsonWriterTest {
}
}
@Test public void badNestingObject() throws IOException {
@Test
public void badNestingObject() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.beginObject();
@@ -163,7 +176,8 @@ public final class JsonWriterTest {
}
}
@Test public void badNestingArray() throws IOException {
@Test
public void badNestingArray() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.beginArray();
@@ -174,7 +188,8 @@ public final class JsonWriterTest {
}
}
@Test public void nullName() throws IOException {
@Test
public void nullName() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
try {
@@ -184,7 +199,8 @@ public final class JsonWriterTest {
}
}
@Test public void nullStringValue() throws IOException {
@Test
public void nullStringValue() throws IOException {
JsonWriter writer = factory.newWriter();
writer.setSerializeNulls(true);
writer.beginObject();
@@ -194,7 +210,8 @@ public final class JsonWriterTest {
assertThat(factory.json()).isEqualTo("{\"a\":null}");
}
@Test public void nonFiniteDoubles() throws IOException {
@Test
public void nonFiniteDoubles() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
try {
@@ -214,7 +231,8 @@ public final class JsonWriterTest {
}
}
@Test public void nonFiniteBoxedDoubles() throws IOException {
@Test
public void nonFiniteBoxedDoubles() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
try {
@@ -234,7 +252,8 @@ public final class JsonWriterTest {
}
}
@Test public void doubles() throws IOException {
@Test
public void doubles() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.value(-0.0);
@@ -248,18 +267,21 @@ public final class JsonWriterTest {
writer.value(Math.E);
writer.endArray();
writer.close();
assertThat(factory.json()).isEqualTo("[-0.0,"
+ "1.0,"
+ "1.7976931348623157E308,"
+ "4.9E-324,"
+ "0.0,"
+ "-0.5,"
+ "2.2250738585072014E-308,"
+ "3.141592653589793,"
+ "2.718281828459045]");
assertThat(factory.json())
.isEqualTo(
"[-0.0,"
+ "1.0,"
+ "1.7976931348623157E308,"
+ "4.9E-324,"
+ "0.0,"
+ "-0.5,"
+ "2.2250738585072014E-308,"
+ "3.141592653589793,"
+ "2.718281828459045]");
}
@Test public void longs() throws IOException {
@Test
public void longs() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.value(0);
@@ -269,14 +291,12 @@ public final class JsonWriterTest {
writer.value(Long.MAX_VALUE);
writer.endArray();
writer.close();
assertThat(factory.json()).isEqualTo("[0,"
+ "1,"
+ "-1,"
+ "-9223372036854775808,"
+ "9223372036854775807]");
assertThat(factory.json())
.isEqualTo("[0," + "1," + "-1," + "-9223372036854775808," + "9223372036854775807]");
}
@Test public void numbers() throws IOException {
@Test
public void numbers() throws IOException {
assumeTrue(factory.supportsBigNumbers());
JsonWriter writer = factory.newWriter();
@@ -287,13 +307,16 @@ public final class JsonWriterTest {
writer.value(new BigDecimal("3.141592653589793238462643383"));
writer.endArray();
writer.close();
assertThat(factory.json()).isEqualTo("[0,"
+ "9223372036854775808,"
+ "-9223372036854775809,"
+ "3.141592653589793238462643383]");
assertThat(factory.json())
.isEqualTo(
"[0,"
+ "9223372036854775808,"
+ "-9223372036854775809,"
+ "3.141592653589793238462643383]");
}
@Test public void nullNumbers() throws IOException {
@Test
public void nullNumbers() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.value((Number) null);
@@ -302,7 +325,8 @@ public final class JsonWriterTest {
assertThat(factory.json()).isEqualTo("[null]");
}
@Test public void booleans() throws IOException {
@Test
public void booleans() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.value(true);
@@ -311,7 +335,8 @@ public final class JsonWriterTest {
assertThat(factory.json()).isEqualTo("[true,false]");
}
@Test public void boxedBooleans() throws IOException {
@Test
public void boxedBooleans() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.value((Boolean) true);
@@ -321,7 +346,8 @@ public final class JsonWriterTest {
assertThat(factory.json()).isEqualTo("[true,false,null]");
}
@Test public void nulls() throws IOException {
@Test
public void nulls() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.nullValue();
@@ -329,7 +355,8 @@ public final class JsonWriterTest {
assertThat(factory.json()).isEqualTo("[null]");
}
@Test public void strings() throws IOException {
@Test
public void strings() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.value("a");
@@ -351,27 +378,30 @@ public final class JsonWriterTest {
writer.value("\0");
writer.value("\u0019");
writer.endArray();
assertThat(factory.json()).isEqualTo("[\"a\","
+ "\"a\\\"\","
+ "\"\\\"\","
+ "\":\","
+ "\",\","
+ "\"\\b\","
+ "\"\\f\","
+ "\"\\n\","
+ "\"\\r\","
+ "\"\\t\","
+ "\" \","
+ "\"\\\\\","
+ "\"{\","
+ "\"}\","
+ "\"[\","
+ "\"]\","
+ "\"\\u0000\","
+ "\"\\u0019\"]");
assertThat(factory.json())
.isEqualTo(
"[\"a\","
+ "\"a\\\"\","
+ "\"\\\"\","
+ "\":\","
+ "\",\","
+ "\"\\b\","
+ "\"\\f\","
+ "\"\\n\","
+ "\"\\r\","
+ "\"\\t\","
+ "\" \","
+ "\"\\\\\","
+ "\"{\","
+ "\"}\","
+ "\"[\","
+ "\"]\","
+ "\"\\u0000\","
+ "\"\\u0019\"]");
}
@Test public void unicodeLineBreaksEscaped() throws IOException {
@Test
public void unicodeLineBreaksEscaped() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.value("\u2028 \u2029");
@@ -379,21 +409,24 @@ public final class JsonWriterTest {
assertThat(factory.json()).isEqualTo("[\"\\u2028 \\u2029\"]");
}
@Test public void emptyArray() throws IOException {
@Test
public void emptyArray() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.endArray();
assertThat(factory.json()).isEqualTo("[]");
}
@Test public void emptyObject() throws IOException {
@Test
public void emptyObject() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.endObject();
assertThat(factory.json()).isEqualTo("{}");
}
@Test public void objectsInArrays() throws IOException {
@Test
public void objectsInArrays() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.beginObject();
@@ -405,11 +438,11 @@ public final class JsonWriterTest {
writer.name("d").value(true);
writer.endObject();
writer.endArray();
assertThat(factory.json()).isEqualTo("[{\"a\":5,\"b\":false},"
+ "{\"c\":6,\"d\":true}]");
assertThat(factory.json()).isEqualTo("[{\"a\":5,\"b\":false}," + "{\"c\":6,\"d\":true}]");
}
@Test public void arraysInObjects() throws IOException {
@Test
public void arraysInObjects() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.name("a");
@@ -423,11 +456,11 @@ public final class JsonWriterTest {
writer.value(true);
writer.endArray();
writer.endObject();
assertThat(factory.json()).isEqualTo("{\"a\":[5,false],"
+ "\"b\":[6,true]}");
assertThat(factory.json()).isEqualTo("{\"a\":[5,false]," + "\"b\":[6,true]}");
}
@Test public void deepNestingArrays() throws IOException {
@Test
public void deepNestingArrays() throws IOException {
JsonWriter writer = factory.newWriter();
for (int i = 0; i < MAX_DEPTH; i++) {
writer.beginArray();
@@ -435,11 +468,11 @@ public final class JsonWriterTest {
for (int i = 0; i < MAX_DEPTH; i++) {
writer.endArray();
}
assertThat(factory.json())
.isEqualTo(repeat("[", MAX_DEPTH) + repeat("]", MAX_DEPTH));
assertThat(factory.json()).isEqualTo(repeat("[", MAX_DEPTH) + repeat("]", MAX_DEPTH));
}
@Test public void tooDeeplyNestingArrays() throws IOException {
@Test
public void tooDeeplyNestingArrays() throws IOException {
JsonWriter writer = factory.newWriter();
for (int i = 0; i < MAX_DEPTH; i++) {
writer.beginArray();
@@ -448,12 +481,13 @@ public final class JsonWriterTest {
writer.beginArray();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage("Nesting too deep at $"
+ repeat("[0]", MAX_DEPTH) + ": circular reference?");
assertThat(expected)
.hasMessage("Nesting too deep at $" + repeat("[0]", MAX_DEPTH) + ": circular reference?");
}
}
@Test public void deepNestingObjects() throws IOException {
@Test
public void deepNestingObjects() throws IOException {
JsonWriter writer = factory.newWriter();
for (int i = 0; i < MAX_DEPTH; i++) {
writer.beginObject();
@@ -463,11 +497,12 @@ public final class JsonWriterTest {
for (int i = 0; i < MAX_DEPTH; i++) {
writer.endObject();
}
assertThat(factory.json()).isEqualTo(
repeat("{\"a\":", MAX_DEPTH) + "true" + repeat("}", MAX_DEPTH));
assertThat(factory.json())
.isEqualTo(repeat("{\"a\":", MAX_DEPTH) + "true" + repeat("}", MAX_DEPTH));
}
@Test public void tooDeeplyNestingObjects() throws IOException {
@Test
public void tooDeeplyNestingObjects() throws IOException {
JsonWriter writer = factory.newWriter();
for (int i = 0; i < MAX_DEPTH; i++) {
writer.beginObject();
@@ -477,12 +512,13 @@ public final class JsonWriterTest {
writer.beginObject();
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessage("Nesting too deep at $"
+ repeat(".a", MAX_DEPTH) + ": circular reference?");
assertThat(expected)
.hasMessage("Nesting too deep at $" + repeat(".a", MAX_DEPTH) + ": circular reference?");
}
}
@Test public void lenientWriterPermitsMultipleTopLevelValues() throws IOException {
@Test
public void lenientWriterPermitsMultipleTopLevelValues() throws IOException {
assumeTrue(factory.encodesToBytes());
JsonWriter writer = factory.newWriter();
@@ -495,7 +531,8 @@ public final class JsonWriterTest {
assertThat(factory.json()).isEqualTo("[][]");
}
@Test public void strictWriterDoesNotPermitMultipleTopLevelValues() throws IOException {
@Test
public void strictWriterDoesNotPermitMultipleTopLevelValues() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.endArray();
@@ -506,7 +543,8 @@ public final class JsonWriterTest {
}
}
@Test public void closedWriterThrowsOnStructure() throws IOException {
@Test
public void closedWriterThrowsOnStructure() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.endArray();
@@ -533,7 +571,8 @@ public final class JsonWriterTest {
}
}
@Test public void closedWriterThrowsOnName() throws IOException {
@Test
public void closedWriterThrowsOnName() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.endArray();
@@ -545,7 +584,8 @@ public final class JsonWriterTest {
}
}
@Test public void closedWriterThrowsOnValue() throws IOException {
@Test
public void closedWriterThrowsOnValue() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.endArray();
@@ -557,7 +597,8 @@ public final class JsonWriterTest {
}
}
@Test public void closedWriterThrowsOnFlush() throws IOException {
@Test
public void closedWriterThrowsOnFlush() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.endArray();
@@ -569,7 +610,8 @@ public final class JsonWriterTest {
}
}
@Test public void writerCloseIsIdempotent() throws IOException {
@Test
public void writerCloseIsIdempotent() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.endArray();
@@ -577,7 +619,8 @@ public final class JsonWriterTest {
writer.close();
}
@Test public void nameNotInObjectFails() throws IOException {
@Test
public void nameNotInObjectFails() throws IOException {
JsonWriter writer = factory.newWriter();
try {
writer.name("a");
@@ -587,7 +630,8 @@ public final class JsonWriterTest {
}
}
@Test public void missingValueInObjectIsANestingProblem() throws IOException {
@Test
public void missingValueInObjectIsANestingProblem() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.name("a");
@@ -599,7 +643,8 @@ public final class JsonWriterTest {
}
}
@Test public void nameInArrayIsANestingProblem() throws IOException {
@Test
public void nameInArrayIsANestingProblem() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
try {
@@ -610,7 +655,8 @@ public final class JsonWriterTest {
}
}
@Test public void danglingNameFails() throws IOException {
@Test
public void danglingNameFails() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.name("a");
@@ -622,7 +668,8 @@ public final class JsonWriterTest {
}
}
@Test public void streamingValueInObject() throws IOException {
@Test
public void streamingValueInObject() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.name("a");
@@ -637,35 +684,26 @@ public final class JsonWriterTest {
assertThat(factory.json()).isEqualTo("{\"a\":\"ffffffffffffffffsup-1\"}");
}
@Test public void streamingValueInArray() throws IOException {
@Test
public void streamingValueInArray() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.valueSink()
.writeByte('"')
.writeHexadecimalUnsignedLong(-1L)
.writeByte('"')
.close();
writer.valueSink()
.writeByte('"')
.writeUtf8("sup")
.writeByte('"')
.close();
writer.valueSink()
.writeUtf8("-1.0")
.close();
writer.valueSink().writeByte('"').writeHexadecimalUnsignedLong(-1L).writeByte('"').close();
writer.valueSink().writeByte('"').writeUtf8("sup").writeByte('"').close();
writer.valueSink().writeUtf8("-1.0").close();
writer.endArray();
assertThat(factory.json()).isEqualTo("[\"ffffffffffffffff\",\"sup\",-1.0]");
}
@Test public void streamingValueTopLevel() throws IOException {
@Test
public void streamingValueTopLevel() throws IOException {
JsonWriter writer = factory.newWriter();
writer.valueSink()
.writeUtf8("-1.0")
.close();
writer.valueSink().writeUtf8("-1.0").close();
assertThat(factory.json()).isEqualTo("-1.0");
}
@Test public void streamingValueTwiceBeforeCloseFails() throws IOException {
@Test
public void streamingValueTwiceBeforeCloseFails() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.name("a");
@@ -678,7 +716,8 @@ public final class JsonWriterTest {
}
}
@Test public void streamingValueTwiceAfterCloseFails() throws IOException {
@Test
public void streamingValueTwiceAfterCloseFails() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.name("a");
@@ -692,7 +731,8 @@ public final class JsonWriterTest {
}
}
@Test public void streamingValueAndScalarValueFails() throws IOException {
@Test
public void streamingValueAndScalarValueFails() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.name("a");
@@ -705,7 +745,8 @@ public final class JsonWriterTest {
}
}
@Test public void streamingValueAndNameFails() throws IOException {
@Test
public void streamingValueAndNameFails() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.name("a");
@@ -718,7 +759,8 @@ public final class JsonWriterTest {
}
}
@Test public void streamingValueInteractionAfterCloseFails() throws IOException {
@Test
public void streamingValueInteractionAfterCloseFails() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.name("a");
@@ -733,7 +775,8 @@ public final class JsonWriterTest {
}
}
@Test public void streamingValueCloseIsIdempotent() throws IOException {
@Test
public void streamingValueCloseIsIdempotent() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.name("a");
@@ -747,7 +790,8 @@ public final class JsonWriterTest {
sink.close();
}
@Test public void jsonValueTypes() throws IOException {
@Test
public void jsonValueTypes() throws IOException {
JsonWriter writer = factory.newWriter();
writer.setSerializeNulls(true);
@@ -767,21 +811,24 @@ public final class JsonWriterTest {
writer.jsonValue(map);
writer.endArray();
assertThat(factory.json()).isEqualTo("["
+ "null,"
+ "1.1,"
+ "1,"
+ "1,"
+ "true,"
+ "\"one\","
+ "[],"
+ "[1,2,null,3],"
+ "{},"
+ "{\"one\":\"uno\",\"two\":null}"
+ "]");
assertThat(factory.json())
.isEqualTo(
"["
+ "null,"
+ "1.1,"
+ "1,"
+ "1,"
+ "true,"
+ "\"one\","
+ "[],"
+ "[1,2,null,3],"
+ "{},"
+ "{\"one\":\"uno\",\"two\":null}"
+ "]");
}
@Test public void jsonValueIllegalTypes() throws IOException {
@Test
public void jsonValueIllegalTypes() throws IOException {
try {
factory.newWriter().jsonValue(new Object());
fail();
@@ -815,7 +862,8 @@ public final class JsonWriterTest {
}
}
@Test public void promoteStringNameToValue() throws IOException {
@Test
public void promoteStringNameToValue() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -825,7 +873,8 @@ public final class JsonWriterTest {
assertThat(factory.json()).isEqualTo("{\"a\":\"b\"}");
}
@Test public void promoteDoubleNameToValue() throws IOException {
@Test
public void promoteDoubleNameToValue() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -835,7 +884,8 @@ public final class JsonWriterTest {
assertThat(factory.json()).isEqualTo("{\"5.0\":\"b\"}");
}
@Test public void promoteLongNameToValue() throws IOException {
@Test
public void promoteLongNameToValue() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -845,7 +895,8 @@ public final class JsonWriterTest {
assertThat(factory.json()).isEqualTo("{\"5\":\"b\"}");
}
@Test public void promoteNumberNameToValue() throws IOException {
@Test
public void promoteNumberNameToValue() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -855,7 +906,8 @@ public final class JsonWriterTest {
assertThat(factory.json()).isEqualTo("{\"1\":\"b\"}");
}
@Test public void promoteNullNameToValue() throws IOException {
@Test
public void promoteNullNameToValue() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -867,7 +919,8 @@ public final class JsonWriterTest {
}
}
@Test public void promoteBooleanNameToValue() throws IOException {
@Test
public void promoteBooleanNameToValue() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -879,7 +932,8 @@ public final class JsonWriterTest {
}
}
@Test public void promoteNameToValueCannotBeWrittenAsName() throws IOException {
@Test
public void promoteNameToValueCannotBeWrittenAsName() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -891,7 +945,8 @@ public final class JsonWriterTest {
}
}
@Test public void promoteNameToValueAtEndOfObject() throws IOException {
@Test
public void promoteNameToValueAtEndOfObject() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();

View File

@@ -15,6 +15,9 @@
*/
package com.squareup.moshi;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import com.squareup.moshi.LinkedHashTreeMap.AvlBuilder;
import com.squareup.moshi.LinkedHashTreeMap.AvlIterator;
import com.squareup.moshi.LinkedHashTreeMap.Node;
@@ -23,11 +26,9 @@ import java.util.Map;
import java.util.Random;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
public final class LinkedHashTreeMapTest {
@Test public void iterationOrder() {
@Test
public void iterationOrder() {
LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<>();
map.put("a", "android");
map.put("c", "cola");
@@ -36,12 +37,13 @@ public final class LinkedHashTreeMapTest {
assertThat(map.values()).containsExactly("android", "cola", "bbq");
}
@Test public void removeRootDoesNotDoubleUnlink() {
@Test
public void removeRootDoesNotDoubleUnlink() {
LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<>();
map.put("a", "android");
map.put("c", "cola");
map.put("b", "bbq");
Iterator<Map.Entry<String,String>> it = map.entrySet().iterator();
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
it.next();
it.next();
it.next();
@@ -49,7 +51,8 @@ public final class LinkedHashTreeMapTest {
assertThat(map.keySet()).containsExactly("a", "c");
}
@Test public void putNullKeyFails() {
@Test
public void putNullKeyFails() {
LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<>();
try {
map.put(null, "android");
@@ -58,27 +61,32 @@ public final class LinkedHashTreeMapTest {
}
}
@Test public void putNonComparableKeyFails() {
@Test
public void putNonComparableKeyFails() {
LinkedHashTreeMap<Object, String> map = new LinkedHashTreeMap<>();
try {
map.put(new Object(), "android");
fail();
} catch (ClassCastException expected) {}
} catch (ClassCastException expected) {
}
}
@Test public void ContainsNonComparableKeyReturnsFalse() {
@Test
public void ContainsNonComparableKeyReturnsFalse() {
LinkedHashTreeMap<Object, String> map = new LinkedHashTreeMap<>();
map.put("a", "android");
assertThat(map).doesNotContainKey(new Object());
}
@Test public void containsNullKeyIsAlwaysFalse() {
@Test
public void containsNullKeyIsAlwaysFalse() {
LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<>();
map.put("a", "android");
assertThat(map).doesNotContainKey(null);
}
@Test public void putOverrides() throws Exception {
@Test
public void putOverrides() throws Exception {
LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<>();
assertThat(map.put("d", "donut")).isNull();
assertThat(map.put("e", "eclair")).isNull();
@@ -90,7 +98,8 @@ public final class LinkedHashTreeMapTest {
assertThat(map).hasSize(3);
}
@Test public void emptyStringValues() {
@Test
public void emptyStringValues() {
LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<>();
map.put("a", "");
assertThat(map.containsKey("a")).isTrue();
@@ -100,7 +109,8 @@ public final class LinkedHashTreeMapTest {
// NOTE that this does not happen every time, but given the below predictable random,
// this test will consistently fail (assuming the initial size is 16 and rehashing
// size remains at 3/4)
@Test public void forceDoublingAndRehash() throws Exception {
@Test
public void forceDoublingAndRehash() throws Exception {
Random random = new Random(1367593214724L);
LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<>();
String[] keys = new String[1000];
@@ -116,7 +126,8 @@ public final class LinkedHashTreeMapTest {
}
}
@Test public void clear() {
@Test
public void clear() {
LinkedHashTreeMap<String, String> map = new LinkedHashTreeMap<>();
map.put("a", "android");
map.put("c", "cola");
@@ -126,7 +137,8 @@ public final class LinkedHashTreeMapTest {
assertThat(map).isEmpty();
}
@Test public void equalsAndHashCode() throws Exception {
@Test
public void equalsAndHashCode() throws Exception {
LinkedHashTreeMap<String, Integer> map1 = new LinkedHashTreeMap<>();
map1.put("A", 1);
map1.put("B", 2);
@@ -143,17 +155,24 @@ public final class LinkedHashTreeMapTest {
assertThat(map2.hashCode()).isEqualTo(map1.hashCode());
}
@Test public void avlWalker() {
assertAvlWalker(node(node("a"), "b", node("c")),
"a", "b", "c");
assertAvlWalker(node(node(node("a"), "b", node("c")), "d", node(node("e"), "f", node("g"))),
"a", "b", "c", "d", "e", "f", "g");
assertAvlWalker(node(node(null, "a", node("b")), "c", node(node("d"), "e", null)),
"a", "b", "c", "d", "e");
assertAvlWalker(node(null, "a", node(null, "b", node(null, "c", node("d")))),
"a", "b", "c", "d");
assertAvlWalker(node(node(node(node("a"), "b", null), "c", null), "d", null),
"a", "b", "c", "d");
@Test
public void avlWalker() {
assertAvlWalker(node(node("a"), "b", node("c")), "a", "b", "c");
assertAvlWalker(
node(node(node("a"), "b", node("c")), "d", node(node("e"), "f", node("g"))),
"a",
"b",
"c",
"d",
"e",
"f",
"g");
assertAvlWalker(
node(node(null, "a", node("b")), "c", node(node("d"), "e", null)), "a", "b", "c", "d", "e");
assertAvlWalker(
node(null, "a", node(null, "b", node(null, "c", node("d")))), "a", "b", "c", "d");
assertAvlWalker(
node(node(node(node("a"), "b", null), "c", null), "d", null), "a", "b", "c", "d");
}
private void assertAvlWalker(Node<String, String> root, String... values) {
@@ -165,7 +184,8 @@ public final class LinkedHashTreeMapTest {
assertThat(iterator.next()).isNull();
}
@Test public void avlBuilder() {
@Test
public void avlBuilder() {
assertAvlBuilder(1, "a");
assertAvlBuilder(2, "(. a b)");
assertAvlBuilder(3, "(a b c)");
@@ -182,10 +202,14 @@ public final class LinkedHashTreeMapTest {
assertAvlBuilder(14, "(((. a b) c (d e f)) g ((h i j) k (l m n)))");
assertAvlBuilder(15, "(((a b c) d (e f g)) h ((i j k) l (m n o)))");
assertAvlBuilder(16, "(((a b c) d (e f g)) h ((i j k) l (m n (. o p))))");
assertAvlBuilder(30, "((((. a b) c (d e f)) g ((h i j) k (l m n))) o "
+ "(((p q r) s (t u v)) w ((x y z) A (B C D))))");
assertAvlBuilder(31, "((((a b c) d (e f g)) h ((i j k) l (m n o))) p "
+ "(((q r s) t (u v w)) x ((y z A) B (C D E))))");
assertAvlBuilder(
30,
"((((. a b) c (d e f)) g ((h i j) k (l m n))) o "
+ "(((p q r) s (t u v)) w ((x y z) A (B C D))))");
assertAvlBuilder(
31,
"((((a b c) d (e f g)) h ((i j k) l (m n o))) p "
+ "(((q r s) t (u v w)) x ((y z A) B (C D E))))");
}
private void assertAvlBuilder(int size, String expected) {
@@ -198,7 +222,8 @@ public final class LinkedHashTreeMapTest {
assertTree(expected, avlBuilder.root());
}
@Test public void doubleCapacity() {
@Test
public void doubleCapacity() {
@SuppressWarnings("unchecked") // Arrays and generics don't get along.
Node<String, String>[] oldTable = new Node[1];
oldTable[0] = node(node(node("a"), "b", node("c")), "d", node(node("e"), "f", node("g")));
@@ -208,9 +233,10 @@ public final class LinkedHashTreeMapTest {
assertTree("(a c (. e g))", newTable[1]); // Odd hash codes!
}
@Test public void doubleCapacityAllNodesOnLeft() {
@Test
public void doubleCapacityAllNodesOnLeft() {
@SuppressWarnings("unchecked") // Arrays and generics don't get along.
Node<String, String>[] oldTable = new Node[1];
Node<String, String>[] oldTable = new Node[1];
oldTable[0] = node(node("b"), "d", node("f"));
Node<String, String>[] newTable = LinkedHashTreeMap.doubleCapacity(oldTable);
@@ -230,8 +256,8 @@ public final class LinkedHashTreeMapTest {
return new Node<>(null, value, value.hashCode(), head, head);
}
private Node<String, String> node(Node<String, String> left, String value,
Node<String, String> right) {
private Node<String, String> node(
Node<String, String> left, String value, Node<String, String> right) {
Node<String, String> result = node(value);
if (left != null) {
result.left = left;

View File

@@ -15,6 +15,11 @@
*/
package com.squareup.moshi;
import static com.squareup.moshi.TestUtil.newReader;
import static com.squareup.moshi.internal.Util.NO_ANNOTATIONS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.AbstractMap.SimpleEntry;
@@ -25,15 +30,11 @@ import java.util.Map;
import okio.Buffer;
import org.junit.Test;
import static com.squareup.moshi.TestUtil.newReader;
import static com.squareup.moshi.internal.Util.NO_ANNOTATIONS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
public final class MapJsonAdapterTest {
private final Moshi moshi = new Moshi.Builder().build();
@Test public void map() throws Exception {
@Test
public void map() throws Exception {
Map<String, Boolean> map = new LinkedHashMap<>();
map.put("a", true);
map.put("b", false);
@@ -42,15 +43,17 @@ public final class MapJsonAdapterTest {
String toJson = toJson(String.class, Boolean.class, map);
assertThat(toJson).isEqualTo("{\"a\":true,\"b\":false,\"c\":null}");
Map<String, Boolean> fromJson = fromJson(
String.class, Boolean.class, "{\"a\":true,\"b\":false,\"c\":null}");
assertThat(fromJson).containsExactly(
new SimpleEntry<String, Boolean>("a", true),
new SimpleEntry<String, Boolean>("b", false),
new SimpleEntry<String, Boolean>("c", null));
Map<String, Boolean> fromJson =
fromJson(String.class, Boolean.class, "{\"a\":true,\"b\":false,\"c\":null}");
assertThat(fromJson)
.containsExactly(
new SimpleEntry<String, Boolean>("a", true),
new SimpleEntry<String, Boolean>("b", false),
new SimpleEntry<String, Boolean>("c", null));
}
@Test public void mapWithNullKeyFailsToEmit() throws Exception {
@Test
public void mapWithNullKeyFailsToEmit() throws Exception {
Map<String, Boolean> map = new LinkedHashMap<>();
map.put(null, true);
@@ -62,7 +65,8 @@ public final class MapJsonAdapterTest {
}
}
@Test public void emptyMap() throws Exception {
@Test
public void emptyMap() throws Exception {
Map<String, Boolean> map = new LinkedHashMap<>();
String toJson = toJson(String.class, Boolean.class, map);
@@ -72,7 +76,8 @@ public final class MapJsonAdapterTest {
assertThat(fromJson).isEmpty();
}
@Test public void nullMap() throws Exception {
@Test
public void nullMap() throws Exception {
JsonAdapter<?> jsonAdapter = mapAdapter(String.class, Boolean.class);
Buffer buffer = new Buffer();
@@ -86,7 +91,8 @@ public final class MapJsonAdapterTest {
assertThat(jsonAdapter.fromJson(jsonReader)).isEqualTo(null);
}
@Test public void covariantValue() throws Exception {
@Test
public void covariantValue() throws Exception {
// Important for Kotlin maps, which are all Map<K, ? extends T>.
JsonAdapter<Map<String, Object>> jsonAdapter =
mapAdapter(String.class, Types.subtypeOf(Object.class));
@@ -107,7 +113,8 @@ public final class MapJsonAdapterTest {
assertThat(jsonAdapter.fromJson(jsonReader)).isEqualTo(map);
}
@Test public void orderIsRetained() throws Exception {
@Test
public void orderIsRetained() throws Exception {
Map<String, Integer> map = new LinkedHashMap<>();
map.put("c", 1);
map.put("a", 2);
@@ -117,13 +124,14 @@ public final class MapJsonAdapterTest {
String toJson = toJson(String.class, Integer.class, map);
assertThat(toJson).isEqualTo("{\"c\":1,\"a\":2,\"d\":3,\"b\":4}");
Map<String, Integer> fromJson = fromJson(
String.class, Integer.class, "{\"c\":1,\"a\":2,\"d\":3,\"b\":4}");
Map<String, Integer> fromJson =
fromJson(String.class, Integer.class, "{\"c\":1,\"a\":2,\"d\":3,\"b\":4}");
assertThat(new ArrayList<Object>(fromJson.keySet()))
.isEqualTo(Arrays.asList("c", "a", "d", "b"));
}
@Test public void duplicatesAreForbidden() throws Exception {
@Test
public void duplicatesAreForbidden() throws Exception {
try {
fromJson(String.class, Integer.class, "{\"c\":1,\"c\":2}");
fail();
@@ -133,7 +141,8 @@ public final class MapJsonAdapterTest {
}
/** This leans on {@code promoteNameToValue} to do the heavy lifting. */
@Test public void mapWithNonStringKeys() throws Exception {
@Test
public void mapWithNonStringKeys() throws Exception {
Map<Integer, Boolean> map = new LinkedHashMap<>();
map.put(5, true);
map.put(6, false);
@@ -142,15 +151,17 @@ public final class MapJsonAdapterTest {
String toJson = toJson(Integer.class, Boolean.class, map);
assertThat(toJson).isEqualTo("{\"5\":true,\"6\":false,\"7\":null}");
Map<Integer, Boolean> fromJson = fromJson(
Integer.class, Boolean.class, "{\"5\":true,\"6\":false,\"7\":null}");
assertThat(fromJson).containsExactly(
new SimpleEntry<Integer, Boolean>(5, true),
new SimpleEntry<Integer, Boolean>(6, false),
new SimpleEntry<Integer, Boolean>(7, null));
Map<Integer, Boolean> fromJson =
fromJson(Integer.class, Boolean.class, "{\"5\":true,\"6\":false,\"7\":null}");
assertThat(fromJson)
.containsExactly(
new SimpleEntry<Integer, Boolean>(5, true),
new SimpleEntry<Integer, Boolean>(6, false),
new SimpleEntry<Integer, Boolean>(7, null));
}
@Test public void mapWithNonStringKeysToJsonObject() {
@Test
public void mapWithNonStringKeysToJsonObject() {
Map<Integer, Boolean> map = new LinkedHashMap<>();
map.put(5, true);
map.put(6, false);
@@ -166,7 +177,8 @@ public final class MapJsonAdapterTest {
assertThat(jsonAdapter.fromJsonValue(jsonObject)).isEqualTo(map);
}
@Test public void booleanKeyTypeHasCoherentErrorMessage() {
@Test
public void booleanKeyTypeHasCoherentErrorMessage() {
Map<Boolean, String> map = new LinkedHashMap<>();
map.put(true, "");
JsonAdapter<Map<Boolean, String>> adapter = mapAdapter(Boolean.class, String.class);
@@ -184,10 +196,10 @@ public final class MapJsonAdapterTest {
}
}
static final class Key {
}
static final class Key {}
@Test public void objectKeyTypeHasCoherentErrorMessage() {
@Test
public void objectKeyTypeHasCoherentErrorMessage() {
Map<Key, String> map = new LinkedHashMap<>();
map.put(new Key(), "");
JsonAdapter<Map<Key, String>> adapter = mapAdapter(Key.class, String.class);
@@ -201,12 +213,12 @@ public final class MapJsonAdapterTest {
adapter.toJsonValue(map);
fail();
} catch (IllegalStateException expected) {
assertThat(expected).hasMessage("Object cannot be "
+ "used as a map key in JSON at path $.");
assertThat(expected).hasMessage("Object cannot be " + "used as a map key in JSON at path $.");
}
}
@Test public void arrayKeyTypeHasCoherentErrorMessage() {
@Test
public void arrayKeyTypeHasCoherentErrorMessage() {
Map<String[], String> map = new LinkedHashMap<>();
map.put(new String[0], "");
JsonAdapter<Map<String[], String>> adapter =
@@ -236,8 +248,9 @@ public final class MapJsonAdapterTest {
@SuppressWarnings("unchecked") // It's the caller's responsibility to make sure K and V match.
private <K, V> JsonAdapter<Map<K, V>> mapAdapter(Type keyType, Type valueType) {
return (JsonAdapter<Map<K, V>>) MapJsonAdapter.FACTORY.create(
Types.newParameterizedType(Map.class, keyType, valueType), NO_ANNOTATIONS, moshi);
return (JsonAdapter<Map<K, V>>)
MapJsonAdapter.FACTORY.create(
Types.newParameterizedType(Map.class, keyType, valueType), NO_ANNOTATIONS, moshi);
}
private <K, V> Map<K, V> fromJson(Type keyType, Type valueType, String json) throws IOException {

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,10 @@
*/
package com.squareup.moshi;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
@@ -35,15 +39,11 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import org.junit.Ignore;
import org.junit.Test;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
public final class ObjectAdapterTest {
@Test public void toJsonUsesRuntimeType() {
@Test
public void toJsonUsesRuntimeType() {
Delivery delivery = new Delivery();
delivery.address = "1455 Market St.";
Pizza pizza = new Pizza();
@@ -53,22 +53,26 @@ public final class ObjectAdapterTest {
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Object> adapter = moshi.adapter(Object.class);
assertThat(adapter.toJson(delivery)).isEqualTo("{"
+ "\"address\":\"1455 Market St.\","
+ "\"items\":["
+ "{\"diameter\":12,\"extraCheese\":true},"
+ "\"Pepsi\""
+ "]"
+ "}");
assertThat(adapter.toJson(delivery))
.isEqualTo(
"{"
+ "\"address\":\"1455 Market St.\","
+ "\"items\":["
+ "{\"diameter\":12,\"extraCheese\":true},"
+ "\"Pepsi\""
+ "]"
+ "}");
}
@Test public void toJsonJavaLangObject() {
@Test
public void toJsonJavaLangObject() {
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Object> adapter = moshi.adapter(Object.class);
assertThat(adapter.toJson(new Object())).isEqualTo("{}");
}
@Test public void fromJsonReturnsMapsAndLists() throws Exception {
@Test
public void fromJsonReturnsMapsAndLists() throws Exception {
Map<Object, Object> delivery = new LinkedHashMap<>();
delivery.put("address", "1455 Market St.");
Map<Object, Object> pizza = new LinkedHashMap<>();
@@ -78,102 +82,126 @@ public final class ObjectAdapterTest {
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Object> adapter = moshi.adapter(Object.class);
assertThat(adapter.fromJson("{"
+ "\"address\":\"1455 Market St.\","
+ "\"items\":["
+ "{\"diameter\":12,\"extraCheese\":true},"
+ "\"Pepsi\""
+ "]"
+ "}")).isEqualTo(delivery);
assertThat(
adapter.fromJson(
"{"
+ "\"address\":\"1455 Market St.\","
+ "\"items\":["
+ "{\"diameter\":12,\"extraCheese\":true},"
+ "\"Pepsi\""
+ "]"
+ "}"))
.isEqualTo(delivery);
}
@Test public void fromJsonUsesDoublesForNumbers() throws Exception {
@Test
public void fromJsonUsesDoublesForNumbers() throws Exception {
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Object> adapter = moshi.adapter(Object.class);
assertThat(adapter.fromJson("[0, 1]")).isEqualTo(Arrays.asList(0d, 1d));
}
@Test public void fromJsonDoesNotFailOnNullValues() throws Exception {
@Test
public void fromJsonDoesNotFailOnNullValues() throws Exception {
Map<Object, Object> emptyDelivery = new LinkedHashMap<>();
emptyDelivery.put("address", null);
emptyDelivery.put("items", null);
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Object> adapter = moshi.adapter(Object.class);
assertThat(adapter.fromJson("{\"address\":null, \"items\":null}"))
.isEqualTo(emptyDelivery);
assertThat(adapter.fromJson("{\"address\":null, \"items\":null}")).isEqualTo(emptyDelivery);
}
@Test public void toJsonCoercesRuntimeTypeForCollections() {
Collection<String> collection = new AbstractCollection<String>() {
@Override public Iterator<String> iterator() {
return Collections.singleton("A").iterator();
}
@Override public int size() {
return 1;
}
};
@Test
public void toJsonCoercesRuntimeTypeForCollections() {
Collection<String> collection =
new AbstractCollection<String>() {
@Override
public Iterator<String> iterator() {
return Collections.singleton("A").iterator();
}
@Override
public int size() {
return 1;
}
};
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Object> adapter = moshi.adapter(Object.class);
assertThat(adapter.toJson(collection)).isEqualTo("[\"A\"]");
}
@Test public void toJsonCoercesRuntimeTypeForLists() {
List<String> list = new AbstractList<String>() {
@Override public String get(int i) {
return "A";
}
@Test
public void toJsonCoercesRuntimeTypeForLists() {
List<String> list =
new AbstractList<String>() {
@Override
public String get(int i) {
return "A";
}
@Override public int size() {
return 1;
}
};
@Override
public int size() {
return 1;
}
};
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Object> adapter = moshi.adapter(Object.class);
assertThat(adapter.toJson(list)).isEqualTo("[\"A\"]");
}
@Test public void toJsonCoercesRuntimeTypeForSets() {
Set<String> set = new AbstractSet<String>() {
@Override public Iterator<String> iterator() {
return Collections.singleton("A").iterator();
}
@Override public int size() {
return 1;
}
};
@Test
public void toJsonCoercesRuntimeTypeForSets() {
Set<String> set =
new AbstractSet<String>() {
@Override
public Iterator<String> iterator() {
return Collections.singleton("A").iterator();
}
@Override
public int size() {
return 1;
}
};
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Object> adapter = moshi.adapter(Object.class);
assertThat(adapter.toJson(set)).isEqualTo("[\"A\"]");
}
@Test public void toJsonCoercesRuntimeTypeForMaps() {
Map<String, Boolean> map = new AbstractMap<String, Boolean>() {
@Override public Set<Entry<String, Boolean>> entrySet() {
return Collections.singletonMap("A", true).entrySet();
}
};
@Test
public void toJsonCoercesRuntimeTypeForMaps() {
Map<String, Boolean> map =
new AbstractMap<String, Boolean>() {
@Override
public Set<Entry<String, Boolean>> entrySet() {
return Collections.singletonMap("A", true).entrySet();
}
};
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Object> adapter = moshi.adapter(Object.class);
assertThat(adapter.toJson(map)).isEqualTo("{\"A\":true}");
}
@Test public void toJsonUsesTypeAdapters() {
Object dateAdapter = new Object() {
@ToJson Long dateToJson(Date d) {
return d.getTime();
}
@FromJson Date dateFromJson(Long millis) {
return new Date(millis);
}
};
Moshi moshi = new Moshi.Builder()
.add(dateAdapter)
.build();
@Test
public void toJsonUsesTypeAdapters() {
Object dateAdapter =
new Object() {
@ToJson
Long dateToJson(Date d) {
return d.getTime();
}
@FromJson
Date dateFromJson(Long millis) {
return new Date(millis);
}
};
Moshi moshi = new Moshi.Builder().add(dateAdapter).build();
JsonAdapter<Object> adapter = moshi.adapter(Object.class);
assertThat(adapter.toJson(Arrays.asList(new Date(1), new Date(2)))).isEqualTo("[1,2]");
}
@@ -182,23 +210,25 @@ public final class ObjectAdapterTest {
* Confirm that the built-in adapter for Object delegates to user-supplied adapters for JSON value
* types like strings.
*/
@Test public void objectAdapterDelegatesStringNamesAndValues() throws Exception {
JsonAdapter<String> stringAdapter = new JsonAdapter<String>() {
@Override public String fromJson(JsonReader reader) throws IOException {
return reader.nextString().toUpperCase(Locale.US);
}
@Test
public void objectAdapterDelegatesStringNamesAndValues() throws Exception {
JsonAdapter<String> stringAdapter =
new JsonAdapter<String>() {
@Override
public String fromJson(JsonReader reader) throws IOException {
return reader.nextString().toUpperCase(Locale.US);
}
@Override public void toJson(JsonWriter writer, @Nullable String value) {
throw new UnsupportedOperationException();
}
};
@Override
public void toJson(JsonWriter writer, @Nullable String value) {
throw new UnsupportedOperationException();
}
};
Moshi moshi = new Moshi.Builder()
.add(String.class, stringAdapter)
.build();
Moshi moshi = new Moshi.Builder().add(String.class, stringAdapter).build();
JsonAdapter<Object> objectAdapter = moshi.adapter(Object.class);
Map<String, String> value
= (Map<String, String>) objectAdapter.fromJson("{\"a\":\"b\", \"c\":\"d\"}");
Map<String, String> value =
(Map<String, String>) objectAdapter.fromJson("{\"a\":\"b\", \"c\":\"d\"}");
assertThat(value).containsExactly(new SimpleEntry<>("A", "B"), new SimpleEntry<>("C", "D"));
}
@@ -206,75 +236,87 @@ public final class ObjectAdapterTest {
* Confirm that the built-in adapter for Object delegates to any user-supplied adapters for
* Object. This is necessary to customize adapters for primitives like numbers.
*/
@Test public void objectAdapterDelegatesObjects() throws Exception {
JsonAdapter.Factory objectFactory = new JsonAdapter.Factory() {
@Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (type != Object.class) return null;
@Test
public void objectAdapterDelegatesObjects() throws Exception {
JsonAdapter.Factory objectFactory =
new JsonAdapter.Factory() {
@Override
public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (type != Object.class) return null;
final JsonAdapter<Object> delegate = moshi.nextAdapter(this, Object.class, annotations);
return new JsonAdapter<Object>() {
@Override public @Nullable Object fromJson(JsonReader reader) throws IOException {
if (reader.peek() != JsonReader.Token.NUMBER) {
return delegate.fromJson(reader);
} else {
return new BigDecimal(reader.nextString());
}
}
final JsonAdapter<Object> delegate = moshi.nextAdapter(this, Object.class, annotations);
return new JsonAdapter<Object>() {
@Override
public @Nullable Object fromJson(JsonReader reader) throws IOException {
if (reader.peek() != JsonReader.Token.NUMBER) {
return delegate.fromJson(reader);
} else {
return new BigDecimal(reader.nextString());
}
}
@Override public void toJson(JsonWriter writer, @Nullable Object value) {
throw new UnsupportedOperationException();
@Override
public void toJson(JsonWriter writer, @Nullable Object value) {
throw new UnsupportedOperationException();
}
};
}
};
}
};
Moshi moshi = new Moshi.Builder()
.add(objectFactory)
.build();
Moshi moshi = new Moshi.Builder().add(objectFactory).build();
JsonAdapter<Object> objectAdapter = moshi.adapter(Object.class);
List<?> value = (List<?>) objectAdapter.fromJson("[0, 1, 2.0, 3.14]");
assertThat(value).isEqualTo(Arrays.asList(new BigDecimal("0"), new BigDecimal("1"),
new BigDecimal("2.0"), new BigDecimal("3.14")));
assertThat(value)
.isEqualTo(
Arrays.asList(
new BigDecimal("0"),
new BigDecimal("1"),
new BigDecimal("2.0"),
new BigDecimal("3.14")));
}
/** Confirm that the built-in adapter for Object delegates to user-supplied adapters for lists. */
@Test public void objectAdapterDelegatesLists() throws Exception {
JsonAdapter<List<?>> listAdapter = new JsonAdapter<List<?>>() {
@Override public List<?> fromJson(JsonReader reader) throws IOException {
reader.skipValue();
return singletonList("z");
}
@Test
public void objectAdapterDelegatesLists() throws Exception {
JsonAdapter<List<?>> listAdapter =
new JsonAdapter<List<?>>() {
@Override
public List<?> fromJson(JsonReader reader) throws IOException {
reader.skipValue();
return singletonList("z");
}
@Override public void toJson(JsonWriter writer, @Nullable List<?> value) {
throw new UnsupportedOperationException();
}
};
@Override
public void toJson(JsonWriter writer, @Nullable List<?> value) {
throw new UnsupportedOperationException();
}
};
Moshi moshi = new Moshi.Builder()
.add(List.class, listAdapter)
.build();
Moshi moshi = new Moshi.Builder().add(List.class, listAdapter).build();
JsonAdapter<Object> objectAdapter = moshi.adapter(Object.class);
Map<?, ?> mapOfList = (Map<?, ?>) objectAdapter.fromJson("{\"a\":[\"b\"]}");
assertThat(mapOfList).isEqualTo(singletonMap("a", singletonList("z")));
}
/** Confirm that the built-in adapter for Object delegates to user-supplied adapters for maps. */
@Test public void objectAdapterDelegatesMaps() throws Exception {
JsonAdapter<Map<?, ?>> mapAdapter = new JsonAdapter<Map<?, ?>>() {
@Override public Map<?, ?> fromJson(JsonReader reader) throws IOException {
reader.skipValue();
return singletonMap("x", "y");
}
@Test
public void objectAdapterDelegatesMaps() throws Exception {
JsonAdapter<Map<?, ?>> mapAdapter =
new JsonAdapter<Map<?, ?>>() {
@Override
public Map<?, ?> fromJson(JsonReader reader) throws IOException {
reader.skipValue();
return singletonMap("x", "y");
}
@Override public void toJson(JsonWriter writer, @Nullable Map<?, ?> value) {
throw new UnsupportedOperationException();
}
};
@Override
public void toJson(JsonWriter writer, @Nullable Map<?, ?> value) {
throw new UnsupportedOperationException();
}
};
Moshi moshi = new Moshi.Builder()
.add(Map.class, mapAdapter)
.build();
Moshi moshi = new Moshi.Builder().add(Map.class, mapAdapter).build();
JsonAdapter<Object> objectAdapter = moshi.adapter(Object.class);
List<?> listOfMap = (List<?>) objectAdapter.fromJson("[{\"b\":\"c\"}]");
assertThat(listOfMap).isEqualTo(singletonList(singletonMap("x", "y")));

View File

@@ -15,6 +15,9 @@
*/
package com.squareup.moshi;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import java.util.List;
import okio.Buffer;
import org.junit.Test;
@@ -23,9 +26,6 @@ import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
@RunWith(Parameterized.class)
public final class PromoteNameToValueTest {
@Parameter public JsonCodecFactory factory;
@@ -35,7 +35,8 @@ public final class PromoteNameToValueTest {
return JsonCodecFactory.factories();
}
@Test public void readerStringValue() throws Exception {
@Test
public void readerStringValue() throws Exception {
JsonReader reader = factory.newReader("{\"a\":1}");
reader.beginObject();
reader.promoteNameToValue();
@@ -49,7 +50,8 @@ public final class PromoteNameToValueTest {
assertThat(reader.getPath()).isEqualTo("$");
}
@Test public void readerIntegerValue() throws Exception {
@Test
public void readerIntegerValue() throws Exception {
JsonReader reader = factory.newReader("{\"5\":1}");
reader.beginObject();
reader.promoteNameToValue();
@@ -63,7 +65,8 @@ public final class PromoteNameToValueTest {
assertThat(reader.getPath()).isEqualTo("$");
}
@Test public void readerDoubleValue() throws Exception {
@Test
public void readerDoubleValue() throws Exception {
JsonReader reader = factory.newReader("{\"5.5\":1}");
reader.beginObject();
reader.promoteNameToValue();
@@ -77,7 +80,8 @@ public final class PromoteNameToValueTest {
assertThat(reader.getPath()).isEqualTo("$");
}
@Test public void readerBooleanValue() throws Exception {
@Test
public void readerBooleanValue() throws Exception {
JsonReader reader = factory.newReader("{\"true\":1}");
reader.beginObject();
reader.promoteNameToValue();
@@ -87,9 +91,10 @@ public final class PromoteNameToValueTest {
reader.nextBoolean();
fail();
} catch (JsonDataException e) {
assertThat(e.getMessage()).isIn(
"Expected BOOLEAN but was true, a java.lang.String, at path $.true",
"Expected a boolean but was STRING at path $.true");
assertThat(e.getMessage())
.isIn(
"Expected BOOLEAN but was true, a java.lang.String, at path $.true",
"Expected a boolean but was STRING at path $.true");
}
assertThat(reader.getPath()).isEqualTo("$.true");
assertThat(reader.nextString()).isEqualTo("true");
@@ -99,7 +104,8 @@ public final class PromoteNameToValueTest {
assertThat(reader.getPath()).isEqualTo("$");
}
@Test public void readerLongValue() throws Exception {
@Test
public void readerLongValue() throws Exception {
JsonReader reader = factory.newReader("{\"5\":1}");
reader.beginObject();
reader.promoteNameToValue();
@@ -113,7 +119,8 @@ public final class PromoteNameToValueTest {
assertThat(reader.getPath()).isEqualTo("$");
}
@Test public void readerNullValue() throws Exception {
@Test
public void readerNullValue() throws Exception {
JsonReader reader = factory.newReader("{\"null\":1}");
reader.beginObject();
reader.promoteNameToValue();
@@ -123,9 +130,10 @@ public final class PromoteNameToValueTest {
reader.nextNull();
fail();
} catch (JsonDataException e) {
assertThat(e.getMessage()).isIn(
"Expected NULL but was null, a java.lang.String, at path $.null",
"Expected null but was STRING at path $.null");
assertThat(e.getMessage())
.isIn(
"Expected NULL but was null, a java.lang.String, at path $.null",
"Expected null but was STRING at path $.null");
}
assertThat(reader.nextString()).isEqualTo("null");
assertThat(reader.getPath()).isEqualTo("$.null");
@@ -135,7 +143,8 @@ public final class PromoteNameToValueTest {
assertThat(reader.getPath()).isEqualTo("$");
}
@Test public void readerMultipleValueObject() throws Exception {
@Test
public void readerMultipleValueObject() throws Exception {
JsonReader reader = factory.newReader("{\"a\":1,\"b\":2}");
reader.beginObject();
assertThat(reader.nextName()).isEqualTo("a");
@@ -151,7 +160,8 @@ public final class PromoteNameToValueTest {
assertThat(reader.getPath()).isEqualTo("$");
}
@Test public void readerEmptyValueObject() throws Exception {
@Test
public void readerEmptyValueObject() throws Exception {
JsonReader reader = factory.newReader("{}");
reader.beginObject();
assertThat(reader.peek()).isEqualTo(JsonReader.Token.END_OBJECT);
@@ -161,7 +171,8 @@ public final class PromoteNameToValueTest {
assertThat(reader.getPath()).isEqualTo("$");
}
@Test public void readerUnusedPromotionDoesntPersist() throws Exception {
@Test
public void readerUnusedPromotionDoesntPersist() throws Exception {
JsonReader reader = factory.newReader("[{},{\"a\":5}]");
reader.beginArray();
reader.beginObject();
@@ -176,7 +187,8 @@ public final class PromoteNameToValueTest {
assertThat(reader.nextName()).isEqualTo("a");
}
@Test public void readerUnquotedIntegerValue() throws Exception {
@Test
public void readerUnquotedIntegerValue() throws Exception {
JsonReader reader = factory.newReader("{5:1}");
reader.setLenient(true);
reader.beginObject();
@@ -186,7 +198,8 @@ public final class PromoteNameToValueTest {
reader.endObject();
}
@Test public void readerUnquotedLongValue() throws Exception {
@Test
public void readerUnquotedLongValue() throws Exception {
JsonReader reader = factory.newReader("{5:1}");
reader.setLenient(true);
reader.beginObject();
@@ -196,7 +209,8 @@ public final class PromoteNameToValueTest {
reader.endObject();
}
@Test public void readerUnquotedDoubleValue() throws Exception {
@Test
public void readerUnquotedDoubleValue() throws Exception {
JsonReader reader = factory.newReader("{5:1}");
reader.setLenient(true);
reader.beginObject();
@@ -206,7 +220,8 @@ public final class PromoteNameToValueTest {
reader.endObject();
}
@Test public void writerStringValue() throws Exception {
@Test
public void writerStringValue() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -219,7 +234,8 @@ public final class PromoteNameToValueTest {
assertThat(factory.json()).isEqualTo("{\"a\":1}");
}
@Test public void writerIntegerValue() throws Exception {
@Test
public void writerIntegerValue() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -232,7 +248,8 @@ public final class PromoteNameToValueTest {
assertThat(factory.json()).isEqualTo("{\"5\":1}");
}
@Test public void writerDoubleValue() throws Exception {
@Test
public void writerDoubleValue() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -245,7 +262,8 @@ public final class PromoteNameToValueTest {
assertThat(factory.json()).isEqualTo("{\"5.5\":1}");
}
@Test public void writerBooleanValue() throws Exception {
@Test
public void writerBooleanValue() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -263,7 +281,8 @@ public final class PromoteNameToValueTest {
assertThat(factory.json()).isEqualTo("{\"true\":1}");
}
@Test public void writerLongValue() throws Exception {
@Test
public void writerLongValue() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -276,7 +295,8 @@ public final class PromoteNameToValueTest {
assertThat(factory.json()).isEqualTo("{\"5\":1}");
}
@Test public void writerNullValue() throws Exception {
@Test
public void writerNullValue() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -295,7 +315,8 @@ public final class PromoteNameToValueTest {
assertThat(factory.json()).isEqualTo("{\"null\":1}");
}
@Test public void writerMultipleValueObject() throws Exception {
@Test
public void writerMultipleValueObject() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.name("a");
@@ -310,7 +331,8 @@ public final class PromoteNameToValueTest {
assertThat(factory.json()).isEqualTo("{\"a\":1,\"b\":2}");
}
@Test public void writerEmptyValueObject() throws Exception {
@Test
public void writerEmptyValueObject() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -320,7 +342,8 @@ public final class PromoteNameToValueTest {
assertThat(factory.json()).isEqualTo("{}");
}
@Test public void writerUnusedPromotionDoesntPersist() throws Exception {
@Test
public void writerUnusedPromotionDoesntPersist() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.beginObject();
@@ -335,7 +358,8 @@ public final class PromoteNameToValueTest {
writer.name("a");
}
@Test public void writerSourceValueFails() throws Exception {
@Test
public void writerSourceValueFails() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -343,8 +367,8 @@ public final class PromoteNameToValueTest {
writer.value(new Buffer().writeUtf8("\"a\""));
fail();
} catch (IllegalStateException expected) {
assertThat(expected).hasMessage(
"BufferedSource cannot be used as a map key in JSON at path $.");
assertThat(expected)
.hasMessage("BufferedSource cannot be used as a map key in JSON at path $.");
}
writer.value("a");
writer.value("a value");
@@ -352,7 +376,8 @@ public final class PromoteNameToValueTest {
assertThat(factory.json()).isEqualTo("{\"a\":\"a value\"}");
}
@Test public void writerValueSinkFails() throws Exception {
@Test
public void writerValueSinkFails() throws Exception {
JsonWriter writer = factory.newWriter();
writer.beginObject();
writer.promoteValueToName();
@@ -360,8 +385,8 @@ public final class PromoteNameToValueTest {
writer.valueSink();
fail();
} catch (IllegalStateException expected) {
assertThat(expected).hasMessage(
"BufferedSink cannot be used as a map key in JSON at path $.");
assertThat(expected)
.hasMessage("BufferedSink cannot be used as a map key in JSON at path $.");
}
writer.value("a");
writer.value("a value");

View File

@@ -16,21 +16,22 @@
package com.squareup.moshi;
import com.squareup.moshi.internal.Util;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import com.squareup.moshi.internal.Util;
import org.junit.Test;
/**
* Test fixes for infinite recursion on {@link Util#resolve(java.lang.reflect.Type, Class,
* java.lang.reflect.Type)}, described at <a href="https://github.com/google/gson/issues/440">Issue #440</a>
* and similar issues.
* <p>
* These tests originally caused {@link StackOverflowError} because of infinite recursion on attempts to
* resolve generics on types, with an intermediate types like 'Foo2&lt;? extends ? super ? extends ... ? extends A&gt;'
* <p>
* Adapted from https://github.com/google/gson/commit/a300148003e3a067875b1444e8268b6e0f0e0e02 in
* java.lang.reflect.Type)}, described at <a href="https://github.com/google/gson/issues/440">Issue
* #440</a> and similar issues.
*
* <p>These tests originally caused {@link StackOverflowError} because of infinite recursion on
* attempts to resolve generics on types, with an intermediate types like 'Foo2&lt;? extends ? super
* ? extends ... ? extends A&gt;'
*
* <p>Adapted from https://github.com/google/gson/commit/a300148003e3a067875b1444e8268b6e0f0e0e02 in
* service of https://github.com/square/moshi/issues/338.
*/
public final class RecursiveTypesResolveTest {
@@ -43,10 +44,9 @@ public final class RecursiveTypesResolveTest {
public Foo1<? super B> foo1;
}
/**
* Test simplest case of recursion.
*/
@Test public void recursiveResolveSimple() {
/** Test simplest case of recursion. */
@Test
public void recursiveResolveSimple() {
JsonAdapter<Foo1> adapter = new Moshi.Builder().build().adapter(Foo1.class);
assertNotNull(adapter);
}
@@ -55,23 +55,24 @@ public final class RecursiveTypesResolveTest {
// Tests belows check the behaviour of the methods changed for the fix
//
@Test public void doubleSupertype() {
assertEquals(Types.supertypeOf(Number.class),
Types.supertypeOf(Types.supertypeOf(Number.class)));
@Test
public void doubleSupertype() {
assertEquals(
Types.supertypeOf(Number.class), Types.supertypeOf(Types.supertypeOf(Number.class)));
}
@Test public void doubleSubtype() {
assertEquals(Types.subtypeOf(Number.class),
Types.subtypeOf(Types.subtypeOf(Number.class)));
@Test
public void doubleSubtype() {
assertEquals(Types.subtypeOf(Number.class), Types.subtypeOf(Types.subtypeOf(Number.class)));
}
@Test public void superSubtype() {
assertEquals(Types.subtypeOf(Object.class),
Types.supertypeOf(Types.subtypeOf(Number.class)));
@Test
public void superSubtype() {
assertEquals(Types.subtypeOf(Object.class), Types.supertypeOf(Types.subtypeOf(Number.class)));
}
@Test public void subSupertype() {
assertEquals(Types.subtypeOf(Object.class),
Types.subtypeOf(Types.supertypeOf(Number.class)));
@Test
public void subSupertype() {
assertEquals(Types.subtypeOf(Object.class), Types.subtypeOf(Types.supertypeOf(Number.class)));
}
}

View File

@@ -15,6 +15,12 @@
*/
package com.squareup.moshi;
import static com.squareup.moshi.internal.Util.canonicalize;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@@ -29,43 +35,43 @@ import java.util.Properties;
import java.util.Set;
import org.junit.Test;
import static com.squareup.moshi.internal.Util.canonicalize;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
public final class TypesTest {
@Retention(RUNTIME) @JsonQualifier @interface TestQualifier {
}
@Retention(RUNTIME)
@JsonQualifier
@interface TestQualifier {}
@Retention(RUNTIME) @JsonQualifier @interface AnotherTestQualifier {
}
@Retention(RUNTIME)
@JsonQualifier
@interface AnotherTestQualifier {}
@Retention(RUNTIME) @interface TestAnnotation {
}
@Retention(RUNTIME)
@interface TestAnnotation {}
@TestQualifier private Object hasTestQualifier;
@Test public void nextAnnotationsRequiresJsonAnnotation() throws Exception {
@Test
public void nextAnnotationsRequiresJsonAnnotation() throws Exception {
try {
Types.nextAnnotations(Collections.<Annotation>emptySet(), TestAnnotation.class);
fail();
} catch (IllegalArgumentException expected) {
assertThat(expected).hasMessage(
"interface com.squareup.moshi.TypesTest$TestAnnotation is not a JsonQualifier.");
assertThat(expected)
.hasMessage(
"interface com.squareup.moshi.TypesTest$TestAnnotation is not a JsonQualifier.");
}
}
@Test public void nextAnnotationsDoesNotContainReturnsNull() throws Exception {
@Test
public void nextAnnotationsDoesNotContainReturnsNull() throws Exception {
Set<? extends Annotation> annotations =
Collections.singleton(Types.createJsonQualifierImplementation(AnotherTestQualifier.class));
assertThat(Types.nextAnnotations(annotations, TestQualifier.class)).isNull();
assertThat(
Types.nextAnnotations(Collections.<Annotation>emptySet(), TestQualifier.class)).isNull();
assertThat(Types.nextAnnotations(Collections.<Annotation>emptySet(), TestQualifier.class))
.isNull();
}
@Test public void nextAnnotationsReturnsDelegateAnnotations() throws Exception {
@Test
public void nextAnnotationsReturnsDelegateAnnotations() throws Exception {
Set<Annotation> annotations = new LinkedHashSet<>(2);
annotations.add(Types.createJsonQualifierImplementation(TestQualifier.class));
annotations.add(Types.createJsonQualifierImplementation(AnotherTestQualifier.class));
@@ -75,7 +81,8 @@ public final class TypesTest {
.isEqualTo(expected);
}
@Test public void newParameterizedType() throws Exception {
@Test
public void newParameterizedType() throws Exception {
// List<A>. List is a top-level class.
Type type = Types.newParameterizedType(List.class, A.class);
assertThat(getFirstTypeArgument(type)).isEqualTo(A.class);
@@ -85,7 +92,8 @@ public final class TypesTest {
assertThat(getFirstTypeArgument(type)).isEqualTo(B.class);
}
@Test public void newParameterizedType_missingTypeVars() {
@Test
public void newParameterizedType_missingTypeVars() {
try {
Types.newParameterizedType(List.class);
fail("Should have errored due to missing type variable");
@@ -101,7 +109,8 @@ public final class TypesTest {
}
}
@Test public void parameterizedTypeWithRequiredOwnerMissing() throws Exception {
@Test
public void parameterizedTypeWithRequiredOwnerMissing() throws Exception {
try {
Types.newParameterizedType(A.class, B.class);
fail();
@@ -110,7 +119,8 @@ public final class TypesTest {
}
}
@Test public void parameterizedTypeWithUnnecessaryOwnerProvided() throws Exception {
@Test
public void parameterizedTypeWithUnnecessaryOwnerProvided() throws Exception {
try {
Types.newParameterizedTypeWithOwner(A.class, List.class, B.class);
fail();
@@ -119,7 +129,8 @@ public final class TypesTest {
}
}
@Test public void parameterizedTypeWithIncorrectOwnerProvided() throws Exception {
@Test
public void parameterizedTypeWithIncorrectOwnerProvided() throws Exception {
try {
Types.newParameterizedTypeWithOwner(A.class, D.class, B.class);
fail();
@@ -128,7 +139,8 @@ public final class TypesTest {
}
}
@Test public void arrayOf() {
@Test
public void arrayOf() {
assertThat(Types.getRawType(Types.arrayOf(int.class))).isEqualTo(int[].class);
assertThat(Types.getRawType(Types.arrayOf(List.class))).isEqualTo(List[].class);
assertThat(Types.getRawType(Types.arrayOf(String[].class))).isEqualTo(String[][].class);
@@ -137,52 +149,53 @@ public final class TypesTest {
List<? extends CharSequence> listSubtype;
List<? super String> listSupertype;
@Test public void subtypeOf() throws Exception {
@Test
public void subtypeOf() throws Exception {
Type listOfWildcardType = TypesTest.class.getDeclaredField("listSubtype").getGenericType();
Type expected = Types.collectionElementType(listOfWildcardType, List.class);
assertThat(Types.subtypeOf(CharSequence.class)).isEqualTo(expected);
}
@Test public void supertypeOf() throws Exception {
@Test
public void supertypeOf() throws Exception {
Type listOfWildcardType = TypesTest.class.getDeclaredField("listSupertype").getGenericType();
Type expected = Types.collectionElementType(listOfWildcardType, List.class);
assertThat(Types.supertypeOf(String.class)).isEqualTo(expected);
}
@Test public void getFirstTypeArgument() throws Exception {
@Test
public void getFirstTypeArgument() throws Exception {
assertThat(getFirstTypeArgument(A.class)).isNull();
Type type = Types.newParameterizedTypeWithOwner(TypesTest.class, A.class, B.class, C.class);
assertThat(getFirstTypeArgument(type)).isEqualTo(B.class);
}
@Test public void newParameterizedTypeObjectMethods() throws Exception {
Type mapOfStringIntegerType = TypesTest.class.getDeclaredField(
"mapOfStringInteger").getGenericType();
ParameterizedType newMapType = Types.newParameterizedType(Map.class, String.class, Integer.class);
@Test
public void newParameterizedTypeObjectMethods() throws Exception {
Type mapOfStringIntegerType =
TypesTest.class.getDeclaredField("mapOfStringInteger").getGenericType();
ParameterizedType newMapType =
Types.newParameterizedType(Map.class, String.class, Integer.class);
assertThat(newMapType).isEqualTo(mapOfStringIntegerType);
assertThat(newMapType.hashCode()).isEqualTo(mapOfStringIntegerType.hashCode());
assertThat(newMapType.toString()).isEqualTo(mapOfStringIntegerType.toString());
Type arrayListOfMapOfStringIntegerType = TypesTest.class.getDeclaredField(
"arrayListOfMapOfStringInteger").getGenericType();
Type arrayListOfMapOfStringIntegerType =
TypesTest.class.getDeclaredField("arrayListOfMapOfStringInteger").getGenericType();
ParameterizedType newListType = Types.newParameterizedType(ArrayList.class, newMapType);
assertThat(newListType).isEqualTo(arrayListOfMapOfStringIntegerType);
assertThat(newListType.hashCode()).isEqualTo(arrayListOfMapOfStringIntegerType.hashCode());
assertThat(newListType.toString()).isEqualTo(arrayListOfMapOfStringIntegerType.toString());
}
private static final class A {
}
private static final class A {}
private static final class B {
}
private static final class B {}
private static final class C {
}
private static final class C {}
private static final class D<T> {
}
private static final class D<T> {}
/**
* Given a parameterized type {@code A<B, C>}, returns B. If the specified type is not a generic
@@ -199,49 +212,55 @@ public final class TypesTest {
Map<String, Integer> mapOfStringInteger;
Map<String, Integer>[] arrayOfMapOfStringInteger;
ArrayList<Map<String, Integer>> arrayListOfMapOfStringInteger;
interface StringIntegerMap extends Map<String, Integer> {
}
@Test public void arrayComponentType() throws Exception {
interface StringIntegerMap extends Map<String, Integer> {}
@Test
public void arrayComponentType() throws Exception {
assertThat(Types.arrayComponentType(String[][].class)).isEqualTo(String[].class);
assertThat(Types.arrayComponentType(String[].class)).isEqualTo(String.class);
Type arrayOfMapOfStringIntegerType = TypesTest.class.getDeclaredField(
"arrayOfMapOfStringInteger").getGenericType();
Type mapOfStringIntegerType = TypesTest.class.getDeclaredField(
"mapOfStringInteger").getGenericType();
Type arrayOfMapOfStringIntegerType =
TypesTest.class.getDeclaredField("arrayOfMapOfStringInteger").getGenericType();
Type mapOfStringIntegerType =
TypesTest.class.getDeclaredField("mapOfStringInteger").getGenericType();
assertThat(Types.arrayComponentType(arrayOfMapOfStringIntegerType))
.isEqualTo(mapOfStringIntegerType);
}
@Test public void collectionElementType() throws Exception {
Type arrayListOfMapOfStringIntegerType = TypesTest.class.getDeclaredField(
"arrayListOfMapOfStringInteger").getGenericType();
Type mapOfStringIntegerType = TypesTest.class.getDeclaredField(
"mapOfStringInteger").getGenericType();
@Test
public void collectionElementType() throws Exception {
Type arrayListOfMapOfStringIntegerType =
TypesTest.class.getDeclaredField("arrayListOfMapOfStringInteger").getGenericType();
Type mapOfStringIntegerType =
TypesTest.class.getDeclaredField("mapOfStringInteger").getGenericType();
assertThat(Types.collectionElementType(arrayListOfMapOfStringIntegerType, List.class))
.isEqualTo(mapOfStringIntegerType);
}
@Test public void mapKeyAndValueTypes() throws Exception {
Type mapOfStringIntegerType = TypesTest.class.getDeclaredField(
"mapOfStringInteger").getGenericType();
@Test
public void mapKeyAndValueTypes() throws Exception {
Type mapOfStringIntegerType =
TypesTest.class.getDeclaredField("mapOfStringInteger").getGenericType();
assertThat(Types.mapKeyAndValueTypes(mapOfStringIntegerType, Map.class))
.containsExactly(String.class, Integer.class);
}
@Test public void propertiesTypes() throws Exception {
@Test
public void propertiesTypes() throws Exception {
assertThat(Types.mapKeyAndValueTypes(Properties.class, Properties.class))
.containsExactly(String.class, String.class);
}
@Test public void fixedVariablesTypes() throws Exception {
@Test
public void fixedVariablesTypes() throws Exception {
assertThat(Types.mapKeyAndValueTypes(StringIntegerMap.class, StringIntegerMap.class))
.containsExactly(String.class, Integer.class);
}
@SuppressWarnings("GetClassOnAnnotation") // Explicitly checking for proxy implementation.
@Test public void createJsonQualifierImplementation() throws Exception {
@Test
public void createJsonQualifierImplementation() throws Exception {
TestQualifier actual = Types.createJsonQualifierImplementation(TestQualifier.class);
TestQualifier expected =
(TestQualifier) TypesTest.class.getDeclaredField("hasTestQualifier").getAnnotations()[0];
@@ -252,14 +271,16 @@ public final class TypesTest {
assertThat(actual.getClass()).isNotEqualTo(TestQualifier.class);
}
@Test public void arrayEqualsGenericTypeArray() {
@Test
public void arrayEqualsGenericTypeArray() {
assertThat(Types.equals(int[].class, Types.arrayOf(int.class))).isTrue();
assertThat(Types.equals(Types.arrayOf(int.class), int[].class)).isTrue();
assertThat(Types.equals(String[].class, Types.arrayOf(String.class))).isTrue();
assertThat(Types.equals(Types.arrayOf(String.class), String[].class)).isTrue();
}
@Test public void parameterizedAndWildcardTypesCannotHavePrimitiveArguments() throws Exception {
@Test
public void parameterizedAndWildcardTypesCannotHavePrimitiveArguments() throws Exception {
try {
Types.newParameterizedType(List.class, int.class);
fail();
@@ -280,39 +301,47 @@ public final class TypesTest {
}
}
@Test public void getFieldJsonQualifierAnnotations_privateFieldTest() {
Set<? extends Annotation> annotations = Types.getFieldJsonQualifierAnnotations(ClassWithAnnotatedFields.class,
"privateField");
@Test
public void getFieldJsonQualifierAnnotations_privateFieldTest() {
Set<? extends Annotation> annotations =
Types.getFieldJsonQualifierAnnotations(ClassWithAnnotatedFields.class, "privateField");
assertThat(annotations).hasSize(1);
assertThat(annotations.iterator().next()).isInstanceOf(FieldAnnotation.class);
}
@Test public void getFieldJsonQualifierAnnotations_publicFieldTest() {
Set<? extends Annotation> annotations = Types.getFieldJsonQualifierAnnotations(ClassWithAnnotatedFields.class,
"publicField");
@Test
public void getFieldJsonQualifierAnnotations_publicFieldTest() {
Set<? extends Annotation> annotations =
Types.getFieldJsonQualifierAnnotations(ClassWithAnnotatedFields.class, "publicField");
assertThat(annotations).hasSize(1);
assertThat(annotations.iterator().next()).isInstanceOf(FieldAnnotation.class);
}
@Test public void getFieldJsonQualifierAnnotations_unannotatedTest() {
Set<? extends Annotation> annotations = Types.getFieldJsonQualifierAnnotations(ClassWithAnnotatedFields.class,
"unannotatedField");
@Test
public void getFieldJsonQualifierAnnotations_unannotatedTest() {
Set<? extends Annotation> annotations =
Types.getFieldJsonQualifierAnnotations(ClassWithAnnotatedFields.class, "unannotatedField");
assertThat(annotations).hasSize(0);
}
@Test public void generatedJsonAdapterName_strings() {
@Test
public void generatedJsonAdapterName_strings() {
assertThat(Types.generatedJsonAdapterName("com.foo.Test")).isEqualTo("com.foo.TestJsonAdapter");
assertThat(Types.generatedJsonAdapterName("com.foo.Test$Bar")).isEqualTo("com.foo.Test_BarJsonAdapter");
assertThat(Types.generatedJsonAdapterName("com.foo.Test$Bar"))
.isEqualTo("com.foo.Test_BarJsonAdapter");
}
@Test public void generatedJsonAdapterName_class() {
assertThat(Types.generatedJsonAdapterName(TestJsonClass.class)).isEqualTo("com.squareup.moshi.TypesTest_TestJsonClassJsonAdapter");
@Test
public void generatedJsonAdapterName_class() {
assertThat(Types.generatedJsonAdapterName(TestJsonClass.class))
.isEqualTo("com.squareup.moshi.TypesTest_TestJsonClassJsonAdapter");
}
@Test public void generatedJsonAdapterName_class_missingJsonClass() {
@Test
public void generatedJsonAdapterName_class_missingJsonClass() {
try {
Types.generatedJsonAdapterName(TestNonJsonClass.class);
fail();
@@ -331,18 +360,25 @@ public final class TypesTest {
RecursiveTypeVars<? super T> superType;
}
@Test public void recursiveTypeVariablesResolve() {
JsonAdapter<RecursiveTypeVars<String>> adapter = new Moshi.Builder().build().adapter(Types
.newParameterizedTypeWithOwner(TypesTest.class, RecursiveTypeVars.class, String.class));
@Test
public void recursiveTypeVariablesResolve() {
JsonAdapter<RecursiveTypeVars<String>> adapter =
new Moshi.Builder()
.build()
.adapter(
Types.newParameterizedTypeWithOwner(
TypesTest.class, RecursiveTypeVars.class, String.class));
assertThat(adapter).isNotNull();
}
@Test public void recursiveTypeVariablesResolve1() {
@Test
public void recursiveTypeVariablesResolve1() {
JsonAdapter<TestType> adapter = new Moshi.Builder().build().adapter(TestType.class);
assertThat(adapter).isNotNull();
}
@Test public void recursiveTypeVariablesResolve2() {
@Test
public void recursiveTypeVariablesResolve2() {
JsonAdapter<TestType2> adapter = new Moshi.Builder().build().adapter(TestType2.class);
assertThat(adapter).isNotNull();
}
@@ -356,35 +392,23 @@ public final class TypesTest {
}
@JsonClass(generateAdapter = false)
static class TestJsonClass {
static class TestJsonClass {}
}
static class TestNonJsonClass {
}
static class TestNonJsonClass {}
@JsonQualifier
@Target(FIELD)
@Retention(RUNTIME)
@interface FieldAnnotation {
}
@interface FieldAnnotation {}
@Target(FIELD)
@Retention(RUNTIME)
@interface NoQualifierAnnotation {
}
@interface NoQualifierAnnotation {}
static class ClassWithAnnotatedFields {
@FieldAnnotation
@NoQualifierAnnotation
private final int privateField = 0;
@FieldAnnotation @NoQualifierAnnotation private final int privateField = 0;
@FieldAnnotation
@NoQualifierAnnotation
public final int publicField = 0;
@FieldAnnotation @NoQualifierAnnotation public final int publicField = 0;
private final int unannotatedField = 0;
}