Import jsr305 and use it to mark @Nullable stuff. (#297)

This commit is contained in:
Jesse Wilson 2017-05-06 20:31:24 -04:00 committed by GitHub
parent dac5f695b3
commit c65b3bf1cb
26 changed files with 149 additions and 92 deletions

View file

@ -12,6 +12,11 @@
<artifactId>moshi-examples</artifactId>
<dependencies>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.squareup.moshi</groupId>
<artifactId>moshi</artifactId>

View file

@ -24,6 +24,7 @@ import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Set;
import javax.annotation.Nullable;
public final class DefaultOnDataMismatchAdapter<T> extends JsonAdapter<T> {
private final JsonAdapter<T> delegate;
@ -54,7 +55,7 @@ public final class DefaultOnDataMismatchAdapter<T> extends JsonAdapter<T> {
public static <T> Factory newFactory(final Class<T> type, final T defaultValue) {
return new Factory() {
@Override public JsonAdapter<?> create(
@Override public @Nullable JsonAdapter<?> create(
Type requestedType, Set<? extends Annotation> annotations, Moshi moshi) {
if (type != requestedType) return null;
JsonAdapter<T> delegate = moshi.nextAdapter(this, type, annotations);

View file

@ -28,6 +28,7 @@ import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.reflect.Type;
import java.util.Set;
import javax.annotation.Nullable;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@ -51,8 +52,8 @@ final class Unwrap {
public static final class EnvelopeJsonAdapter extends JsonAdapter<Object> {
public static final JsonAdapter.Factory FACTORY = new Factory() {
@Override
public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi) {
@Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Set<? extends Annotation> delegateAnnotations =
Types.nextAnnotations(annotations, Enveloped.class);
if (delegateAnnotations == null) {

View file

@ -0,0 +1,3 @@
/** Moshi code samples. */
@javax.annotation.ParametersAreNonnullByDefault
package com.squareup.moshi.recipes;

View file

@ -95,7 +95,9 @@ internal class KotlinJsonAdapter<T>(
return result
}
override fun toJson(writer: JsonWriter, value: T) {
override fun toJson(writer: JsonWriter, value: T?) {
if (value == null) throw NullPointerException("value == null")
writer.beginObject()
for (binding in bindings) {
if (binding == null) continue // Skip constructor parameters that aren't properties.
@ -202,6 +204,6 @@ object KotlinJsonAdapterFactory : JsonAdapter.Factory {
bindings += bindingsByName.values
val options = JsonReader.Options.of(*bindings.map { it?.name ?: "\u0000" }.toTypedArray())
return KotlinJsonAdapter(constructor, bindings, options)
return KotlinJsonAdapter(constructor, bindings, options).nullSafe()
}
}

View file

@ -31,7 +31,7 @@ class KotlinJsonAdapterTest {
val encoded = ConstructorParameters(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.b).isEqualTo(6)
}
@ -47,7 +47,7 @@ class KotlinJsonAdapterTest {
encoded.b = 5
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":3,\"b\":5}")!!
assertThat(decoded.a).isEqualTo(3)
assertThat(decoded.b).isEqualTo(5)
}
@ -65,7 +65,7 @@ class KotlinJsonAdapterTest {
encoded.b = 5
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.b).isEqualTo(6)
}
@ -81,7 +81,7 @@ class KotlinJsonAdapterTest {
val encoded = ImmutableConstructorParameters(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.b).isEqualTo(6)
}
@ -95,7 +95,7 @@ class KotlinJsonAdapterTest {
val encoded = ImmutableProperties(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":3,\"b\":5}")!!
assertThat(decoded.a).isEqualTo(3)
assertThat(decoded.b).isEqualTo(5)
}
@ -112,7 +112,7 @@ class KotlinJsonAdapterTest {
val encoded = ConstructorDefaultValues(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"b\":6}")!!
assertThat(decoded.a).isEqualTo(-1)
assertThat(decoded.b).isEqualTo(6)
}
@ -155,7 +155,7 @@ class KotlinJsonAdapterTest {
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}")
assertThat(jsonAdapter.serializeNulls().toJson(encoded)).isEqualTo("{\"a\":null,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":null,\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"a\":null,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(null)
assertThat(decoded.b).isEqualTo(6)
}
@ -170,7 +170,7 @@ class KotlinJsonAdapterTest {
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}")
assertThat(jsonAdapter.serializeNulls().toJson(encoded)).isEqualTo("{\"a\":null,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"b\":6}")!!
assertThat(decoded.a).isNull()
assertThat(decoded.b).isEqualTo(6)
}
@ -201,7 +201,7 @@ class KotlinJsonAdapterTest {
val encoded = ConstructorParameterWithQualifier("Android", "Banana")
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":\"ANDROID\",\"b\":\"Banana\"}")
val decoded = jsonAdapter.fromJson("{\"a\":\"Android\",\"b\":\"Banana\"}")
val decoded = jsonAdapter.fromJson("{\"a\":\"Android\",\"b\":\"Banana\"}")!!
assertThat(decoded.a).isEqualTo("android")
assertThat(decoded.b).isEqualTo("Banana")
}
@ -220,7 +220,7 @@ class KotlinJsonAdapterTest {
encoded.b = "Banana"
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":\"ANDROID\",\"b\":\"Banana\"}")
val decoded = jsonAdapter.fromJson("{\"a\":\"Android\",\"b\":\"Banana\"}")
val decoded = jsonAdapter.fromJson("{\"a\":\"Android\",\"b\":\"Banana\"}")!!
assertThat(decoded.a).isEqualTo("android")
assertThat(decoded.b).isEqualTo("Banana")
}
@ -237,7 +237,7 @@ class KotlinJsonAdapterTest {
val encoded = ConstructorParameterWithJsonName(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"key a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"key a\":4,\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"key a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.b).isEqualTo(6)
}
@ -253,7 +253,7 @@ class KotlinJsonAdapterTest {
encoded.b = 5
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"key a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"key a\":4,\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"key a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.b).isEqualTo(6)
}
@ -270,7 +270,7 @@ class KotlinJsonAdapterTest {
val encoded = TransientConstructorParameter(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(-1)
assertThat(decoded.b).isEqualTo(6)
}
@ -286,7 +286,7 @@ class KotlinJsonAdapterTest {
encoded.b = 5
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(-1)
assertThat(decoded.b).isEqualTo(6)
}
@ -303,7 +303,7 @@ class KotlinJsonAdapterTest {
val encoded = SubtypeConstructorParameters(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.b).isEqualTo(6)
}
@ -321,7 +321,7 @@ class KotlinJsonAdapterTest {
encoded.b = 5
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5,\"a\":3}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.b).isEqualTo(6)
}
@ -341,7 +341,7 @@ class KotlinJsonAdapterTest {
val encoded = ExtendsPlatformClassWithPrivateField(3)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"id\":\"B\"}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"id\":\"B\"}")!!
assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.id).isEqualTo("C")
}
@ -355,7 +355,7 @@ class KotlinJsonAdapterTest {
val encoded = ExtendsPlatformClassWithProtectedField(3)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"buf\":[0,0],\"count\":0}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"buf\":[0,0],\"size\":0}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"buf\":[0,0],\"size\":0}")!!
assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.buf()).isEqualTo(ByteArray(2, { 0 }))
assertThat(decoded.count()).isEqualTo(0)
@ -384,7 +384,7 @@ class KotlinJsonAdapterTest {
val encoded = PrivateConstructorParameters(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a()).isEqualTo(4)
assertThat(decoded.b()).isEqualTo(6)
}
@ -401,7 +401,7 @@ class KotlinJsonAdapterTest {
val encoded = PrivateConstructor.newInstance(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a()).isEqualTo(4)
assertThat(decoded.b()).isEqualTo(6)
}
@ -423,7 +423,7 @@ class KotlinJsonAdapterTest {
encoded.b(5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a()).isEqualTo(4)
assertThat(decoded.b()).isEqualTo(6)
}

View file

@ -17,6 +17,11 @@
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>

View file

@ -24,6 +24,7 @@ import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import static com.squareup.moshi.Util.jsonAnnotations;
@ -36,7 +37,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
this.fromAdapters = fromAdapters;
}
@Override public 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);
@ -59,7 +60,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
if (fromAdapter != null) fromAdapter.bind(moshi, this);
return new JsonAdapter<Object>() {
@Override public void toJson(JsonWriter writer, 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) {
@ -75,7 +76,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
}
}
@Override public 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) {
@ -155,7 +156,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
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, Object value)
@Override public void toJson(Moshi moshi, JsonWriter writer, @Nullable Object value)
throws IOException, InvocationTargetException {
invoke(writer, value);
}
@ -175,7 +176,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
delegate = moshi.adapter(returnType, returnTypeAnnotations);
}
@Override public void toJson(Moshi moshi, JsonWriter writer, Object value)
@Override public void toJson(Moshi moshi, JsonWriter writer, @Nullable Object value)
throws IOException, InvocationTargetException {
Object intermediate = invoke(value);
delegate.toJson(writer, intermediate);
@ -260,7 +261,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
}
/** Returns the matching adapter method from the list. */
private static AdapterMethod get(
private static @Nullable AdapterMethod get(
List<AdapterMethod> adapterMethods, Type type, Set<? extends Annotation> annotations) {
for (int i = 0, size = adapterMethods.size(); i < size; i++) {
AdapterMethod adapterMethod = adapterMethods.get(i);
@ -306,18 +307,18 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
}
}
public void toJson(Moshi moshi, JsonWriter writer, Object value)
public void toJson(Moshi moshi, JsonWriter writer, @Nullable Object value)
throws IOException, InvocationTargetException {
throw new AssertionError();
}
public Object fromJson(Moshi moshi, JsonReader reader)
public @Nullable Object fromJson(Moshi moshi, JsonReader reader)
throws IOException, InvocationTargetException {
throw new AssertionError();
}
/** Invoke the method with one fixed argument, plus any number of JSON adapter arguments. */
protected Object invoke(Object a1) throws InvocationTargetException {
protected @Nullable Object invoke(@Nullable Object a1) throws InvocationTargetException {
Object[] args = new Object[1 + jsonAdapters.length];
args[0] = a1;
System.arraycopy(jsonAdapters, 0, args, 1, jsonAdapters.length);
@ -330,7 +331,8 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
}
/** Invoke the method with two fixed arguments, plus any number of JSON adapter arguments. */
protected Object invoke(Object a1, Object a2) throws InvocationTargetException {
protected Object invoke(@Nullable Object a1, @Nullable Object a2)
throws InvocationTargetException {
Object[] args = new Object[2 + jsonAdapters.length];
args[0] = a1;
args[1] = a2;

View file

@ -22,6 +22,7 @@ import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Converts arrays to JSON arrays containing their converted contents. This
@ -29,7 +30,7 @@ import java.util.Set;
*/
final class ArrayJsonAdapter extends JsonAdapter<Object> {
public static final Factory FACTORY = new Factory() {
@Override public JsonAdapter<?> create(
@Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Type elementType = Types.arrayComponentType(type);
if (elementType == null) return null;

View file

@ -24,6 +24,7 @@ import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.annotation.Nullable;
/**
* Emits a regular class as a JSON object by mapping Java fields to JSON object properties.
@ -42,7 +43,7 @@ import java.util.TreeMap;
*/
final class ClassJsonAdapter<T> extends JsonAdapter<T> {
public static final JsonAdapter.Factory FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(
@Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Class<?> rawType = Types.getRawType(type);
if (rawType.isInterface() || rawType.isEnum()) return null;

View file

@ -23,11 +23,12 @@ import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
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 JsonAdapter<?> create(
@Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Class<?> rawType = Types.getRawType(type);
if (!annotations.isEmpty()) return null;

View file

@ -20,6 +20,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.Set;
import javax.annotation.Nullable;
import okio.Buffer;
import okio.BufferedSink;
import okio.BufferedSource;
@ -28,24 +29,24 @@ import okio.BufferedSource;
* Converts Java values to JSON, and JSON values to Java.
*/
public abstract class JsonAdapter<T> {
public abstract T fromJson(JsonReader reader) throws IOException;
public abstract @Nullable T fromJson(JsonReader reader) throws IOException;
public final T fromJson(BufferedSource source) throws IOException {
public final @Nullable T fromJson(BufferedSource source) throws IOException {
return fromJson(JsonReader.of(source));
}
public final T fromJson(String string) throws IOException {
public final @Nullable T fromJson(String string) throws IOException {
return fromJson(new Buffer().writeUtf8(string));
}
public abstract void toJson(JsonWriter writer, T value) throws IOException;
public abstract void toJson(JsonWriter writer, @Nullable T value) throws IOException;
public final void toJson(BufferedSink sink, T value) throws IOException {
public final void toJson(BufferedSink sink, @Nullable T value) throws IOException {
JsonWriter writer = JsonWriter.of(sink);
toJson(writer, value);
}
public final String toJson(T value) {
public final String toJson(@Nullable T value) {
Buffer buffer = new Buffer();
try {
toJson(buffer, value);
@ -65,7 +66,7 @@ public abstract class JsonAdapter<T> {
* Long}), as a {@link Double} for boxed floating point types ({@link Float} and {@link Double}),
* and as a {@link BigDecimal} for all other types.
*/
public final Object toJsonValue(T value) {
public final @Nullable Object toJsonValue(@Nullable T value) {
JsonValueWriter writer = new JsonValueWriter();
try {
toJson(writer, value);
@ -79,7 +80,7 @@ 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.
*/
public final T fromJsonValue(Object value) {
public final @Nullable T fromJsonValue(@Nullable Object value) {
JsonValueReader reader = new JsonValueReader(value);
try {
return fromJson(reader);
@ -95,10 +96,10 @@ public abstract class JsonAdapter<T> {
public final JsonAdapter<T> serializeNulls() {
final JsonAdapter<T> delegate = this;
return new JsonAdapter<T>() {
@Override public 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, T value) throws IOException {
@Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
boolean serializeNulls = writer.getSerializeNulls();
writer.setSerializeNulls(true);
try {
@ -120,14 +121,14 @@ public abstract class JsonAdapter<T> {
public final JsonAdapter<T> nullSafe() {
final JsonAdapter<T> delegate = this;
return new JsonAdapter<T>() {
@Override public 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 {
return delegate.fromJson(reader);
}
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
@Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
if (value == null) {
writer.nullValue();
} else {
@ -144,7 +145,7 @@ public abstract class JsonAdapter<T> {
public final JsonAdapter<T> lenient() {
final JsonAdapter<T> delegate = this;
return new JsonAdapter<T>() {
@Override public T fromJson(JsonReader reader) throws IOException {
@Override public @Nullable T fromJson(JsonReader reader) throws IOException {
boolean lenient = reader.isLenient();
reader.setLenient(true);
try {
@ -153,7 +154,7 @@ public abstract class JsonAdapter<T> {
reader.setLenient(lenient);
}
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
@Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
boolean lenient = writer.isLenient();
writer.setLenient(true);
try {
@ -177,7 +178,7 @@ public abstract class JsonAdapter<T> {
public final JsonAdapter<T> failOnUnknown() {
final JsonAdapter<T> delegate = this;
return new JsonAdapter<T>() {
@Override public T fromJson(JsonReader reader) throws IOException {
@Override public @Nullable T fromJson(JsonReader reader) throws IOException {
boolean skipForbidden = reader.failOnUnknown();
reader.setFailOnUnknown(true);
try {
@ -186,7 +187,7 @@ public abstract class JsonAdapter<T> {
reader.setFailOnUnknown(skipForbidden);
}
}
@Override public void toJson(JsonWriter writer, T value) throws IOException {
@Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
delegate.toJson(writer, value);
}
@Override public String toString() {
@ -209,10 +210,10 @@ public abstract class JsonAdapter<T> {
}
final JsonAdapter<T> delegate = this;
return new JsonAdapter<T>() {
@Override public 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, T value) throws IOException {
@Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
String originalIndent = writer.getIndent();
writer.setIndent(indent);
try {
@ -236,6 +237,6 @@ public abstract class JsonAdapter<T> {
* <p>Implementations may use to {@link Moshi#adapter} to compose adapters of other types, or
* {@link Moshi#nextAdapter} to delegate to the underlying adapter of the same type.
*/
JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi);
@Nullable JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi);
}
}

View file

@ -15,6 +15,8 @@
*/
package com.squareup.moshi;
import javax.annotation.Nullable;
/**
* Thrown when the data in a JSON document doesn't match the data expected by the caller. For
* example, suppose the application expects a boolean but the JSON document contains a string. When
@ -31,15 +33,15 @@ public final class JsonDataException extends RuntimeException {
public JsonDataException() {
}
public JsonDataException(String message) {
public JsonDataException(@Nullable String message) {
super(message);
}
public JsonDataException(Throwable cause) {
public JsonDataException(@Nullable Throwable cause) {
super(cause);
}
public JsonDataException(String message, Throwable cause) {
public JsonDataException(@Nullable String message, @Nullable Throwable cause) {
super(message, cause);
}
}

View file

@ -16,10 +16,11 @@
package com.squareup.moshi;
import java.io.IOException;
import javax.annotation.Nullable;
/** Thrown when the data being parsed is not encoded as valid JSON. */
public final class JsonEncodingException extends IOException {
public JsonEncodingException(String message) {
public JsonEncodingException(@Nullable String message) {
super(message);
}
}

View file

@ -20,6 +20,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import okio.Buffer;
import okio.BufferedSource;
import okio.ByteString;
@ -212,7 +213,7 @@ public abstract class JsonReader implements Closeable {
throw new JsonEncodingException(message + " at path " + getPath());
}
final JsonDataException typeMismatch(Object value, Object expected) {
final JsonDataException typeMismatch(@Nullable Object value, Object expected) {
if (value == null) {
return new JsonDataException(
"Expected " + expected + " but was null at path " + getPath());
@ -351,7 +352,7 @@ public abstract class JsonReader implements Closeable {
*
* @throws JsonDataException if the next token is not null or if this reader is closed.
*/
public abstract <T> T nextNull() throws IOException;
public abstract @Nullable <T> T nextNull() throws IOException;
/**
* Returns the {@linkplain Token#NUMBER double} value of the next token, consuming it. If the next
@ -400,7 +401,7 @@ public abstract class JsonReader implements Closeable {
* @throws JsonDataException if the next token is not a literal value, if a JSON object has a
* duplicate key.
*/
public final Object readJsonValue() throws IOException {
public final @Nullable Object readJsonValue() throws IOException {
switch (peek()) {
case BEGIN_ARRAY:
List<Object> list = new ArrayList<>();

View file

@ -18,6 +18,7 @@ package com.squareup.moshi;
import java.io.EOFException;
import java.io.IOException;
import java.math.BigDecimal;
import javax.annotation.Nullable;
import okio.Buffer;
import okio.BufferedSource;
import okio.ByteString;
@ -86,7 +87,7 @@ final class JsonUtf8Reader extends JsonReader {
* This is populated before a numeric value is parsed and used if that parsing
* fails.
*/
private String peekedString;
private @Nullable String peekedString;
JsonUtf8Reader(BufferedSource source) {
if (source == null) {
@ -670,7 +671,7 @@ final class JsonUtf8Reader extends JsonReader {
throw new JsonDataException("Expected a boolean but was " + peek() + " at path " + getPath());
}
@Override public <T> T nextNull() throws IOException {
@Override public @Nullable <T> T nextNull() throws IOException {
int p = peeked;
if (p == PEEKED_NONE) {
p = doPeek();

View file

@ -16,6 +16,7 @@
package com.squareup.moshi;
import java.io.IOException;
import javax.annotation.Nullable;
import okio.BufferedSink;
import okio.Sink;
@ -222,7 +223,7 @@ final class JsonUtf8Writer extends JsonWriter {
return this;
}
@Override public JsonWriter value(Number value) throws IOException {
@Override public JsonWriter value(@Nullable Number value) throws IOException {
if (value == null) {
return nullValue();
}

View file

@ -22,6 +22,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import javax.annotation.Nullable;
/**
* This class reads a JSON document by traversing a Java object comprising maps, lists, and JSON
@ -173,7 +174,7 @@ final class JsonValueReader extends JsonReader {
return peeked;
}
@Override public <T> T nextNull() throws IOException {
@Override public @Nullable <T> T nextNull() throws IOException {
require(Void.class, Token.NULL);
remove();
return null;
@ -297,7 +298,7 @@ final class JsonValueReader extends JsonReader {
* Returns the top of the stack which is required to be a {@code type}. Throws if this reader is
* closed, or if the type isn't what was expected.
*/
private <T> T require(Class<T> type, Token expected) throws IOException {
private @Nullable <T> T require(Class<T> type, Token expected) throws IOException {
Object peeked = (stackSize != 0 ? stack[stackSize - 1] : null);
if (type.isInstance(peeked)) {

View file

@ -20,6 +20,7 @@ import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import static com.squareup.moshi.JsonScope.EMPTY_ARRAY;
import static com.squareup.moshi.JsonScope.EMPTY_DOCUMENT;
@ -31,7 +32,7 @@ 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 {
private final Object[] stack = new Object[32];
private String deferredName;
private @Nullable String deferredName;
JsonValueWriter() {
pushScope(EMPTY_DOCUMENT);
@ -106,7 +107,7 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public JsonWriter value(String value) throws IOException {
@Override public JsonWriter value(@Nullable String value) throws IOException {
if (promoteValueToName) {
return name(value);
}
@ -127,7 +128,7 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public JsonWriter value(Boolean value) throws IOException {
@Override public JsonWriter value(@Nullable Boolean value) throws IOException {
add(value);
pathIndices[stackSize - 1]++;
return this;
@ -155,7 +156,7 @@ final class JsonValueWriter extends JsonWriter {
return this;
}
@Override public JsonWriter value(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
@ -169,6 +170,10 @@ final class JsonValueWriter extends JsonWriter {
return value(value.doubleValue());
}
if (value == null) {
return nullValue();
}
// Everything else gets converted to a BigDecimal.
BigDecimal bigDecimalValue = value instanceof BigDecimal
? ((BigDecimal) value)
@ -195,7 +200,7 @@ final class JsonValueWriter extends JsonWriter {
}
}
private JsonValueWriter add(Object newTop) {
private JsonValueWriter add(@Nullable Object newTop) {
int scope = peekScope();
if (stackSize == 1) {

View file

@ -18,6 +18,7 @@ package com.squareup.moshi;
import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException;
import javax.annotation.Nullable;
import okio.BufferedSink;
import static com.squareup.moshi.JsonScope.EMPTY_OBJECT;
@ -257,7 +258,7 @@ public abstract class JsonWriter implements Closeable, Flushable {
/**
* Encodes the property name.
*
* @param name the name of the forthcoming value. May not be null.
* @param name the name of the forthcoming value. Must not be null.
* @return this writer.
*/
public abstract JsonWriter name(String name) throws IOException;
@ -268,7 +269,7 @@ public abstract class JsonWriter implements Closeable, Flushable {
* @param value the literal string value, or null to encode a null literal.
* @return this writer.
*/
public abstract JsonWriter value(String value) throws IOException;
public abstract JsonWriter value(@Nullable String value) throws IOException;
/**
* Encodes {@code null}.
@ -289,7 +290,7 @@ public abstract class JsonWriter implements Closeable, Flushable {
*
* @return this writer.
*/
public abstract JsonWriter value(Boolean value) throws IOException;
public abstract JsonWriter value(@Nullable Boolean value) throws IOException;
/**
* Encodes {@code value}.
@ -314,7 +315,7 @@ public abstract class JsonWriter implements Closeable, Flushable {
* {@linkplain Double#isInfinite() infinities}.
* @return this writer.
*/
public abstract JsonWriter value(Number value) throws IOException;
public abstract JsonWriter value(@Nullable Number value) throws IOException;
/**
* Changes the writer to treat the next value as a string name. This is useful for map adapters so

View file

@ -20,6 +20,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Converts maps with string keys to JSON objects.
@ -28,7 +29,7 @@ import java.util.Set;
*/
final class MapJsonAdapter<K, V> extends JsonAdapter<Map<K, V>> {
public static final Factory FACTORY = new Factory() {
@Override public JsonAdapter<?> create(
@Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (!annotations.isEmpty()) return null;
Class<?> rawType = Types.getRawType(type);

View file

@ -25,6 +25,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Coordinates binding between JSON values and Java objects.
@ -154,7 +155,7 @@ public final class Moshi {
if (jsonAdapter == null) throw new IllegalArgumentException("jsonAdapter == null");
return add(new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(
@Override public @Nullable JsonAdapter<?> create(
Type targetType, Set<? extends Annotation> annotations, Moshi moshi) {
return annotations.isEmpty() && Util.typesMatch(type, targetType) ? jsonAdapter : null;
}
@ -174,7 +175,7 @@ public final class Moshi {
}
return add(new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create(
@Override public @Nullable JsonAdapter<?> create(
Type targetType, Set<? extends Annotation> annotations, Moshi moshi) {
if (Util.typesMatch(type, targetType)
&& annotations.size() == 1
@ -216,8 +217,8 @@ public final class Moshi {
* class that has a {@code List<Employee>} field for an organization's management hierarchy.
*/
private static class DeferredAdapter<T> extends JsonAdapter<T> {
Object cacheKey;
private JsonAdapter<T> delegate;
@Nullable Object cacheKey;
private @Nullable JsonAdapter<T> delegate;
DeferredAdapter(Object cacheKey) {
this.cacheKey = cacheKey;

View file

@ -34,6 +34,7 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Set;
import javax.annotation.Nullable;
/** Factory methods for types. */
public final class Types {
@ -200,12 +201,12 @@ public final class Types {
});
}
static boolean equal(Object a, Object b) {
static boolean equal(@Nullable Object a, @Nullable Object b) {
return a == b || (a != null && a.equals(b));
}
/** Returns true if {@code a} and {@code b} are equal. */
public static boolean equals(Type a, Type b) {
public static boolean equals(@Nullable Type a, @Nullable Type b) {
if (a == b) {
return true; // Also handles (a == null && b == null).
@ -260,7 +261,7 @@ public final class Types {
}
}
static int hashCodeOrZero(Object o) {
static int hashCodeOrZero(@Nullable Object o) {
return o != null ? o.hashCode() : 0;
}
@ -484,7 +485,7 @@ public final class Types {
* Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
* a class.
*/
private static Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
private static @Nullable Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
return genericDeclaration instanceof Class ? (Class<?>) genericDeclaration : null;
}
@ -496,11 +497,11 @@ public final class Types {
}
private static final class ParameterizedTypeImpl implements ParameterizedType {
private final Type ownerType;
private final @Nullable Type ownerType;
private final Type rawType;
final Type[] typeArguments;
ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
ParameterizedTypeImpl(@Nullable Type ownerType, Type rawType, Type... typeArguments) {
// Require an owner type if the raw type needs it.
if (rawType instanceof Class<?>
&& (ownerType == null) != (((Class<?>) rawType).getEnclosingClass() == null)) {
@ -526,7 +527,7 @@ public final class Types {
return rawType;
}
@Override public Type getOwnerType() {
@Override public @Nullable Type getOwnerType() {
return ownerType;
}
@ -589,7 +590,7 @@ public final class Types {
*/
private static final class WildcardTypeImpl implements WildcardType {
private final Type upperBound;
private final Type lowerBound;
private final @Nullable Type lowerBound;
WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
if (lowerBounds.length > 1) throw new IllegalArgumentException();

View file

@ -0,0 +1,3 @@
/** Moshi is modern JSON library for Android and Java. */
@javax.annotation.ParametersAreNonnullByDefault
package com.squareup.moshi;

View file

@ -286,6 +286,15 @@ public final class JsonWriterTest {
+ "3.141592653589793238462643383]");
}
@Test public void nullNumbers() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.value((Number) null);
writer.endArray();
writer.close();
assertThat(factory.json()).isEqualTo("[null]");
}
@Test public void booleans() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();

View file

@ -58,6 +58,12 @@
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>