diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/RuntimeJsonAdapterFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/RuntimeJsonAdapterFactory.kt index c9bf6cc662..e18d66e5ad 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/RuntimeJsonAdapterFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/RuntimeJsonAdapterFactory.kt @@ -13,157 +13,115 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.matrix.android.sdk.internal.network.parsing -package org.matrix.android.sdk.internal.network.parsing; - -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 java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -import javax.annotation.CheckReturnValue; +import com.squareup.moshi.JsonAdapter +import org.matrix.android.sdk.internal.network.parsing.RuntimeJsonAdapterFactory +import com.squareup.moshi.Moshi +import org.matrix.android.sdk.internal.network.parsing.RuntimeJsonAdapterFactory.RuntimeJsonAdapter +import kotlin.Throws +import com.squareup.moshi.JsonDataException +import com.squareup.moshi.JsonReader +import com.squareup.moshi.JsonWriter +import com.squareup.moshi.Types +import java.io.IOException +import java.lang.IllegalArgumentException +import java.lang.NullPointerException +import java.lang.reflect.Type +import java.util.LinkedHashMap +import javax.annotation.CheckReturnValue /** * A JsonAdapter factory for polymorphic types. This is useful when the type is not known before * decoding the JSON. This factory's adapters expect JSON in the format of a JSON object with a * key whose value is a label that determines the type to which to map the JSON object. */ -public final class RuntimeJsonAdapterFactory implements JsonAdapter.Factory { - final Class baseType; - final String labelKey; - final Class fallbackType; - final Map labelToType = new LinkedHashMap<>(); - - /** - * @param baseType The base type for which this factory will create adapters. Cannot be Object. - * @param labelKey The key in the JSON object whose value determines the type to which to map the - * JSON object. - */ - @CheckReturnValue - public static RuntimeJsonAdapterFactory of(Class baseType, String labelKey, Class fallbackType) { - if (baseType == null) throw new NullPointerException("baseType == null"); - if (labelKey == null) throw new NullPointerException("labelKey == null"); - if (baseType == Object.class) { - throw new IllegalArgumentException( - "The base type must not be Object. Consider using a marker interface."); - } - return new RuntimeJsonAdapterFactory<>(baseType, labelKey, fallbackType); - } - - RuntimeJsonAdapterFactory(Class baseType, String labelKey, Class fallbackType) { - this.baseType = baseType; - this.labelKey = labelKey; - this.fallbackType = fallbackType; - } +class RuntimeJsonAdapterFactory internal constructor(val baseType: Class, val labelKey: String, val fallbackType: Class<*>?) : JsonAdapter.Factory { + val labelToType: MutableMap = LinkedHashMap() /** * Register the subtype that can be created based on the label. When an unknown type is found - * during encoding an {@linkplain IllegalArgumentException} will be thrown. When an unknown label - * is found during decoding a {@linkplain JsonDataException} will be thrown. + * during encoding an [IllegalArgumentException] will be thrown. When an unknown label + * is found during decoding a [JsonDataException] will be thrown. */ - public RuntimeJsonAdapterFactory registerSubtype(Class subtype, String label) { - if (subtype == null) throw new NullPointerException("subtype == null"); - if (label == null) throw new NullPointerException("label == null"); - if (labelToType.containsKey(label) || labelToType.containsValue(subtype)) { - throw new IllegalArgumentException("Subtypes and labels must be unique."); - } - labelToType.put(label, subtype); - return this; + fun registerSubtype(subtype: Class?, label: String?): RuntimeJsonAdapterFactory { + if (subtype == null) throw NullPointerException("subtype == null") + if (label == null) throw NullPointerException("label == null") + require(!(labelToType.containsKey(label) || labelToType.containsValue(subtype))) { "Subtypes and labels must be unique." } + labelToType[label] = subtype + return this } - @Override - public JsonAdapter create(Type type, Set annotations, Moshi moshi) { + override fun create(type: Type, annotations: Set, moshi: Moshi): JsonAdapter<*>? { if (Types.getRawType(type) != baseType || !annotations.isEmpty()) { - return null; + return null } - int size = labelToType.size(); - Map> labelToAdapter = new LinkedHashMap<>(size); - Map typeToLabel = new LinkedHashMap<>(size); - for (Map.Entry entry : labelToType.entrySet()) { - String label = entry.getKey(); - Type typeValue = entry.getValue(); - typeToLabel.put(typeValue, label); - labelToAdapter.put(label, moshi.adapter(typeValue)); + val size = labelToType.size + val labelToAdapter: MutableMap> = LinkedHashMap(size) + val typeToLabel: MutableMap = LinkedHashMap(size) + for ((label, typeValue) in labelToType) { + typeToLabel[typeValue] = label + labelToAdapter[label] = moshi.adapter(typeValue) } - - final JsonAdapter fallbackAdapter = moshi.adapter(fallbackType); - JsonAdapter objectJsonAdapter = moshi.adapter(Object.class); - - return new RuntimeJsonAdapter(labelKey, labelToAdapter, typeToLabel, - objectJsonAdapter, fallbackAdapter).nullSafe(); + val fallbackAdapter = moshi.adapter(fallbackType) + val objectJsonAdapter = moshi.adapter(Any::class.java) + return RuntimeJsonAdapter(labelKey, labelToAdapter, typeToLabel, + objectJsonAdapter, fallbackAdapter).nullSafe() } - static final class RuntimeJsonAdapter extends JsonAdapter { - final String labelKey; - final Map> labelToAdapter; - final Map typeToLabel; - final JsonAdapter objectJsonAdapter; - final JsonAdapter fallbackAdapter; - - RuntimeJsonAdapter(String labelKey, Map> labelToAdapter, - Map typeToLabel, JsonAdapter objectJsonAdapter, - JsonAdapter fallbackAdapter) { - this.labelKey = labelKey; - this.labelToAdapter = labelToAdapter; - this.typeToLabel = typeToLabel; - this.objectJsonAdapter = objectJsonAdapter; - this.fallbackAdapter = fallbackAdapter; - } - - @Override - public Object fromJson(JsonReader reader) throws IOException { - JsonReader.Token peekedToken = reader.peek(); + internal class RuntimeJsonAdapter(val labelKey: String, val labelToAdapter: Map>, + val typeToLabel: Map, val objectJsonAdapter: JsonAdapter, + val fallbackAdapter: JsonAdapter) : JsonAdapter() { + @Throws(IOException::class) + override fun fromJson(reader: JsonReader): Any? { + val peekedToken = reader.peek() if (peekedToken != JsonReader.Token.BEGIN_OBJECT) { - throw new JsonDataException("Expected BEGIN_OBJECT but was " + peekedToken - + " at path " + reader.getPath()); + throw JsonDataException("Expected BEGIN_OBJECT but was " + peekedToken + + " at path " + reader.path) } - Object jsonValue = reader.readJsonValue(); - Map jsonObject = (Map) jsonValue; - Object label = jsonObject.get(labelKey); - if (!(label instanceof String)) { - return null; - } - JsonAdapter adapter = labelToAdapter.get(label); - if (adapter == null) { - return fallbackAdapter.fromJsonValue(jsonValue); - } - return adapter.fromJsonValue(jsonValue); + val jsonValue = reader.readJsonValue() + val jsonObject = jsonValue as Map? + val label = jsonObject!![labelKey] as? String ?: return null + val adapter = labelToAdapter[label] ?: return fallbackAdapter.fromJsonValue(jsonValue) + return adapter.fromJsonValue(jsonValue) } - @Override - public void toJson(JsonWriter writer, Object value) throws IOException { - Class type = value.getClass(); - String label = typeToLabel.get(type); - if (label == null) { - throw new IllegalArgumentException("Expected one of " - + typeToLabel.keySet() - + " but found " - + value - + ", a " - + value.getClass() - + ". Register this subtype."); - } - JsonAdapter adapter = labelToAdapter.get(label); - Map jsonValue = (Map) adapter.toJsonValue(value); - - Map valueWithLabel = new LinkedHashMap<>(1 + jsonValue.size()); - valueWithLabel.put(labelKey, label); - valueWithLabel.putAll(jsonValue); - objectJsonAdapter.toJson(writer, valueWithLabel); + @Throws(IOException::class) + override fun toJson(writer: JsonWriter, value: Any?) { + val type: Class<*> = value!!.javaClass + val label = typeToLabel[type] + ?: throw IllegalArgumentException("Expected one of " + + typeToLabel.keys + + " but found " + + value + + ", a " + + value.javaClass + + ". Register this subtype.") + val adapter = labelToAdapter[label]!! + val jsonValue = adapter.toJsonValue(value) as Map? + val valueWithLabel: MutableMap = LinkedHashMap(1 + jsonValue!!.size) + valueWithLabel[labelKey] = label + valueWithLabel.putAll(jsonValue) + objectJsonAdapter.toJson(writer, valueWithLabel) } - @Override - public String toString() { - return "RuntimeJsonAdapter(" + labelKey + ")"; + override fun toString(): String { + return "RuntimeJsonAdapter($labelKey)" } } -} + + companion object { + /** + * @param baseType The base type for which this factory will create adapters. Cannot be Object. + * @param labelKey The key in the JSON object whose value determines the type to which to map the + * JSON object. + */ + @CheckReturnValue + fun of(baseType: Class?, labelKey: String?, fallbackType: Class?): RuntimeJsonAdapterFactory { + if (baseType == null) throw NullPointerException("baseType == null") + if (labelKey == null) throw NullPointerException("labelKey == null") + require(baseType != Any::class.java) { "The base type must not be Object. Consider using a marker interface." } + return RuntimeJsonAdapterFactory(baseType, labelKey, fallbackType) + } + } +} \ No newline at end of file