package com.google.gson.functional; import com.google.gson.Gson; import java.io.IOException; import com.google.gson.annotations.JsonAdapter; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import java.util.LinkedHashMap; import java.util.Map; import junit.framework.TestCase; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; import com.google.gson.reflect.TypeToken; /** * Functional tests for the RuntimeTypeAdapterFactory feature in extras. */ public final class RuntimeTypeAdapterFactoryFunctionalTest extends TestCase { private final Gson gson = new Gson(); /** * This test also ensures that {@link TypeAdapterFactory} registered through {@link JsonAdapter} * work correctly for {@link Gson#getDelegateAdapter(TypeAdapterFactory, TypeToken)}. */ public void testSubclassesAutomaticallySerialzed() throws Exception { fr.inria.diversify.testamplification.logger.Logger.writeTestStart(Thread.currentThread(),this, "testSubclassesAutomaticallySerialzed"); Shape shape = new Circle(26); String json = gson.toJson(shape); shape = gson.fromJson(json, Shape.class); fr.inria.diversify.testamplification.logger.Logger.logAssertArgument(Thread.currentThread(),2409,((com.google.gson.functional.RuntimeTypeAdapterFactoryFunctionalTest.Circle)(shape)).radius); shape = new Square(15); json = gson.toJson(shape); shape = gson.fromJson(json, Shape.class); fr.inria.diversify.testamplification.logger.Logger.logAssertArgument(Thread.currentThread(),2410,((com.google.gson.functional.RuntimeTypeAdapterFactoryFunctionalTest.Square)(shape)).side); fr.inria.diversify.testamplification.logger.Logger.logAssertArgument(Thread.currentThread(),2411,com.google.gson.functional.RuntimeTypeAdapterFactoryFunctionalTest.ShapeType.SQUARE); fr.inria.diversify.testamplification.logger.Logger.logAssertArgument(Thread.currentThread(),2412,shape.type); fr.inria.diversify.testamplification.logger.Logger.writeTestFinish(Thread.currentThread()); } /** * This test also ensures that {@link TypeAdapterFactory} registered through {@link JsonAdapter} * work correctly for {@link Gson#getDelegateAdapter(TypeAdapterFactory, TypeToken)}. */ public void testSubclassesAutomaticallySerialzed_literalMutation1937() throws Exception { fr.inria.diversify.testamplification.logger.Logger.writeTestStart(Thread.currentThread(),this, "testSubclassesAutomaticallySerialzed_literalMutation1937"); Shape shape = new Circle(25); String json = gson.toJson(shape); shape = gson.fromJson(json, Shape.class); fr.inria.diversify.testamplification.logger.Logger.logAssertArgument(Thread.currentThread(),2409,((com.google.gson.functional.RuntimeTypeAdapterFactoryFunctionalTest.Circle)(shape)).radius); shape = new Square(16); json = gson.toJson(shape); shape = gson.fromJson(json, Shape.class); fr.inria.diversify.testamplification.logger.Logger.logAssertArgument(Thread.currentThread(),2410,((com.google.gson.functional.RuntimeTypeAdapterFactoryFunctionalTest.Square)(shape)).side); fr.inria.diversify.testamplification.logger.Logger.logAssertArgument(Thread.currentThread(),2411,com.google.gson.functional.RuntimeTypeAdapterFactoryFunctionalTest.ShapeType.SQUARE); fr.inria.diversify.testamplification.logger.Logger.logAssertArgument(Thread.currentThread(),2412,shape.type); fr.inria.diversify.testamplification.logger.Logger.writeTestFinish(Thread.currentThread()); } @JsonAdapter(value = Shape.JsonAdapterFactory.class) static class Shape { final ShapeType type; Shape(ShapeType type) { this.type = type; } private static final class JsonAdapterFactory extends RuntimeTypeAdapterFactory<Shape> { public JsonAdapterFactory() { super(Shape.class, "type"); registerSubtype(Circle.class, ShapeType.CIRCLE.toString()); registerSubtype(Square.class, ShapeType.SQUARE.toString()); } } } public enum ShapeType { SQUARE, CIRCLE; } private static final class Circle extends Shape { final int radius; Circle(int radius) { super(ShapeType.CIRCLE); this.radius = radius; } } private static final class Square extends Shape { final int side; Square(int side) { super(ShapeType.SQUARE); this.side = side; } } static class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory { private final Class<?> baseType; private final String typeFieldName; private final Map<java.lang.String, java.lang.Class<?>> labelToSubtype = new LinkedHashMap<java.lang.String, java.lang.Class<?>>(); private final Map<java.lang.Class<?>, String> subtypeToLabel = new LinkedHashMap<java.lang.Class<?>, String>(); protected RuntimeTypeAdapterFactory(Class<?> baseType ,String typeFieldName) { if ((typeFieldName == null) || (baseType == null)) { throw new NullPointerException(); } this.baseType = baseType; this.typeFieldName = typeFieldName; } /** * Creates a new runtime type adapter using for {@code baseType} using {@code * typeFieldName} as the type field name. Type field names are case sensitive. */ public static <T>RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) { return new RuntimeTypeAdapterFactory<T>(baseType , typeFieldName); } /** * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as * the type field name. */ public static <T>RuntimeTypeAdapterFactory<T> of(Class<T> baseType) { return new RuntimeTypeAdapterFactory<T>(baseType , "type"); } /** * Registers {@code type} identified by {@code label}. Labels are case * sensitive. * * @throws IllegalArgumentException if either {@code type} or {@code label} * have already been registered on this type adapter. */ public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) { if ((type == null) || (label == null)) { throw new NullPointerException(); } if ((subtypeToLabel.containsKey(type)) || (labelToSubtype.containsKey(label))) { throw new IllegalArgumentException("types and labels must be unique"); } labelToSubtype.put(label, type); subtypeToLabel.put(type, label); return this; } /** * Registers {@code type} identified by its {@link Class#getSimpleName simple * name}. Labels are case sensitive. * * @throws IllegalArgumentException if either {@code type} or its simple name * have already been registered on this type adapter. */ public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) { return registerSubtype(type, type.getSimpleName()); } public <R>TypeAdapter<R> create(Gson gson, TypeToken<R> type) { if ((type.getRawType()) != (baseType)) { return null; } final Map<java.lang.String, com.google.gson.TypeAdapter<?>> labelToDelegate = new LinkedHashMap<java.lang.String, com.google.gson.TypeAdapter<?>>(); final Map<java.lang.Class<?>, TypeAdapter<?>> subtypeToDelegate = new LinkedHashMap<java.lang.Class<?>, TypeAdapter<?>>(); for (Map.Entry<java.lang.String, java.lang.Class<?>> entry : labelToSubtype.entrySet()) { TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); labelToDelegate.put(entry.getKey(), delegate); subtypeToDelegate.put(entry.getValue(), delegate); } return new TypeAdapter<R>() { @Override public R read(JsonReader in) throws IOException { JsonElement jsonElement = com.google.gson.internal.Streams.parse(in); JsonElement labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName); if (labelJsonElement == null) { throw new JsonParseException(((("cannot deserialize " + (baseType)) + " because it does not define a field named ") + (typeFieldName))); } String label = labelJsonElement.getAsString(); @SuppressWarnings(value = "unchecked") TypeAdapter<R> delegate = ((TypeAdapter<R>)(labelToDelegate.get(label))); if (delegate == null) { throw new JsonParseException((((("cannot deserialize " + (baseType)) + " subtype named ") + label) + "; did you forget to register a subtype?")); } return delegate.fromJsonTree(jsonElement); } @Override public void write(JsonWriter out, R value) throws IOException { Class<?> srcType = value.getClass(); String label = subtypeToLabel.get(srcType); @SuppressWarnings(value = "unchecked") TypeAdapter<R> delegate = ((TypeAdapter<R>)(subtypeToDelegate.get(srcType))); if (delegate == null) { throw new JsonParseException((("cannot serialize " + (srcType.getName())) + "; did you forget to register a subtype?")); } JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); if (!(jsonObject.has(typeFieldName))) { JsonObject clone = new JsonObject(); clone.add(typeFieldName, new com.google.gson.JsonPrimitive(label)); for (Map.Entry<java.lang.String, com.google.gson.JsonElement> e : jsonObject.entrySet()) { clone.add(e.getKey(), e.getValue()); } jsonObject = clone; } com.google.gson.internal.Streams.write(jsonObject, out); } }; } } }