Added checks to ensure that typeHierarchyAdapter being registered doesn't hide a previously existing one.

Fixed a bug where registerIfAbsent was adding type adapters in the reverse order of priority.
Added toString() to Pair.
This commit is contained in:
Inderjeet Singh 2010-06-24 21:51:18 +00:00
parent 8aedbc84db
commit b2af57d288
4 changed files with 43 additions and 7 deletions

View File

@ -110,7 +110,6 @@ final class DefaultTypeAdapters {
map.register(UUID.class, UUUID_TYPE_ADAPTER);
map.register(Locale.class, LOCALE_TYPE_ADAPTER);
map.registerForTypeHierarchy(Collection.class, COLLECTION_TYPE_ADAPTER);
map.registerForTypeHierarchy(Set.class, COLLECTION_TYPE_ADAPTER);
map.registerForTypeHierarchy(Map.class, MAP_TYPE_ADAPTER);
map.register(Date.class, DATE_TYPE_ADAPTER);
map.register(java.sql.Date.class, JAVA_SQL_DATE_TYPE_ADAPTER);
@ -148,7 +147,6 @@ final class DefaultTypeAdapters {
map.register(UUID.class, wrapDeserializer(UUUID_TYPE_ADAPTER));
map.register(Locale.class, wrapDeserializer(LOCALE_TYPE_ADAPTER));
map.registerForTypeHierarchy(Collection.class, wrapDeserializer(COLLECTION_TYPE_ADAPTER));
map.registerForTypeHierarchy(Set.class, wrapDeserializer(COLLECTION_TYPE_ADAPTER));
map.registerForTypeHierarchy(Map.class, wrapDeserializer(MAP_TYPE_ADAPTER));
map.register(Date.class, wrapDeserializer(DATE_TYPE_ADAPTER));
map.register(java.sql.Date.class, wrapDeserializer(JAVA_SQL_DATE_TYPE_ADAPTER));
@ -186,14 +184,13 @@ final class DefaultTypeAdapters {
private static ParameterizedTypeHandlerMap<InstanceCreator<?>> createDefaultInstanceCreators() {
ParameterizedTypeHandlerMap<InstanceCreator<?>> map =
new ParameterizedTypeHandlerMap<InstanceCreator<?>>();
map.register(Map.class, MAP_TYPE_ADAPTER);
map.registerForTypeHierarchy(Map.class, MAP_TYPE_ADAPTER);
// Add Collection type instance creators
map.registerForTypeHierarchy(Collection.class, COLLECTION_TYPE_ADAPTER);
map.register(Set.class, HASH_SET_CREATOR);
map.register(SortedSet.class, TREE_SET_CREATOR);
map.register(TreeSet.class, TREE_SET_CREATOR);
map.registerForTypeHierarchy(Set.class, HASH_SET_CREATOR);
map.registerForTypeHierarchy(SortedSet.class, TREE_SET_CREATOR);
map.register(Properties.class, PROPERTIES_CREATOR);
map.makeUnmodifiable();
return map;

View File

@ -54,4 +54,9 @@ final class Pair<FIRST, SECOND> {
private static boolean equal(Object a, Object b) {
return a == b || (a != null && a.equals(b));
}
@Override
public String toString() {
return String.format("{%s,%s}", first, second);
}
}

View File

@ -54,11 +54,27 @@ final class ParameterizedTypeHandlerMap<T> {
logger.log(Level.WARNING, "Overriding the existing type handler for {0}", pair.first);
typeHierarchyList.remove(index);
}
index = getIndexOfAnOverriddenHandler(pair.first);
if (index >= 0) {
throw new IllegalArgumentException("The specified type handler for type " + pair.first
+ " hides the previously registered type hierarchy handler for "
+ typeHierarchyList.get(index).first + ". Gson does not allow this.");
}
// We want stack behavior for adding to this list. A type adapter added subsequently should
// override a previously registered one.
typeHierarchyList.add(0, pair);
}
private int getIndexOfAnOverriddenHandler(Class<?> type) {
for (int i = typeHierarchyList.size()-1; i >= 0; --i) {
Pair<Class<?>, T> entry = typeHierarchyList.get(i);
if (type.isAssignableFrom(entry.first)) {
return i;
}
}
return -1;
}
public synchronized void register(Type typeOfT, T value) {
if (!modifiable) {
throw new IllegalStateException("Attempted to modify an unmodifiable map.");
@ -78,7 +94,10 @@ final class ParameterizedTypeHandlerMap<T> {
register(entry.getKey(), entry.getValue());
}
}
for (Pair<Class<?>, T> entry : other.typeHierarchyList) {
// 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<Class<?>, T> entry = other.typeHierarchyList.get(i);
int index = getIndexOfSpecificHandlerForTypeHierarchy(entry.first);
if (index < 0) {
registerForTypeHierarchy(entry);

View File

@ -109,6 +109,21 @@ public class ParameterizedTypeHandlerMapTest extends TestCase {
assertEquals("baseHandler", handler);
}
public void testReplaceExistingTypeHierarchyHandler() {
paramMap.registerForTypeHierarchy(Base.class, "baseHandler");
paramMap.registerForTypeHierarchy(Base.class, "base2Handler");
String handler = paramMap.getHandlerFor(Base.class);
assertEquals("base2Handler", handler);
}
public void testHidingExistingTypeHierarchyHandlerIsDisallowed() {
paramMap.registerForTypeHierarchy(Sub.class, "subHandler");
try {
paramMap.registerForTypeHierarchy(Base.class, "baseHandler");
fail("A handler that hides an existing type hierarchy handler is not allowed");
} catch (IllegalArgumentException expected) {
}
}
private static class SubOfSub extends Sub {
}
}