diff --git a/adapters/pom.xml b/adapters/pom.xml index 8e44654..0897ec0 100644 --- a/adapters/pom.xml +++ b/adapters/pom.xml @@ -33,4 +33,20 @@ test + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + com.squareup.moshi.adapters + + + + + + diff --git a/adapters/src/main/java/com/squareup/moshi/Rfc3339DateJsonAdapter.java b/adapters/src/main/java/com/squareup/moshi/Rfc3339DateJsonAdapter.java index f1108c7..021e987 100644 --- a/adapters/src/main/java/com/squareup/moshi/Rfc3339DateJsonAdapter.java +++ b/adapters/src/main/java/com/squareup/moshi/Rfc3339DateJsonAdapter.java @@ -19,17 +19,18 @@ import java.io.IOException; import java.util.Date; /** - * Formats dates using RFC 3339, which is - * formatted like {@code 2015-09-26T18:23:50.250Z}. + * @deprecated this class moved to avoid a package name conflict in the Java Platform Module System. + * The new class is {@code com.squareup.moshi.adapters.Rfc3339DateJsonAdapter}. */ public final class Rfc3339DateJsonAdapter extends JsonAdapter { - @Override public synchronized Date fromJson(JsonReader reader) throws IOException { - String string = reader.nextString(); - return Iso8601Utils.parse(string); + com.squareup.moshi.adapters.Rfc3339DateJsonAdapter delegate + = new com.squareup.moshi.adapters.Rfc3339DateJsonAdapter(); + + @Override public Date fromJson(JsonReader reader) throws IOException { + return delegate.fromJson(reader); } - @Override public synchronized void toJson(JsonWriter writer, Date value) throws IOException { - String string = Iso8601Utils.format(value); - writer.value(string); + @Override public void toJson(JsonWriter writer, Date value) throws IOException { + delegate.toJson(writer, value); } } diff --git a/adapters/src/main/java/com/squareup/moshi/Iso8601Utils.java b/adapters/src/main/java/com/squareup/moshi/adapters/Iso8601Utils.java similarity index 99% rename from adapters/src/main/java/com/squareup/moshi/Iso8601Utils.java rename to adapters/src/main/java/com/squareup/moshi/adapters/Iso8601Utils.java index 49c2ce8..b7d3952 100644 --- a/adapters/src/main/java/com/squareup/moshi/Iso8601Utils.java +++ b/adapters/src/main/java/com/squareup/moshi/adapters/Iso8601Utils.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.squareup.moshi; +package com.squareup.moshi.adapters; +import com.squareup.moshi.JsonDataException; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; diff --git a/adapters/src/main/java/com/squareup/moshi/adapters/Rfc3339DateJsonAdapter.java b/adapters/src/main/java/com/squareup/moshi/adapters/Rfc3339DateJsonAdapter.java new file mode 100644 index 0000000..158b26a --- /dev/null +++ b/adapters/src/main/java/com/squareup/moshi/adapters/Rfc3339DateJsonAdapter.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.moshi.adapters; + +import com.squareup.moshi.JsonAdapter; +import com.squareup.moshi.JsonReader; +import com.squareup.moshi.JsonWriter; +import java.io.IOException; +import java.util.Date; + +/** + * Formats dates using RFC 3339, which is + * formatted like {@code 2015-09-26T18:23:50.250Z}. + */ +public final class Rfc3339DateJsonAdapter extends JsonAdapter { + @Override public synchronized Date fromJson(JsonReader reader) throws IOException { + String string = reader.nextString(); + return Iso8601Utils.parse(string); + } + + @Override public synchronized void toJson(JsonWriter writer, Date value) throws IOException { + String string = Iso8601Utils.format(value); + writer.value(string); + } +} diff --git a/adapters/src/test/java/com/squareup/moshi/Rfc3339DateJsonAdapterTest.java b/adapters/src/test/java/com/squareup/moshi/adapters/Rfc3339DateJsonAdapterTest.java similarity index 95% rename from adapters/src/test/java/com/squareup/moshi/Rfc3339DateJsonAdapterTest.java rename to adapters/src/test/java/com/squareup/moshi/adapters/Rfc3339DateJsonAdapterTest.java index f7bab59..978ee42 100644 --- a/adapters/src/test/java/com/squareup/moshi/Rfc3339DateJsonAdapterTest.java +++ b/adapters/src/test/java/com/squareup/moshi/adapters/Rfc3339DateJsonAdapterTest.java @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.squareup.moshi; +package com.squareup.moshi.adapters; +import com.squareup.moshi.JsonAdapter; +import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; diff --git a/examples/src/main/java/com/squareup/moshi/recipes/ReadAndWriteRfc3339Dates.java b/examples/src/main/java/com/squareup/moshi/recipes/ReadAndWriteRfc3339Dates.java index b48090a..101a00e 100644 --- a/examples/src/main/java/com/squareup/moshi/recipes/ReadAndWriteRfc3339Dates.java +++ b/examples/src/main/java/com/squareup/moshi/recipes/ReadAndWriteRfc3339Dates.java @@ -17,7 +17,7 @@ package com.squareup.moshi.recipes; import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.Moshi; -import com.squareup.moshi.Rfc3339DateJsonAdapter; +import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter; import com.squareup.moshi.recipes.models.Tournament; import java.util.Calendar; import java.util.Date; diff --git a/kotlin/pom.xml b/kotlin/pom.xml index e54f041..7c45388 100644 --- a/kotlin/pom.xml +++ b/kotlin/pom.xml @@ -84,6 +84,17 @@ + + org.apache.maven.plugins + maven-jar-plugin + + + + com.squareup.moshi.kotlin + + + + diff --git a/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt b/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt index 4a8736f..be25787 100644 --- a/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt +++ b/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt @@ -15,223 +15,9 @@ */ package com.squareup.moshi -import java.lang.reflect.Modifier -import java.lang.reflect.Type -import java.util.AbstractMap.SimpleEntry -import kotlin.collections.Map.Entry -import kotlin.reflect.KFunction -import kotlin.reflect.KMutableProperty1 -import kotlin.reflect.KParameter -import kotlin.reflect.KProperty1 -import kotlin.reflect.full.findAnnotation -import kotlin.reflect.full.memberProperties -import kotlin.reflect.full.primaryConstructor -import kotlin.reflect.jvm.isAccessible -import kotlin.reflect.jvm.javaField -import kotlin.reflect.jvm.javaType - -/** Classes annotated with this are eligible for this adapter. */ -private val KOTLIN_METADATA = Class.forName("kotlin.Metadata") as Class - -/** - * Placeholder value used when a field is absent from the JSON. Note that this code - * distinguishes between absent values and present-but-null values. - */ -private object ABSENT_VALUE - -/** - * This class encodes Kotlin classes using their properties. It decodes them by first invoking the - * constructor, and then by setting any additional properties that exist, if any. - */ -internal class KotlinJsonAdapter( - val constructor: KFunction, - val bindings: List?>, - val options: JsonReader.Options) : JsonAdapter() { - - override fun fromJson(reader: JsonReader): T { - val constructorSize = constructor.parameters.size - - // Read each value into its slot in the array. - val values = Array(bindings.size) { ABSENT_VALUE } - reader.beginObject() - while (reader.hasNext()) { - val index = reader.selectName(options) - val binding = if (index != -1) bindings[index] else null - - if (binding == null) { - reader.nextName() - reader.skipValue() - continue - } - - if (values[index] !== ABSENT_VALUE) { - throw JsonDataException( - "Multiple values for ${constructor.parameters[index].name} at ${reader.path}") - } - - values[index] = binding.adapter.fromJson(reader) - } - reader.endObject() - - // Confirm all parameters are present, optional, or nullable. - for (i in 0 until constructorSize) { - if (values[i] === ABSENT_VALUE && !constructor.parameters[i].isOptional) { - if (!constructor.parameters[i].type.isMarkedNullable) { - throw JsonDataException( - "Required value ${constructor.parameters[i].name} missing at ${reader.path}") - } - values[i] = null // Replace absent with null. - } else if (values[i] == null && !constructor.parameters[i].type.isMarkedNullable) { - throw JsonDataException("Non-null value ${constructor.parameters[i].name} " + - "was null at ${reader.path}") - } - } - - // Call the constructor using a Map so that absent optionals get defaults. - val result = constructor.callBy(IndexedParameterMap(constructor.parameters, values)) - - // Set remaining properties. - for (i in constructorSize until bindings.size) { - val binding = bindings[i]!! - val value = values[i] - if (value == null && !binding.property.returnType.isMarkedNullable) { - throw JsonDataException("Non-null value ${binding.property.name} " + - "was null at ${reader.path}") - } - binding.set(result, value) - } - - return result - } - - 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. - - writer.name(binding.name) - binding.adapter.toJson(writer, binding.get(value)) - } - writer.endObject() - } - - override fun toString() = "KotlinJsonAdapter(${constructor.returnType})" - - data class Binding( - val name: String, - val adapter: JsonAdapter

, - val property: KProperty1, - val parameter: KParameter?) { - fun get(value: K) = property.get(value) - - fun set(result: K, value: P) { - if (value !== ABSENT_VALUE) { - (property as KMutableProperty1).set(result, value) - } - } - } - - /** A simple [Map] that uses parameter indexes instead of sorting or hashing. */ - class IndexedParameterMap(val parameterKeys: List, val parameterValues: Array) - : AbstractMap() { - - override val entries: Set> - get() { - val allPossibleEntries = parameterKeys.mapIndexed { index, value -> - SimpleEntry(value, parameterValues[index]) - } - return allPossibleEntries.filterTo(LinkedHashSet>()) { - it.value !== ABSENT_VALUE - } - } - - override fun containsKey(key: KParameter) = parameterValues[key.index] !== ABSENT_VALUE - - override fun get(key: KParameter): Any? { - val value = parameterValues[key.index] - return if (value !== ABSENT_VALUE) value else null - } - } -} - -class KotlinJsonAdapterFactory : JsonAdapter.Factory { - override fun create(type: Type, annotations: MutableSet, moshi: Moshi) - : JsonAdapter<*>? { - if (!annotations.isEmpty()) return null - - val rawType = Types.getRawType(type) - if (rawType.isInterface) return null - if (rawType.isEnum) return null - if (!rawType.isAnnotationPresent(KOTLIN_METADATA)) return null - if (ClassJsonAdapter.isPlatformType(rawType)) return null - - if (rawType.isLocalClass) { - throw IllegalArgumentException("Cannot serialize local class or object expression ${rawType.name}") - } - val rawTypeKotlin = rawType.kotlin - if (rawTypeKotlin.isAbstract) { - throw IllegalArgumentException("Cannot serialize abstract class ${rawType.name}") - } - if (rawTypeKotlin.isInner) { - throw IllegalArgumentException("Cannot serialize inner class ${rawType.name}") - } - if (rawTypeKotlin.objectInstance != null) { - throw IllegalArgumentException("Cannot serialize object declaration ${rawType.name}") - } - - val constructor = rawTypeKotlin.primaryConstructor ?: return null - val parametersByName = constructor.parameters.associateBy { it.name } - constructor.isAccessible = true - - val bindingsByName = LinkedHashMap>() - - for (property in rawTypeKotlin.memberProperties) { - val parameter = parametersByName[property.name] - - if (Modifier.isTransient(property.javaField?.modifiers ?: 0)) { - if (parameter != null && !parameter.isOptional) { - throw IllegalArgumentException( - "No default value for transient constructor $parameter") - } - continue - } - - if (property !is KMutableProperty1 && parameter == null) continue - - property.isAccessible = true - var allAnnotations = property.annotations - var jsonAnnotation = property.findAnnotation() - - if (parameter != null) { - allAnnotations += parameter.annotations - if (jsonAnnotation == null) { - jsonAnnotation = parameter.findAnnotation() - } - } - - val name = jsonAnnotation?.name ?: property.name - val adapter = moshi.adapter( - property.returnType.javaType, Util.jsonAnnotations(allAnnotations.toTypedArray())) - - bindingsByName[property.name] = - KotlinJsonAdapter.Binding(name, adapter, property as KProperty1, parameter) - } - - val bindings = ArrayList?>() - - for (parameter in constructor.parameters) { - val binding = bindingsByName.remove(parameter.name) - if (binding == null && !parameter.isOptional) { - throw IllegalArgumentException("No property for required constructor ${parameter}") - } - bindings += binding - } - - bindings += bindingsByName.values - - val options = JsonReader.Options.of(*bindings.map { it?.name ?: "\u0000" }.toTypedArray()) - return KotlinJsonAdapter(constructor, bindings, options).nullSafe() - } -} +@Deprecated( + message = "this moved to avoid a package name conflict in the Java Platform Module System.", + replaceWith = ReplaceWith("com.squareup.moshi.kotlin.KotlinJsonAdapterFactory") +) +class KotlinJsonAdapterFactory + : JsonAdapter.Factory by com.squareup.moshi.kotlin.KotlinJsonAdapterFactory() diff --git a/kotlin/src/main/java/com/squareup/moshi/kotlin/KotlinJsonAdapter.kt b/kotlin/src/main/java/com/squareup/moshi/kotlin/KotlinJsonAdapter.kt new file mode 100644 index 0000000..b50a8c1 --- /dev/null +++ b/kotlin/src/main/java/com/squareup/moshi/kotlin/KotlinJsonAdapter.kt @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2017 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.moshi.kotlin + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.JsonDataException +import com.squareup.moshi.JsonReader +import com.squareup.moshi.JsonWriter +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types +import com.squareup.moshi.internal.Util +import java.lang.reflect.Modifier +import java.lang.reflect.Type +import java.util.AbstractMap.SimpleEntry +import kotlin.collections.Map.Entry +import kotlin.reflect.KFunction +import kotlin.reflect.KMutableProperty1 +import kotlin.reflect.KParameter +import kotlin.reflect.KProperty1 +import kotlin.reflect.full.findAnnotation +import kotlin.reflect.full.memberProperties +import kotlin.reflect.full.primaryConstructor +import kotlin.reflect.jvm.isAccessible +import kotlin.reflect.jvm.javaField +import kotlin.reflect.jvm.javaType + +/** Classes annotated with this are eligible for this adapter. */ +private val KOTLIN_METADATA = Class.forName("kotlin.Metadata") as Class + +/** + * Placeholder value used when a field is absent from the JSON. Note that this code + * distinguishes between absent values and present-but-null values. + */ +private object ABSENT_VALUE + +/** + * This class encodes Kotlin classes using their properties. It decodes them by first invoking the + * constructor, and then by setting any additional properties that exist, if any. + */ +internal class KotlinJsonAdapter( + val constructor: KFunction, + val bindings: List?>, + val options: JsonReader.Options) : JsonAdapter() { + + override fun fromJson(reader: JsonReader): T { + val constructorSize = constructor.parameters.size + + // Read each value into its slot in the array. + val values = Array(bindings.size) { ABSENT_VALUE } + reader.beginObject() + while (reader.hasNext()) { + val index = reader.selectName(options) + val binding = if (index != -1) bindings[index] else null + + if (binding == null) { + reader.nextName() + reader.skipValue() + continue + } + + if (values[index] !== ABSENT_VALUE) { + throw JsonDataException( + "Multiple values for ${constructor.parameters[index].name} at ${reader.path}") + } + + values[index] = binding.adapter.fromJson(reader) + } + reader.endObject() + + // Confirm all parameters are present, optional, or nullable. + for (i in 0 until constructorSize) { + if (values[i] === ABSENT_VALUE && !constructor.parameters[i].isOptional) { + if (!constructor.parameters[i].type.isMarkedNullable) { + throw JsonDataException( + "Required value ${constructor.parameters[i].name} missing at ${reader.path}") + } + values[i] = null // Replace absent with null. + } else if (values[i] == null && !constructor.parameters[i].type.isMarkedNullable) { + throw JsonDataException("Non-null value ${constructor.parameters[i].name} " + + "was null at ${reader.path}") + } + } + + // Call the constructor using a Map so that absent optionals get defaults. + val result = constructor.callBy(IndexedParameterMap(constructor.parameters, values)) + + // Set remaining properties. + for (i in constructorSize until bindings.size) { + val binding = bindings[i]!! + val value = values[i] + if (value == null && !binding.property.returnType.isMarkedNullable) { + throw JsonDataException("Non-null value ${binding.property.name} " + + "was null at ${reader.path}") + } + binding.set(result, value) + } + + return result + } + + 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. + + writer.name(binding.name) + binding.adapter.toJson(writer, binding.get(value)) + } + writer.endObject() + } + + override fun toString() = "KotlinJsonAdapter(${constructor.returnType})" + + data class Binding( + val name: String, + val adapter: JsonAdapter

, + val property: KProperty1, + val parameter: KParameter?) { + fun get(value: K) = property.get(value) + + fun set(result: K, value: P) { + if (value !== ABSENT_VALUE) { + (property as KMutableProperty1).set(result, value) + } + } + } + + /** A simple [Map] that uses parameter indexes instead of sorting or hashing. */ + class IndexedParameterMap(val parameterKeys: List, val parameterValues: Array) + : AbstractMap() { + + override val entries: Set> + get() { + val allPossibleEntries = parameterKeys.mapIndexed { index, value -> + SimpleEntry(value, parameterValues[index]) + } + return allPossibleEntries.filterTo(LinkedHashSet>()) { + it.value !== ABSENT_VALUE + } + } + + override fun containsKey(key: KParameter) = parameterValues[key.index] !== ABSENT_VALUE + + override fun get(key: KParameter): Any? { + val value = parameterValues[key.index] + return if (value !== ABSENT_VALUE) value else null + } + } +} + +class KotlinJsonAdapterFactory : JsonAdapter.Factory { + override fun create(type: Type, annotations: MutableSet, moshi: Moshi) + : JsonAdapter<*>? { + if (!annotations.isEmpty()) return null + + val rawType = Types.getRawType(type) + if (rawType.isInterface) return null + if (rawType.isEnum) return null + if (!rawType.isAnnotationPresent(KOTLIN_METADATA)) return null + if (Util.isPlatformType(rawType)) return null + + if (rawType.isLocalClass) { + throw IllegalArgumentException("Cannot serialize local class or object expression ${rawType.name}") + } + val rawTypeKotlin = rawType.kotlin + if (rawTypeKotlin.isAbstract) { + throw IllegalArgumentException("Cannot serialize abstract class ${rawType.name}") + } + if (rawTypeKotlin.isInner) { + throw IllegalArgumentException("Cannot serialize inner class ${rawType.name}") + } + if (rawTypeKotlin.objectInstance != null) { + throw IllegalArgumentException("Cannot serialize object declaration ${rawType.name}") + } + + val constructor = rawTypeKotlin.primaryConstructor ?: return null + val parametersByName = constructor.parameters.associateBy { it.name } + constructor.isAccessible = true + + val bindingsByName = LinkedHashMap>() + + for (property in rawTypeKotlin.memberProperties) { + val parameter = parametersByName[property.name] + + if (Modifier.isTransient(property.javaField?.modifiers ?: 0)) { + if (parameter != null && !parameter.isOptional) { + throw IllegalArgumentException( + "No default value for transient constructor $parameter") + } + continue + } + + if (property !is KMutableProperty1 && parameter == null) continue + + property.isAccessible = true + var allAnnotations = property.annotations + var jsonAnnotation = property.findAnnotation() + + if (parameter != null) { + allAnnotations += parameter.annotations + if (jsonAnnotation == null) { + jsonAnnotation = parameter.findAnnotation() + } + } + + val name = jsonAnnotation?.name ?: property.name + val adapter = moshi.adapter( + property.returnType.javaType, Util.jsonAnnotations(allAnnotations.toTypedArray())) + + bindingsByName[property.name] = + KotlinJsonAdapter.Binding(name, adapter, property as KProperty1, parameter) + } + + val bindings = ArrayList?>() + + for (parameter in constructor.parameters) { + val binding = bindingsByName.remove(parameter.name) + if (binding == null && !parameter.isOptional) { + throw IllegalArgumentException("No property for required constructor ${parameter}") + } + bindings += binding + } + + bindings += bindingsByName.values + + val options = JsonReader.Options.of(*bindings.map { it?.name ?: "\u0000" }.toTypedArray()) + 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/kotlin/KotlinJsonAdapterTest.kt similarity index 96% rename from kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt rename to kotlin/src/test/java/com/squareup/moshi/kotlin/KotlinJsonAdapterTest.kt index 1af9183..e25f23b 100644 --- a/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt +++ b/kotlin/src/test/java/com/squareup/moshi/kotlin/KotlinJsonAdapterTest.kt @@ -13,8 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.squareup.moshi +package com.squareup.moshi.kotlin +import com.squareup.moshi.FromJson +import com.squareup.moshi.Json +import com.squareup.moshi.JsonDataException +import com.squareup.moshi.JsonQualifier +import com.squareup.moshi.Moshi +import com.squareup.moshi.ToJson import org.assertj.core.api.Assertions.assertThat import org.junit.Assert.fail import org.junit.Test @@ -315,7 +321,7 @@ class KotlinJsonAdapterTest { } catch (expected: IllegalArgumentException) { assertThat(expected).hasMessage("No default value for transient constructor parameter #0 " + "a of fun (kotlin.Int): " + - "com.squareup.moshi.KotlinJsonAdapterTest.RequiredTransientConstructorParameter") + "com.squareup.moshi.kotlin.KotlinJsonAdapterTest.RequiredTransientConstructorParameter") } } @@ -587,7 +593,7 @@ class KotlinJsonAdapterTest { fail() } catch (e: IllegalArgumentException) { assertThat(e).hasMessage("No JsonAdapter for interface " + - "com.squareup.moshi.KotlinJsonAdapterTest\$Interface annotated []") + "com.squareup.moshi.kotlin.KotlinJsonAdapterTest\$Interface annotated []") } } @@ -599,8 +605,8 @@ class KotlinJsonAdapterTest { moshi.adapter(AbstractClass::class.java) fail() } catch (e: IllegalArgumentException) { - assertThat(e).hasMessage( - "Cannot serialize abstract class com.squareup.moshi.KotlinJsonAdapterTest\$AbstractClass") + assertThat(e).hasMessage("Cannot serialize abstract class " + + "com.squareup.moshi.kotlin.KotlinJsonAdapterTest\$AbstractClass") } } @@ -612,8 +618,8 @@ class KotlinJsonAdapterTest { moshi.adapter(InnerClass::class.java) fail() } catch (e: IllegalArgumentException) { - assertThat(e).hasMessage( - "Cannot serialize inner class com.squareup.moshi.KotlinJsonAdapterTest\$InnerClass") + assertThat(e).hasMessage("Cannot serialize inner class " + + "com.squareup.moshi.kotlin.KotlinJsonAdapterTest\$InnerClass") } } @@ -627,7 +633,7 @@ class KotlinJsonAdapterTest { fail() } catch (e: IllegalArgumentException) { assertThat(e).hasMessage("Cannot serialize local class or object expression " + - "com.squareup.moshi.KotlinJsonAdapterTest\$localClassesNotSupported\$LocalClass") + "com.squareup.moshi.kotlin.KotlinJsonAdapterTest\$localClassesNotSupported\$LocalClass") } } @@ -638,7 +644,7 @@ class KotlinJsonAdapterTest { fail() } catch (e: IllegalArgumentException) { assertThat(e).hasMessage("Cannot serialize object declaration " + - "com.squareup.moshi.KotlinJsonAdapterTest\$ObjectDeclaration") + "com.squareup.moshi.kotlin.KotlinJsonAdapterTest\$ObjectDeclaration") } } @@ -656,7 +662,8 @@ class KotlinJsonAdapterTest { fail() } catch (e: IllegalArgumentException) { assertThat(e).hasMessage("Cannot serialize local class or object expression " + - "com.squareup.moshi.KotlinJsonAdapterTest\$objectExpressionsNotSupported\$expression$1") + "com.squareup.moshi.kotlin.KotlinJsonAdapterTest\$objectExpressionsNotSupported" + + "\$expression$1") } } diff --git a/moshi/pom.xml b/moshi/pom.xml index a915c33..ad32ad4 100644 --- a/moshi/pom.xml +++ b/moshi/pom.xml @@ -33,4 +33,20 @@ test + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + com.squareup.moshi + + + + + + diff --git a/moshi/src/main/java/com/squareup/moshi/AdapterMethodsFactory.java b/moshi/src/main/java/com/squareup/moshi/AdapterMethodsFactory.java index 5bfc3bc..266e018 100644 --- a/moshi/src/main/java/com/squareup/moshi/AdapterMethodsFactory.java +++ b/moshi/src/main/java/com/squareup/moshi/AdapterMethodsFactory.java @@ -15,6 +15,7 @@ */ package com.squareup.moshi; +import com.squareup.moshi.internal.Util; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; @@ -26,7 +27,7 @@ import java.util.List; import java.util.Set; import javax.annotation.Nullable; -import static com.squareup.moshi.Util.jsonAnnotations; +import static com.squareup.moshi.internal.Util.jsonAnnotations; final class AdapterMethodsFactory implements JsonAdapter.Factory { private final List toAdapters; diff --git a/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java b/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java index 76c929d..1508240 100644 --- a/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java +++ b/moshi/src/main/java/com/squareup/moshi/ClassJsonAdapter.java @@ -15,6 +15,7 @@ */ package com.squareup.moshi; +import com.squareup.moshi.internal.Util; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Field; @@ -48,7 +49,7 @@ final class ClassJsonAdapter extends JsonAdapter { if (!(type instanceof Class)) return null; Class rawType = (Class) type; if (rawType.isInterface() || rawType.isEnum()) return null; - if (isPlatformType(rawType) && !Types.isAllowedPlatformType(rawType)) { + if (Util.isPlatformType(rawType) && !Types.isAllowedPlatformType(rawType)) { throw new IllegalArgumentException("Platform " + type + " annotated " @@ -83,7 +84,7 @@ final class ClassJsonAdapter extends JsonAdapter { private void createFieldBindings( Moshi moshi, Type type, Map> fieldBindings) { Class rawType = Types.getRawType(type); - boolean platformType = isPlatformType(rawType); + boolean platformType = Util.isPlatformType(rawType); for (Field field : rawType.getDeclaredFields()) { if (!includeField(platformType, field.getModifiers())) continue; @@ -115,19 +116,6 @@ final class ClassJsonAdapter extends JsonAdapter { } }; - /** - * Returns true if {@code rawType} is built in. We don't reflect on private fields of platform - * types because they're unspecified and likely to be different on Java vs. Android. - */ - static boolean isPlatformType(Class rawType) { - String name = rawType.getName(); - return name.startsWith("android.") - || name.startsWith("java.") - || name.startsWith("javax.") - || name.startsWith("kotlin.") - || name.startsWith("scala."); - } - private final ClassFactory classFactory; private final FieldBinding[] fieldsArray; private final JsonReader.Options options; diff --git a/moshi/src/main/java/com/squareup/moshi/Moshi.java b/moshi/src/main/java/com/squareup/moshi/Moshi.java index 7ccfb92..315f6fb 100644 --- a/moshi/src/main/java/com/squareup/moshi/Moshi.java +++ b/moshi/src/main/java/com/squareup/moshi/Moshi.java @@ -15,6 +15,7 @@ */ package com.squareup.moshi; +import com.squareup.moshi.internal.Util; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Type; diff --git a/moshi/src/main/java/com/squareup/moshi/StandardJsonAdapters.java b/moshi/src/main/java/com/squareup/moshi/StandardJsonAdapters.java index 526dac4..5a7eb46 100644 --- a/moshi/src/main/java/com/squareup/moshi/StandardJsonAdapters.java +++ b/moshi/src/main/java/com/squareup/moshi/StandardJsonAdapters.java @@ -15,6 +15,7 @@ */ package com.squareup.moshi; +import com.squareup.moshi.internal.Util; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Type; diff --git a/moshi/src/main/java/com/squareup/moshi/Util.java b/moshi/src/main/java/com/squareup/moshi/internal/Util.java similarity index 80% rename from moshi/src/main/java/com/squareup/moshi/Util.java rename to moshi/src/main/java/com/squareup/moshi/internal/Util.java index 0d93867..8ee3707 100644 --- a/moshi/src/main/java/com/squareup/moshi/Util.java +++ b/moshi/src/main/java/com/squareup/moshi/internal/Util.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.squareup.moshi; +package com.squareup.moshi.internal; +import com.squareup.moshi.JsonQualifier; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Type; @@ -22,7 +23,7 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; -final class Util { +public final class Util { public static final Set NO_ANNOTATIONS = Collections.emptySet(); private Util() { @@ -66,4 +67,17 @@ final class Util { } return false; } + + /** + * Returns true if {@code rawType} is built in. We don't reflect on private fields of platform + * types because they're unspecified and likely to be different on Java vs. Android. + */ + public static boolean isPlatformType(Class rawType) { + String name = rawType.getName(); + return name.startsWith("android.") + || name.startsWith("java.") + || name.startsWith("javax.") + || name.startsWith("kotlin.") + || name.startsWith("scala."); + } } diff --git a/moshi/src/test/java/com/squareup/moshi/CircularAdaptersTest.java b/moshi/src/test/java/com/squareup/moshi/CircularAdaptersTest.java index 14256f3..00f0363 100644 --- a/moshi/src/test/java/com/squareup/moshi/CircularAdaptersTest.java +++ b/moshi/src/test/java/com/squareup/moshi/CircularAdaptersTest.java @@ -15,6 +15,7 @@ */ package com.squareup.moshi; +import com.squareup.moshi.internal.Util; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; diff --git a/moshi/src/test/java/com/squareup/moshi/ClassJsonAdapterTest.java b/moshi/src/test/java/com/squareup/moshi/ClassJsonAdapterTest.java index ff28f28..5a6fa24 100644 --- a/moshi/src/test/java/com/squareup/moshi/ClassJsonAdapterTest.java +++ b/moshi/src/test/java/com/squareup/moshi/ClassJsonAdapterTest.java @@ -25,7 +25,7 @@ import okio.Buffer; import org.junit.Test; import static com.squareup.moshi.TestUtil.newReader; -import static com.squareup.moshi.Util.NO_ANNOTATIONS; +import static com.squareup.moshi.internal.Util.NO_ANNOTATIONS; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; diff --git a/moshi/src/test/java/com/squareup/moshi/MapJsonAdapterTest.java b/moshi/src/test/java/com/squareup/moshi/MapJsonAdapterTest.java index 68a85c3..dd3e796 100644 --- a/moshi/src/test/java/com/squareup/moshi/MapJsonAdapterTest.java +++ b/moshi/src/test/java/com/squareup/moshi/MapJsonAdapterTest.java @@ -26,7 +26,7 @@ import org.assertj.core.data.MapEntry; import org.junit.Test; import static com.squareup.moshi.TestUtil.newReader; -import static com.squareup.moshi.Util.NO_ANNOTATIONS; +import static com.squareup.moshi.internal.Util.NO_ANNOTATIONS; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; diff --git a/moshi/src/test/java/com/squareup/moshi/MoshiTest.java b/moshi/src/test/java/com/squareup/moshi/MoshiTest.java index 66a1428..12be431 100644 --- a/moshi/src/test/java/com/squareup/moshi/MoshiTest.java +++ b/moshi/src/test/java/com/squareup/moshi/MoshiTest.java @@ -16,6 +16,7 @@ package com.squareup.moshi; import android.util.Pair; +import com.squareup.moshi.internal.Util; import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation;