From c65b3bf1cbdf710ea689cf5e9c26579aa75ea42c Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Sat, 6 May 2017 20:31:24 -0400 Subject: [PATCH] Import jsr305 and use it to mark @Nullable stuff. (#297) --- examples/pom.xml | 5 +++ .../recipes/DefaultOnDataMismatchAdapter.java | 3 +- .../com/squareup/moshi/recipes/Unwrap.java | 5 ++- .../squareup/moshi/recipes/package-info.java | 3 ++ .../com/squareup/moshi/KotlinJsonAdapter.kt | 6 ++- .../squareup/moshi/KotlinJsonAdapterTest.kt | 42 +++++++++---------- moshi/pom.xml | 5 +++ .../squareup/moshi/AdapterMethodsFactory.java | 22 +++++----- .../com/squareup/moshi/ArrayJsonAdapter.java | 3 +- .../com/squareup/moshi/ClassJsonAdapter.java | 3 +- .../squareup/moshi/CollectionJsonAdapter.java | 3 +- .../java/com/squareup/moshi/JsonAdapter.java | 39 ++++++++--------- .../com/squareup/moshi/JsonDataException.java | 8 ++-- .../squareup/moshi/JsonEncodingException.java | 3 +- .../java/com/squareup/moshi/JsonReader.java | 7 ++-- .../com/squareup/moshi/JsonUtf8Reader.java | 5 ++- .../com/squareup/moshi/JsonUtf8Writer.java | 3 +- .../com/squareup/moshi/JsonValueReader.java | 5 ++- .../com/squareup/moshi/JsonValueWriter.java | 15 ++++--- .../java/com/squareup/moshi/JsonWriter.java | 9 ++-- .../com/squareup/moshi/MapJsonAdapter.java | 3 +- .../main/java/com/squareup/moshi/Moshi.java | 9 ++-- .../main/java/com/squareup/moshi/Types.java | 17 ++++---- .../java/com/squareup/moshi/package-info.java | 3 ++ .../com/squareup/moshi/JsonWriterTest.java | 9 ++++ pom.xml | 6 +++ 26 files changed, 149 insertions(+), 92 deletions(-) create mode 100644 examples/src/main/java/com/squareup/moshi/recipes/package-info.java create mode 100644 moshi/src/main/java/com/squareup/moshi/package-info.java diff --git a/examples/pom.xml b/examples/pom.xml index 2d8226c..60c8236 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -12,6 +12,11 @@ moshi-examples + + com.google.code.findbugs + jsr305 + provided + com.squareup.moshi moshi diff --git a/examples/src/main/java/com/squareup/moshi/recipes/DefaultOnDataMismatchAdapter.java b/examples/src/main/java/com/squareup/moshi/recipes/DefaultOnDataMismatchAdapter.java index 7142ea8..19a5324 100644 --- a/examples/src/main/java/com/squareup/moshi/recipes/DefaultOnDataMismatchAdapter.java +++ b/examples/src/main/java/com/squareup/moshi/recipes/DefaultOnDataMismatchAdapter.java @@ -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 extends JsonAdapter { private final JsonAdapter delegate; @@ -54,7 +55,7 @@ public final class DefaultOnDataMismatchAdapter extends JsonAdapter { public static Factory newFactory(final Class type, final T defaultValue) { return new Factory() { - @Override public JsonAdapter create( + @Override public @Nullable JsonAdapter create( Type requestedType, Set annotations, Moshi moshi) { if (type != requestedType) return null; JsonAdapter delegate = moshi.nextAdapter(this, type, annotations); diff --git a/examples/src/main/java/com/squareup/moshi/recipes/Unwrap.java b/examples/src/main/java/com/squareup/moshi/recipes/Unwrap.java index 3ac14cf..bd19b8f 100644 --- a/examples/src/main/java/com/squareup/moshi/recipes/Unwrap.java +++ b/examples/src/main/java/com/squareup/moshi/recipes/Unwrap.java @@ -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 { public static final JsonAdapter.Factory FACTORY = new Factory() { - @Override - public JsonAdapter create(Type type, Set annotations, Moshi moshi) { + @Override public @Nullable JsonAdapter create( + Type type, Set annotations, Moshi moshi) { Set delegateAnnotations = Types.nextAnnotations(annotations, Enveloped.class); if (delegateAnnotations == null) { diff --git a/examples/src/main/java/com/squareup/moshi/recipes/package-info.java b/examples/src/main/java/com/squareup/moshi/recipes/package-info.java new file mode 100644 index 0000000..e8ea728 --- /dev/null +++ b/examples/src/main/java/com/squareup/moshi/recipes/package-info.java @@ -0,0 +1,3 @@ +/** Moshi code samples. */ +@javax.annotation.ParametersAreNonnullByDefault +package com.squareup.moshi.recipes; diff --git a/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt b/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt index 6f040e2..77a689e 100644 --- a/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt +++ b/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt @@ -95,7 +95,9 @@ internal class KotlinJsonAdapter( 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() } } diff --git a/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt b/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt index 0fdefd0..e3e87d8 100644 --- a/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt +++ b/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt @@ -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) } diff --git a/moshi/pom.xml b/moshi/pom.xml index 95c16fd..a745ce7 100644 --- a/moshi/pom.xml +++ b/moshi/pom.xml @@ -17,6 +17,11 @@ com.squareup.okio okio + + com.google.code.findbugs + jsr305 + provided + junit junit diff --git a/moshi/src/main/java/com/squareup/moshi/AdapterMethodsFactory.java b/moshi/src/main/java/com/squareup/moshi/AdapterMethodsFactory.java index e14d7c4..e9fdf7d 100644 --- a/moshi/src/main/java/com/squareup/moshi/AdapterMethodsFactory.java +++ b/moshi/src/main/java/com/squareup/moshi/AdapterMethodsFactory.java @@ -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 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() { - @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 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 adapterMethods, Type type, Set 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; diff --git a/moshi/src/main/java/com/squareup/moshi/ArrayJsonAdapter.java b/moshi/src/main/java/com/squareup/moshi/ArrayJsonAdapter.java index b6c6973..9f382bd 100644 --- a/moshi/src/main/java/com/squareup/moshi/ArrayJsonAdapter.java +++ b/moshi/src/main/java/com/squareup/moshi/ArrayJsonAdapter.java @@ -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 { public static final Factory FACTORY = new Factory() { - @Override public JsonAdapter create( + @Override public @Nullable JsonAdapter create( Type type, Set annotations, Moshi moshi) { Type elementType = Types.arrayComponentType(type); if (elementType == null) return null; diff --git a/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java b/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java index 74aab3b..833238e 100644 --- a/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java +++ b/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java @@ -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 extends JsonAdapter { public static final JsonAdapter.Factory FACTORY = new JsonAdapter.Factory() { - @Override public JsonAdapter create( + @Override public @Nullable JsonAdapter create( Type type, Set annotations, Moshi moshi) { Class rawType = Types.getRawType(type); if (rawType.isInterface() || rawType.isEnum()) return null; diff --git a/moshi/src/main/java/com/squareup/moshi/CollectionJsonAdapter.java b/moshi/src/main/java/com/squareup/moshi/CollectionJsonAdapter.java index 3b2418f..a027f29 100644 --- a/moshi/src/main/java/com/squareup/moshi/CollectionJsonAdapter.java +++ b/moshi/src/main/java/com/squareup/moshi/CollectionJsonAdapter.java @@ -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, T> extends JsonAdapter { public static final JsonAdapter.Factory FACTORY = new JsonAdapter.Factory() { - @Override public JsonAdapter create( + @Override public @Nullable JsonAdapter create( Type type, Set annotations, Moshi moshi) { Class rawType = Types.getRawType(type); if (!annotations.isEmpty()) return null; diff --git a/moshi/src/main/java/com/squareup/moshi/JsonAdapter.java b/moshi/src/main/java/com/squareup/moshi/JsonAdapter.java index b60a1d2..aee928a 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonAdapter.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonAdapter.java @@ -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 { - 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 { * 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 { * 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 { public final JsonAdapter serializeNulls() { final JsonAdapter delegate = this; return new JsonAdapter() { - @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 { public final JsonAdapter nullSafe() { final JsonAdapter delegate = this; return new JsonAdapter() { - @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 { public final JsonAdapter lenient() { final JsonAdapter delegate = this; return new JsonAdapter() { - @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 { 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 { public final JsonAdapter failOnUnknown() { final JsonAdapter delegate = this; return new JsonAdapter() { - @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 { 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 { } final JsonAdapter delegate = this; return new JsonAdapter() { - @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 { *

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 annotations, Moshi moshi); + @Nullable JsonAdapter create(Type type, Set annotations, Moshi moshi); } } diff --git a/moshi/src/main/java/com/squareup/moshi/JsonDataException.java b/moshi/src/main/java/com/squareup/moshi/JsonDataException.java index 0626263..3bfbf4f 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonDataException.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonDataException.java @@ -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); } } diff --git a/moshi/src/main/java/com/squareup/moshi/JsonEncodingException.java b/moshi/src/main/java/com/squareup/moshi/JsonEncodingException.java index 625a20e..5c483ca 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonEncodingException.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonEncodingException.java @@ -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); } } diff --git a/moshi/src/main/java/com/squareup/moshi/JsonReader.java b/moshi/src/main/java/com/squareup/moshi/JsonReader.java index 90c04f8..f9b60dd 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonReader.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonReader.java @@ -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 nextNull() throws IOException; + public abstract @Nullable 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 list = new ArrayList<>(); diff --git a/moshi/src/main/java/com/squareup/moshi/JsonUtf8Reader.java b/moshi/src/main/java/com/squareup/moshi/JsonUtf8Reader.java index d0a76f7..5c0e6a5 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonUtf8Reader.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonUtf8Reader.java @@ -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 nextNull() throws IOException { + @Override public @Nullable T nextNull() throws IOException { int p = peeked; if (p == PEEKED_NONE) { p = doPeek(); diff --git a/moshi/src/main/java/com/squareup/moshi/JsonUtf8Writer.java b/moshi/src/main/java/com/squareup/moshi/JsonUtf8Writer.java index a049573..97838bc 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonUtf8Writer.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonUtf8Writer.java @@ -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(); } diff --git a/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java b/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java index 2111757..9b3206f 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java @@ -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 nextNull() throws IOException { + @Override public @Nullable 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 require(Class type, Token expected) throws IOException { + private @Nullable T require(Class type, Token expected) throws IOException { Object peeked = (stackSize != 0 ? stack[stackSize - 1] : null); if (type.isInstance(peeked)) { diff --git a/moshi/src/main/java/com/squareup/moshi/JsonValueWriter.java b/moshi/src/main/java/com/squareup/moshi/JsonValueWriter.java index 01038e0..79fc9b8 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonValueWriter.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonValueWriter.java @@ -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) { diff --git a/moshi/src/main/java/com/squareup/moshi/JsonWriter.java b/moshi/src/main/java/com/squareup/moshi/JsonWriter.java index 96c86bd..c9b1043 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonWriter.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonWriter.java @@ -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 diff --git a/moshi/src/main/java/com/squareup/moshi/MapJsonAdapter.java b/moshi/src/main/java/com/squareup/moshi/MapJsonAdapter.java index a31cf22..598b0e0 100644 --- a/moshi/src/main/java/com/squareup/moshi/MapJsonAdapter.java +++ b/moshi/src/main/java/com/squareup/moshi/MapJsonAdapter.java @@ -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 extends JsonAdapter> { public static final Factory FACTORY = new Factory() { - @Override public JsonAdapter create( + @Override public @Nullable JsonAdapter create( Type type, Set annotations, Moshi moshi) { if (!annotations.isEmpty()) return null; Class rawType = Types.getRawType(type); diff --git a/moshi/src/main/java/com/squareup/moshi/Moshi.java b/moshi/src/main/java/com/squareup/moshi/Moshi.java index 16cedad..c422435 100644 --- a/moshi/src/main/java/com/squareup/moshi/Moshi.java +++ b/moshi/src/main/java/com/squareup/moshi/Moshi.java @@ -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 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 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} field for an organization's management hierarchy. */ private static class DeferredAdapter extends JsonAdapter { - Object cacheKey; - private JsonAdapter delegate; + @Nullable Object cacheKey; + private @Nullable JsonAdapter delegate; DeferredAdapter(Object cacheKey) { this.cacheKey = cacheKey; diff --git a/moshi/src/main/java/com/squareup/moshi/Types.java b/moshi/src/main/java/com/squareup/moshi/Types.java index f3bf0d7..7ecfbe5 100644 --- a/moshi/src/main/java/com/squareup/moshi/Types.java +++ b/moshi/src/main/java/com/squareup/moshi/Types.java @@ -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(); diff --git a/moshi/src/main/java/com/squareup/moshi/package-info.java b/moshi/src/main/java/com/squareup/moshi/package-info.java new file mode 100644 index 0000000..f43a6f6 --- /dev/null +++ b/moshi/src/main/java/com/squareup/moshi/package-info.java @@ -0,0 +1,3 @@ +/** Moshi is modern JSON library for Android and Java. */ +@javax.annotation.ParametersAreNonnullByDefault +package com.squareup.moshi; diff --git a/moshi/src/test/java/com/squareup/moshi/JsonWriterTest.java b/moshi/src/test/java/com/squareup/moshi/JsonWriterTest.java index 571f644..35538c6 100644 --- a/moshi/src/test/java/com/squareup/moshi/JsonWriterTest.java +++ b/moshi/src/test/java/com/squareup/moshi/JsonWriterTest.java @@ -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(); diff --git a/pom.xml b/pom.xml index cac2cd1..cd4a5ea 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,12 @@ + + com.google.code.findbugs + jsr305 + 3.0.2 + provided + junit junit