diff --git a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java index 716b6ec0..1294ebf2 100644 --- a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java +++ b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java @@ -120,35 +120,35 @@ final class DefaultTypeAdapters { ParameterizedTypeHandlerMap> map = new ParameterizedTypeHandlerMap>(); - map.register(URL.class, URL_TYPE_ADAPTER); - map.register(URI.class, URI_TYPE_ADAPTER); - map.register(UUID.class, UUUID_TYPE_ADAPTER); - map.register(Locale.class, LOCALE_TYPE_ADAPTER); - map.register(Date.class, DATE_TYPE_ADAPTER); - map.register(java.sql.Date.class, JAVA_SQL_DATE_TYPE_ADAPTER); - map.register(Timestamp.class, DATE_TYPE_ADAPTER); - map.register(Time.class, TIME_TYPE_ADAPTER); - map.register(Calendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER); - map.register(GregorianCalendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER); - map.register(BigDecimal.class, BIG_DECIMAL_TYPE_ADAPTER); - map.register(BigInteger.class, BIG_INTEGER_TYPE_ADAPTER); - map.register(BitSet.class, BIT_SET_ADAPTER); + map.register(URL.class, URL_TYPE_ADAPTER, true); + map.register(URI.class, URI_TYPE_ADAPTER, true); + map.register(UUID.class, UUUID_TYPE_ADAPTER, true); + map.register(Locale.class, LOCALE_TYPE_ADAPTER, true); + map.register(Date.class, DATE_TYPE_ADAPTER, true); + map.register(java.sql.Date.class, JAVA_SQL_DATE_TYPE_ADAPTER, true); + map.register(Timestamp.class, DATE_TYPE_ADAPTER, true); + map.register(Time.class, TIME_TYPE_ADAPTER, true); + map.register(Calendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER, true); + map.register(GregorianCalendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER, true); + map.register(BigDecimal.class, BIG_DECIMAL_TYPE_ADAPTER, true); + map.register(BigInteger.class, BIG_INTEGER_TYPE_ADAPTER, true); + map.register(BitSet.class, BIT_SET_ADAPTER, true); // Add primitive serializers - map.register(Boolean.class, BOOLEAN_TYPE_ADAPTER); - map.register(boolean.class, BOOLEAN_TYPE_ADAPTER); - map.register(Byte.class, BYTE_TYPE_ADAPTER); - map.register(byte.class, BYTE_TYPE_ADAPTER); - map.register(Character.class, CHARACTER_TYPE_ADAPTER); - map.register(char.class, CHARACTER_TYPE_ADAPTER); - map.register(Integer.class, INTEGER_TYPE_ADAPTER); - map.register(int.class, INTEGER_TYPE_ADAPTER); - map.register(Number.class, NUMBER_TYPE_ADAPTER); - map.register(Short.class, SHORT_TYPE_ADAPTER); - map.register(short.class, SHORT_TYPE_ADAPTER); - map.register(String.class, STRING_TYPE_ADAPTER); - map.register(StringBuilder.class, STRING_BUILDER_TYPE_ADAPTER); - map.register(StringBuffer.class, STRING_BUFFER_TYPE_ADAPTER); + map.register(Boolean.class, BOOLEAN_TYPE_ADAPTER, true); + map.register(boolean.class, BOOLEAN_TYPE_ADAPTER, true); + map.register(Byte.class, BYTE_TYPE_ADAPTER, true); + map.register(byte.class, BYTE_TYPE_ADAPTER, true); + map.register(Character.class, CHARACTER_TYPE_ADAPTER, true); + map.register(char.class, CHARACTER_TYPE_ADAPTER, true); + map.register(Integer.class, INTEGER_TYPE_ADAPTER, true); + map.register(int.class, INTEGER_TYPE_ADAPTER, true); + map.register(Number.class, NUMBER_TYPE_ADAPTER, true); + map.register(Short.class, SHORT_TYPE_ADAPTER, true); + map.register(short.class, SHORT_TYPE_ADAPTER, true); + map.register(String.class, STRING_TYPE_ADAPTER, true); + map.register(StringBuilder.class, STRING_BUILDER_TYPE_ADAPTER, true); + map.register(StringBuffer.class, STRING_BUFFER_TYPE_ADAPTER, true); map.makeUnmodifiable(); return map; @@ -157,10 +157,10 @@ final class DefaultTypeAdapters { private static ParameterizedTypeHandlerMap> createDefaultHierarchySerializers() { ParameterizedTypeHandlerMap> map = new ParameterizedTypeHandlerMap>(); - map.registerForTypeHierarchy(Enum.class, ENUM_TYPE_ADAPTER); - map.registerForTypeHierarchy(InetAddress.class, INET_ADDRESS_ADAPTER); - map.registerForTypeHierarchy(Collection.class, COLLECTION_TYPE_ADAPTER); - map.registerForTypeHierarchy(Map.class, MAP_TYPE_ADAPTER); + map.registerForTypeHierarchy(Enum.class, ENUM_TYPE_ADAPTER, true); + map.registerForTypeHierarchy(InetAddress.class, INET_ADDRESS_ADAPTER, true); + map.registerForTypeHierarchy(Collection.class, COLLECTION_TYPE_ADAPTER, true); + map.registerForTypeHierarchy(Map.class, MAP_TYPE_ADAPTER, true); map.makeUnmodifiable(); return map; } @@ -168,41 +168,41 @@ final class DefaultTypeAdapters { private static ParameterizedTypeHandlerMap> createDefaultDeserializers() { ParameterizedTypeHandlerMap> map = new ParameterizedTypeHandlerMap>(); - map.register(URL.class, wrapDeserializer(URL_TYPE_ADAPTER)); - map.register(URI.class, wrapDeserializer(URI_TYPE_ADAPTER)); - map.register(UUID.class, wrapDeserializer(UUUID_TYPE_ADAPTER)); - map.register(Locale.class, wrapDeserializer(LOCALE_TYPE_ADAPTER)); - map.register(Date.class, wrapDeserializer(DATE_TYPE_ADAPTER)); - map.register(java.sql.Date.class, wrapDeserializer(JAVA_SQL_DATE_TYPE_ADAPTER)); - map.register(Timestamp.class, wrapDeserializer(TIMESTAMP_DESERIALIZER)); - map.register(Time.class, wrapDeserializer(TIME_TYPE_ADAPTER)); - map.register(Calendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER); - map.register(GregorianCalendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER); - map.register(BigDecimal.class, BIG_DECIMAL_TYPE_ADAPTER); - map.register(BigInteger.class, BIG_INTEGER_TYPE_ADAPTER); - map.register(BitSet.class, BIT_SET_ADAPTER); + map.register(URL.class, wrapDeserializer(URL_TYPE_ADAPTER), true); + map.register(URI.class, wrapDeserializer(URI_TYPE_ADAPTER), true); + map.register(UUID.class, wrapDeserializer(UUUID_TYPE_ADAPTER), true); + map.register(Locale.class, wrapDeserializer(LOCALE_TYPE_ADAPTER), true); + map.register(Date.class, wrapDeserializer(DATE_TYPE_ADAPTER), true); + map.register(java.sql.Date.class, wrapDeserializer(JAVA_SQL_DATE_TYPE_ADAPTER), true); + map.register(Timestamp.class, wrapDeserializer(TIMESTAMP_DESERIALIZER), true); + map.register(Time.class, wrapDeserializer(TIME_TYPE_ADAPTER), true); + map.register(Calendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER, true); + map.register(GregorianCalendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER, true); + map.register(BigDecimal.class, BIG_DECIMAL_TYPE_ADAPTER, true); + map.register(BigInteger.class, BIG_INTEGER_TYPE_ADAPTER, true); + map.register(BitSet.class, BIT_SET_ADAPTER, true); // Add primitive deserializers - map.register(Boolean.class, BOOLEAN_TYPE_ADAPTER); - map.register(boolean.class, BOOLEAN_TYPE_ADAPTER); - map.register(Byte.class, BYTE_TYPE_ADAPTER); - map.register(byte.class, BYTE_TYPE_ADAPTER); - map.register(Character.class, wrapDeserializer(CHARACTER_TYPE_ADAPTER)); - map.register(char.class, wrapDeserializer(CHARACTER_TYPE_ADAPTER)); - map.register(Double.class, DOUBLE_TYPE_ADAPTER); - map.register(double.class, DOUBLE_TYPE_ADAPTER); - map.register(Float.class, FLOAT_TYPE_ADAPTER); - map.register(float.class, FLOAT_TYPE_ADAPTER); - map.register(Integer.class, INTEGER_TYPE_ADAPTER); - map.register(int.class, INTEGER_TYPE_ADAPTER); - map.register(Long.class, LONG_DESERIALIZER); - map.register(long.class, LONG_DESERIALIZER); - map.register(Number.class, NUMBER_TYPE_ADAPTER); - map.register(Short.class, SHORT_TYPE_ADAPTER); - map.register(short.class, SHORT_TYPE_ADAPTER); - map.register(String.class, wrapDeserializer(STRING_TYPE_ADAPTER)); - map.register(StringBuilder.class, wrapDeserializer(STRING_BUILDER_TYPE_ADAPTER)); - map.register(StringBuffer.class, wrapDeserializer(STRING_BUFFER_TYPE_ADAPTER)); + map.register(Boolean.class, BOOLEAN_TYPE_ADAPTER, true); + map.register(boolean.class, BOOLEAN_TYPE_ADAPTER, true); + map.register(Byte.class, BYTE_TYPE_ADAPTER, true); + map.register(byte.class, BYTE_TYPE_ADAPTER, true); + map.register(Character.class, wrapDeserializer(CHARACTER_TYPE_ADAPTER), true); + map.register(char.class, wrapDeserializer(CHARACTER_TYPE_ADAPTER), true); + map.register(Double.class, DOUBLE_TYPE_ADAPTER, true); + map.register(double.class, DOUBLE_TYPE_ADAPTER, true); + map.register(Float.class, FLOAT_TYPE_ADAPTER, true); + map.register(float.class, FLOAT_TYPE_ADAPTER, true); + map.register(Integer.class, INTEGER_TYPE_ADAPTER, true); + map.register(int.class, INTEGER_TYPE_ADAPTER, true); + map.register(Long.class, LONG_DESERIALIZER, true); + map.register(long.class, LONG_DESERIALIZER, true); + map.register(Number.class, NUMBER_TYPE_ADAPTER, true); + map.register(Short.class, SHORT_TYPE_ADAPTER, true); + map.register(short.class, SHORT_TYPE_ADAPTER, true); + map.register(String.class, wrapDeserializer(STRING_TYPE_ADAPTER), true); + map.register(StringBuilder.class, wrapDeserializer(STRING_BUILDER_TYPE_ADAPTER), true); + map.register(StringBuffer.class, wrapDeserializer(STRING_BUFFER_TYPE_ADAPTER), true); map.makeUnmodifiable(); return map; @@ -211,10 +211,10 @@ final class DefaultTypeAdapters { private static ParameterizedTypeHandlerMap> createDefaultHierarchyDeserializers() { ParameterizedTypeHandlerMap> map = new ParameterizedTypeHandlerMap>(); - map.registerForTypeHierarchy(Enum.class, wrapDeserializer(ENUM_TYPE_ADAPTER)); - map.registerForTypeHierarchy(InetAddress.class, wrapDeserializer(INET_ADDRESS_ADAPTER)); - map.registerForTypeHierarchy(Collection.class, wrapDeserializer(COLLECTION_TYPE_ADAPTER)); - map.registerForTypeHierarchy(Map.class, wrapDeserializer(MAP_TYPE_ADAPTER)); + map.registerForTypeHierarchy(Enum.class, wrapDeserializer(ENUM_TYPE_ADAPTER), true); + map.registerForTypeHierarchy(InetAddress.class, wrapDeserializer(INET_ADDRESS_ADAPTER), true); + map.registerForTypeHierarchy(Collection.class, wrapDeserializer(COLLECTION_TYPE_ADAPTER), true); + map.registerForTypeHierarchy(Map.class, wrapDeserializer(MAP_TYPE_ADAPTER), true); map.makeUnmodifiable(); return map; } @@ -227,7 +227,7 @@ final class DefaultTypeAdapters { // Map Instance Creators map.registerForTypeHierarchy(Map.class, - new DefaultConstructorCreator(LinkedHashMap.class, allocator)); + new DefaultConstructorCreator(LinkedHashMap.class, allocator), true); // Add Collection type instance creators DefaultConstructorCreator listCreator = @@ -238,10 +238,10 @@ final class DefaultTypeAdapters { new DefaultConstructorCreator(HashSet.class, allocator); DefaultConstructorCreator sortedSetCreator = new DefaultConstructorCreator(TreeSet.class, allocator); - map.registerForTypeHierarchy(Collection.class, listCreator); - map.registerForTypeHierarchy(Queue.class, queueCreator); - map.registerForTypeHierarchy(Set.class, setCreator); - map.registerForTypeHierarchy(SortedSet.class, sortedSetCreator); + map.registerForTypeHierarchy(Collection.class, listCreator, true); + map.registerForTypeHierarchy(Queue.class, queueCreator, true); + map.registerForTypeHierarchy(Set.class, setCreator, true); + map.registerForTypeHierarchy(SortedSet.class, sortedSetCreator, true); map.makeUnmodifiable(); return map; diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index 6fc68126..14554d8d 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -387,6 +387,7 @@ public final class GsonBuilder { deserializeExclusionStrategies.add(strategy); return this; } + /** * Configures Gson to output Json that fits in a page for pretty printing. This option only * affects Json serialization. @@ -488,16 +489,20 @@ public final class GsonBuilder { * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) { + return registerTypeAdapter(type, typeAdapter, false); + } + + private GsonBuilder registerTypeAdapter(Type type, Object typeAdapter, boolean isSystem) { $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer - || typeAdapter instanceof JsonDeserializer || typeAdapter instanceof InstanceCreator); + || typeAdapter instanceof JsonDeserializer || typeAdapter instanceof InstanceCreator); if (typeAdapter instanceof InstanceCreator) { - registerInstanceCreator(type, (InstanceCreator) typeAdapter); + registerInstanceCreator(type, (InstanceCreator) typeAdapter, isSystem); } if (typeAdapter instanceof JsonSerializer) { - registerSerializer(type, (JsonSerializer) typeAdapter); + registerSerializer(type, (JsonSerializer) typeAdapter, isSystem); } if (typeAdapter instanceof JsonDeserializer) { - registerDeserializer(type, (JsonDeserializer) typeAdapter); + registerDeserializer(type, (JsonDeserializer) typeAdapter, isSystem); } return this; } @@ -514,8 +519,8 @@ public final class GsonBuilder { * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ private GsonBuilder registerInstanceCreator(Type typeOfT, - InstanceCreator instanceCreator) { - instanceCreators.register(typeOfT, instanceCreator); + InstanceCreator instanceCreator, boolean isSystem) { + instanceCreators.register(typeOfT, instanceCreator, isSystem); return this; } @@ -529,8 +534,9 @@ public final class GsonBuilder { * @param serializer the custom serializer * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ - private GsonBuilder registerSerializer(Type typeOfT, final JsonSerializer serializer) { - serializers.register(typeOfT, serializer); + private GsonBuilder registerSerializer(Type typeOfT, JsonSerializer serializer, + boolean isSystem) { + serializers.register(typeOfT, serializer, isSystem); return this; } @@ -544,8 +550,9 @@ public final class GsonBuilder { * @param deserializer the custom deserializer * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ - private GsonBuilder registerDeserializer(Type typeOfT, JsonDeserializer deserializer) { - deserializers.register(typeOfT, new JsonDeserializerExceptionWrapper(deserializer)); + private GsonBuilder registerDeserializer(Type typeOfT, JsonDeserializer deserializer, + boolean isSystem) { + deserializers.register(typeOfT, new JsonDeserializerExceptionWrapper(deserializer), isSystem); return this; } @@ -567,36 +574,41 @@ public final class GsonBuilder { * @since 1.7 */ public GsonBuilder registerTypeHierarchyAdapter(Class baseType, Object typeAdapter) { + return registerTypeHierarchyAdapter(baseType, typeAdapter, false); + } + + private GsonBuilder registerTypeHierarchyAdapter(Class baseType, Object typeAdapter, + boolean isSystem) { $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer - || typeAdapter instanceof JsonDeserializer || typeAdapter instanceof InstanceCreator); + || typeAdapter instanceof JsonDeserializer || typeAdapter instanceof InstanceCreator); if (typeAdapter instanceof InstanceCreator) { - registerInstanceCreatorForTypeHierarchy(baseType, (InstanceCreator) typeAdapter); + registerInstanceCreatorForTypeHierarchy(baseType, (InstanceCreator) typeAdapter, isSystem); } if (typeAdapter instanceof JsonSerializer) { - registerSerializerForTypeHierarchy(baseType, (JsonSerializer) typeAdapter); + registerSerializerForTypeHierarchy(baseType, (JsonSerializer) typeAdapter, isSystem); } if (typeAdapter instanceof JsonDeserializer) { - registerDeserializerForTypeHierarchy(baseType, (JsonDeserializer) typeAdapter); + registerDeserializerForTypeHierarchy(baseType, (JsonDeserializer) typeAdapter, isSystem); } return this; } private GsonBuilder registerInstanceCreatorForTypeHierarchy(Class classOfT, - InstanceCreator instanceCreator) { - instanceCreators.registerForTypeHierarchy(classOfT, instanceCreator); + InstanceCreator instanceCreator, boolean isSystem) { + instanceCreators.registerForTypeHierarchy(classOfT, instanceCreator, isSystem); return this; } private GsonBuilder registerSerializerForTypeHierarchy(Class classOfT, - final JsonSerializer serializer) { - serializers.registerForTypeHierarchy(classOfT, serializer); + JsonSerializer serializer, boolean isSystem) { + serializers.registerForTypeHierarchy(classOfT, serializer, isSystem); return this; } private GsonBuilder registerDeserializerForTypeHierarchy(Class classOfT, - JsonDeserializer deserializer) { + JsonDeserializer deserializer, boolean isSystem) { deserializers.registerForTypeHierarchy(classOfT, - new JsonDeserializerExceptionWrapper(deserializer)); + new JsonDeserializerExceptionWrapper(deserializer), isSystem); return this; } @@ -709,7 +721,7 @@ public final class GsonBuilder { private static void registerIfAbsent(Class type, ParameterizedTypeHandlerMap adapters, T adapter) { if (!adapters.hasSpecificHandlerFor(type)) { - adapters.register(type, adapter); + adapters.register(type, adapter, false); } } } diff --git a/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java b/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java index a4e7b411..df05dd70 100644 --- a/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java +++ b/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java @@ -38,25 +38,34 @@ import java.util.logging.Logger; final class ParameterizedTypeHandlerMap { private static final Logger logger = Logger.getLogger(ParameterizedTypeHandlerMap.class.getName()); - private final Map map = new HashMap(); - private final List, T>> typeHierarchyList = new ArrayList, T>>(); + /** + * Map that is meant for storing default type adapters + */ + private final Map systemMap = new HashMap(); + private final Map userMap = new HashMap(); + /** + * List of default type hierarchy adapters + */ + private final List, T>> systemTypeHierarchyList = new ArrayList, T>>(); + private final List, T>> userTypeHierarchyList = new ArrayList, T>>(); private boolean modifiable = true; - public synchronized void registerForTypeHierarchy(Class typeOfT, T value) { + public synchronized void registerForTypeHierarchy(Class typeOfT, T value, boolean isSystem) { Pair, T> pair = new Pair, T>(typeOfT, value); - registerForTypeHierarchy(pair); + registerForTypeHierarchy(pair, isSystem); } - public synchronized void registerForTypeHierarchy(Pair, T> pair) { + public synchronized void registerForTypeHierarchy(Pair, T> pair, boolean isSystem) { if (!modifiable) { throw new IllegalStateException("Attempted to modify an unmodifiable map."); } - int index = getIndexOfSpecificHandlerForTypeHierarchy(pair.first); + List, T>> typeHierarchyList = isSystem ? systemTypeHierarchyList : userTypeHierarchyList; + int index = getIndexOfSpecificHandlerForTypeHierarchy(pair.first, typeHierarchyList); if (index >= 0) { logger.log(Level.WARNING, "Overriding the existing type handler for {0}", pair.first); typeHierarchyList.remove(index); } - index = getIndexOfAnOverriddenHandler(pair.first); + index = getIndexOfAnOverriddenHandler(pair.first, typeHierarchyList); if (index >= 0) { throw new IllegalArgumentException("The specified type handler for type " + pair.first + " hides the previously registered type hierarchy handler for " @@ -67,7 +76,7 @@ final class ParameterizedTypeHandlerMap { typeHierarchyList.add(0, pair); } - private int getIndexOfAnOverriddenHandler(Class type) { + private static int getIndexOfAnOverriddenHandler(Class type, List, T>> typeHierarchyList) { for (int i = typeHierarchyList.size()-1; i >= 0; --i) { Pair, T> entry = typeHierarchyList.get(i); if (type.isAssignableFrom(entry.first)) { @@ -77,13 +86,14 @@ final class ParameterizedTypeHandlerMap { return -1; } - public synchronized void register(Type typeOfT, T value) { + public synchronized void register(Type typeOfT, T value, boolean isSystem) { if (!modifiable) { throw new IllegalStateException("Attempted to modify an unmodifiable map."); } if (hasSpecificHandlerFor(typeOfT)) { logger.log(Level.WARNING, "Overriding the existing type handler for {0}", typeOfT); } + Map map = isSystem ? systemMap : userMap; map.put(typeOfT, value); } @@ -91,18 +101,30 @@ final class ParameterizedTypeHandlerMap { if (!modifiable) { throw new IllegalStateException("Attempted to modify an unmodifiable map."); } - for (Map.Entry entry : other.map.entrySet()) { - if (!map.containsKey(entry.getKey())) { - register(entry.getKey(), entry.getValue()); + for (Map.Entry entry : other.userMap.entrySet()) { + if (!userMap.containsKey(entry.getKey())) { + register(entry.getKey(), entry.getValue(), false); + } + } + for (Map.Entry entry : other.systemMap.entrySet()) { + if (!systemMap.containsKey(entry.getKey())) { + register(entry.getKey(), entry.getValue(), true); } } // Quite important to traverse the typeHierarchyList from stack bottom first since // we want to register the handlers in the same order to preserve priority order - for (int i = other.typeHierarchyList.size()-1; i >= 0; --i) { - Pair, T> entry = other.typeHierarchyList.get(i); - int index = getIndexOfSpecificHandlerForTypeHierarchy(entry.first); + for (int i = other.userTypeHierarchyList.size()-1; i >= 0; --i) { + Pair, T> entry = other.userTypeHierarchyList.get(i); + int index = getIndexOfSpecificHandlerForTypeHierarchy(entry.first, userTypeHierarchyList); if (index < 0) { - registerForTypeHierarchy(entry); + registerForTypeHierarchy(entry, false); + } + } + for (int i = other.systemTypeHierarchyList.size()-1; i >= 0; --i) { + Pair, T> entry = other.systemTypeHierarchyList.get(i); + int index = getIndexOfSpecificHandlerForTypeHierarchy(entry.first, systemTypeHierarchyList); + if (index < 0) { + registerForTypeHierarchy(entry, true); } } } @@ -111,14 +133,21 @@ final class ParameterizedTypeHandlerMap { if (!modifiable) { throw new IllegalStateException("Attempted to modify an unmodifiable map."); } - for (Map.Entry entry : other.map.entrySet()) { - register(entry.getKey(), entry.getValue()); + for (Map.Entry entry : other.userMap.entrySet()) { + register(entry.getKey(), entry.getValue(), false); + } + for (Map.Entry entry : other.systemMap.entrySet()) { + register(entry.getKey(), entry.getValue(), true); } // Quite important to traverse the typeHierarchyList from stack bottom first since // we want to register the handlers in the same order to preserve priority order - for (int i = other.typeHierarchyList.size()-1; i >= 0; --i) { - Pair, T> entry = other.typeHierarchyList.get(i); - registerForTypeHierarchy(entry); + for (int i = other.userTypeHierarchyList.size()-1; i >= 0; --i) { + Pair, T> entry = other.userTypeHierarchyList.get(i); + registerForTypeHierarchy(entry, false); + } + for (int i = other.systemTypeHierarchyList.size()-1; i >= 0; --i) { + Pair, T> entry = other.systemTypeHierarchyList.get(i); + registerForTypeHierarchy(entry, true); } } @@ -126,8 +155,8 @@ final class ParameterizedTypeHandlerMap { if (!modifiable) { throw new IllegalStateException("Attempted to modify an unmodifiable map."); } - if (!map.containsKey(typeOfT)) { - register(typeOfT, value); + if (!userMap.containsKey(typeOfT)) { + register(typeOfT, value, false); } } @@ -136,22 +165,33 @@ final class ParameterizedTypeHandlerMap { } public synchronized T getHandlerFor(Type type) { - T handler = map.get(type); - if (handler == null) { - Class rawClass = $Gson$Types.getRawType(type); - if (rawClass != type) { - handler = getHandlerFor(rawClass); - } - if (handler == null) { - // check if something registered for type hierarchy - handler = getHandlerForTypeHierarchy(rawClass); + T handler = userMap.get(type); + if (handler != null) { + return handler; + } + handler = systemMap.get(type); + if (handler != null) { + return handler; + } + Class rawClass = $Gson$Types.getRawType(type); + if (rawClass != type) { + handler = getHandlerFor(rawClass); + if (handler != null) { + return handler; } } + // check if something registered for type hierarchy + handler = getHandlerForTypeHierarchy(rawClass); return handler; } private T getHandlerForTypeHierarchy(Class type) { - for (Pair, T> entry : typeHierarchyList) { + for (Pair, T> entry : userTypeHierarchyList) { + if (entry.first.isAssignableFrom(type)) { + return entry.second; + } + } + for (Pair, T> entry : systemTypeHierarchyList) { if (entry.first.isAssignableFrom(type)) { return entry.second; } @@ -160,10 +200,11 @@ final class ParameterizedTypeHandlerMap { } public synchronized boolean hasSpecificHandlerFor(Type type) { - return map.containsKey(type); + return userMap.containsKey(type) || systemMap.containsKey(type); } - private synchronized int getIndexOfSpecificHandlerForTypeHierarchy(Class type) { + private static int getIndexOfSpecificHandlerForTypeHierarchy( + Class type, List, T>> typeHierarchyList) { for (int i = typeHierarchyList.size()-1; i >= 0; --i) { if (type.equals(typeHierarchyList.get(i).first)) { return i; @@ -176,16 +217,33 @@ final class ParameterizedTypeHandlerMap { ParameterizedTypeHandlerMap copy = new ParameterizedTypeHandlerMap(); // Instead of individually registering entries in the map, make an efficient copy // of the list and map - copy.map.putAll(map); - copy.typeHierarchyList.addAll(typeHierarchyList); + + // TODO (inder): Performance optimization. We can probably just share the + // systemMap and systemTypeHierarchyList instead of making copies + copy.systemMap.putAll(systemMap); + copy.userMap.putAll(userMap); + copy.systemTypeHierarchyList.addAll(systemTypeHierarchyList); + copy.userTypeHierarchyList.addAll(userTypeHierarchyList); return copy; } @Override public String toString() { - StringBuilder sb = new StringBuilder("{mapForTypeHierarchy:{"); + StringBuilder sb = new StringBuilder("{userTypeHierarchyList:{"); + appendList(sb, userTypeHierarchyList); + sb.append("},systemTypeHierarchyList:{"); + appendList(sb, systemTypeHierarchyList); + sb.append("},userMap:{"); + appendMap(sb, userMap); + sb.append("},systemMap:{"); + appendMap(sb, systemMap); + sb.append("}"); + return sb.toString(); + } + + private void appendList(StringBuilder sb, List,T>> list) { boolean first = true; - for (Pair, T> entry : typeHierarchyList) { + for (Pair, T> entry : list) { if (first) { first = false; } else { @@ -194,8 +252,10 @@ final class ParameterizedTypeHandlerMap { sb.append(typeToString(entry.first)).append(':'); sb.append(entry.second); } - sb.append("},map:{"); - first = true; + } + + private void appendMap(StringBuilder sb, Map map) { + boolean first = true; for (Map.Entry entry : map.entrySet()) { if (first) { first = false; @@ -205,8 +265,6 @@ final class ParameterizedTypeHandlerMap { sb.append(typeToString(entry.getKey())).append(':'); sb.append(entry.getValue()); } - sb.append("}"); - return sb.toString(); } private String typeToString(Type type) { diff --git a/gson/src/test/java/com/google/gson/MappedObjectConstructorTest.java b/gson/src/test/java/com/google/gson/MappedObjectConstructorTest.java index 3b224210..da36ef02 100644 --- a/gson/src/test/java/com/google/gson/MappedObjectConstructorTest.java +++ b/gson/src/test/java/com/google/gson/MappedObjectConstructorTest.java @@ -37,7 +37,7 @@ public class MappedObjectConstructorTest extends TestCase { } public void testInstanceCreatorTakesTopPrecedence() throws Exception { - creatorMap.register(ObjectWithDefaultConstructor.class, new MyInstanceCreator()); + creatorMap.register(ObjectWithDefaultConstructor.class, new MyInstanceCreator(), false); ObjectWithDefaultConstructor obj = constructor.construct(ObjectWithDefaultConstructor.class); assertEquals("instanceCreator", obj.stringValue); diff --git a/gson/src/test/java/com/google/gson/ParameterizedTypeHandlerMapTest.java b/gson/src/test/java/com/google/gson/ParameterizedTypeHandlerMapTest.java index dbd00899..4c9231de 100644 --- a/gson/src/test/java/com/google/gson/ParameterizedTypeHandlerMapTest.java +++ b/gson/src/test/java/com/google/gson/ParameterizedTypeHandlerMapTest.java @@ -48,7 +48,7 @@ public class ParameterizedTypeHandlerMapTest extends TestCase { public void testHasGenericButNotSpecific() throws Exception { Type specificType = new TypeToken>() {}.getType(); String handler = "blah"; - paramMap.register(List.class, handler); + paramMap.register(List.class, handler, false); assertFalse(paramMap.hasSpecificHandlerFor(specificType)); assertTrue(paramMap.hasSpecificHandlerFor(List.class)); @@ -60,7 +60,7 @@ public class ParameterizedTypeHandlerMapTest extends TestCase { public void testHasSpecificType() throws Exception { Type specificType = new TypeToken>() {}.getType(); String handler = "blah"; - paramMap.register(specificType, handler); + paramMap.register(specificType, handler, false); assertTrue(paramMap.hasSpecificHandlerFor(specificType)); assertFalse(paramMap.hasSpecificHandlerFor(List.class)); @@ -72,8 +72,8 @@ public class ParameterizedTypeHandlerMapTest extends TestCase { public void testTypeOverridding() throws Exception { String handler1 = "blah1"; String handler2 = "blah2"; - paramMap.register(String.class, handler1); - paramMap.register(String.class, handler2); + paramMap.register(String.class, handler1, false); + paramMap.register(String.class, handler2, false); assertTrue(paramMap.hasSpecificHandlerFor(String.class)); assertEquals(handler2, paramMap.getHandlerFor(String.class)); @@ -82,44 +82,44 @@ public class ParameterizedTypeHandlerMapTest extends TestCase { public void testMakeUnmodifiable() throws Exception { paramMap.makeUnmodifiable(); try { - paramMap.register(String.class, "blah"); + paramMap.register(String.class, "blah", false); fail("Can not register handlers when map is unmodifiable"); } catch (IllegalStateException expected) { } } public void testTypeHierarchy() { - paramMap.registerForTypeHierarchy(Base.class, "baseHandler"); + paramMap.registerForTypeHierarchy(Base.class, "baseHandler", false); String handler = paramMap.getHandlerFor(Sub.class); assertEquals("baseHandler", handler); } public void testTypeHierarchyMultipleHandlers() { - paramMap.registerForTypeHierarchy(Base.class, "baseHandler"); - paramMap.registerForTypeHierarchy(Sub.class, "subHandler"); + paramMap.registerForTypeHierarchy(Base.class, "baseHandler", false); + paramMap.registerForTypeHierarchy(Sub.class, "subHandler", false); String handler = paramMap.getHandlerFor(SubOfSub.class); assertEquals("subHandler", handler); } public void testTypeHierarchyRegisterIfAbsent() { - paramMap.registerForTypeHierarchy(Base.class, "baseHandler"); + paramMap.registerForTypeHierarchy(Base.class, "baseHandler", false); ParameterizedTypeHandlerMap otherMap = new ParameterizedTypeHandlerMap(); - otherMap.registerForTypeHierarchy(Base.class, "baseHandler2"); + otherMap.registerForTypeHierarchy(Base.class, "baseHandler2", false); paramMap.registerIfAbsent(otherMap); String handler = paramMap.getHandlerFor(Base.class); assertEquals("baseHandler", handler); } public void testReplaceExistingTypeHierarchyHandler() { - paramMap.registerForTypeHierarchy(Base.class, "baseHandler"); - paramMap.registerForTypeHierarchy(Base.class, "base2Handler"); + paramMap.registerForTypeHierarchy(Base.class, "baseHandler", false); + paramMap.registerForTypeHierarchy(Base.class, "base2Handler", false); String handler = paramMap.getHandlerFor(Base.class); assertEquals("base2Handler", handler); } public void testHidingExistingTypeHierarchyHandlerIsDisallowed() { - paramMap.registerForTypeHierarchy(Sub.class, "subHandler"); + paramMap.registerForTypeHierarchy(Sub.class, "subHandler", false); try { - paramMap.registerForTypeHierarchy(Base.class, "baseHandler"); + paramMap.registerForTypeHierarchy(Base.class, "baseHandler", false); fail("A handler that hides an existing type hierarchy handler is not allowed"); } catch (IllegalArgumentException expected) { }