Added a new GsonBuilder option to register a type adapter for a type hierarchy instead of a single type.
Upgraded Gson version to 1.5 since a new API call is being added.
diff --git a/gson/pom.xml b/gson/pom.xml
index e781e58..c92ca28 100644
--- a/gson/pom.xml
+++ b/gson/pom.xml
@@ -4,7 +4,7 @@
   <groupId>com.google.code.gson</groupId>
   <artifactId>gson</artifactId>
   <packaging>jar</packaging>
-  <version>1.4</version>
+  <version>1.5</version>
   <inceptionYear>2008</inceptionYear>
   <name>Gson</name>
   <url>http://code.google.com/p/google-gson/</url>
diff --git a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java
index b34d8eb..695b89a 100644
--- a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java
+++ b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java
@@ -36,11 +36,9 @@
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedList;
-import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
-import java.util.Queue;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.StringTokenizer;
@@ -106,13 +104,14 @@
     ParameterizedTypeHandlerMap<JsonSerializer<?>> map =
         new ParameterizedTypeHandlerMap<JsonSerializer<?>>();
 
-    map.register(Enum.class, ENUM_TYPE_ADAPTER);
+    map.registerForTypeHierarchy(Enum.class, ENUM_TYPE_ADAPTER);
     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(Collection.class, COLLECTION_TYPE_ADAPTER);
-    map.register(Map.class, MAP_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);
     map.register(Timestamp.class, DATE_TYPE_ADAPTER);
@@ -143,13 +142,14 @@
   private static ParameterizedTypeHandlerMap<JsonDeserializer<?>> createDefaultDeserializers() {
     ParameterizedTypeHandlerMap<JsonDeserializer<?>> map =
         new ParameterizedTypeHandlerMap<JsonDeserializer<?>>();
-    map.register(Enum.class, wrapDeserializer(ENUM_TYPE_ADAPTER));
+    map.registerForTypeHierarchy(Enum.class, wrapDeserializer(ENUM_TYPE_ADAPTER));
     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(Collection.class, wrapDeserializer(COLLECTION_TYPE_ADAPTER));
-    map.register(Map.class, wrapDeserializer(MAP_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));
     map.register(Timestamp.class, wrapDeserializer(TIMESTAMP_DESERIALIZER));
@@ -189,9 +189,7 @@
     map.register(Map.class, MAP_TYPE_ADAPTER);
 
     // Add Collection type instance creators
-    map.register(Collection.class, COLLECTION_TYPE_ADAPTER);
-    map.register(List.class, COLLECTION_TYPE_ADAPTER);
-    map.register(Queue.class, COLLECTION_TYPE_ADAPTER);
+    map.registerForTypeHierarchy(Collection.class, COLLECTION_TYPE_ADAPTER);
 
     map.register(Set.class, HASH_SET_CREATOR);
     map.register(SortedSet.class, TREE_SET_CREATOR);
diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java
index c0ad98e..996d715 100644
--- a/gson/src/main/java/com/google/gson/GsonBuilder.java
+++ b/gson/src/main/java/com/google/gson/GsonBuilder.java
@@ -418,6 +418,57 @@
   }
 
   /**
+   * Configures Gson for custom serialization or deserialization for an inheritance type hierarchy.
+   * This method combines the registration of an {@link InstanceCreator}, {@link JsonSerializer},
+   * and a {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter}
+   * implements all the required interfaces for custom serialization with Gson.
+   * If an instance creator, serializer or deserializer was previously registered for the specified
+   * type hierarchy, it is overwritten. If an instance creator, serializer or deserializer is
+   * registered for a specific type in the type hierarchy, it will be invoked instead of the one
+   * registered for the type hierarchy.
+   *
+   * @param baseType the class definition for the type adapter being registered for the base class
+   *        or interface
+   * @param typeAdapter This object must implement at least one of the {@link InstanceCreator},
+   * {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.5
+   */
+  public GsonBuilder registerTypeHierarchyAdapter(Class<?> baseType, Object typeAdapter) {
+    Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
+    || typeAdapter instanceof JsonDeserializer<?> || typeAdapter instanceof InstanceCreator<?>);
+    if (typeAdapter instanceof InstanceCreator<?>) {
+      registerInstanceCreatorForTypeHierarchy(baseType, (InstanceCreator<?>) typeAdapter);
+    }
+    if (typeAdapter instanceof JsonSerializer<?>) {
+      registerSerializerForTypeHierarchy(baseType, (JsonSerializer<?>) typeAdapter);
+    }
+    if (typeAdapter instanceof JsonDeserializer<?>) {
+      registerDeserializerForTypeHierarchy(baseType, (JsonDeserializer<?>) typeAdapter);
+    }
+    return this;
+  }
+
+  private <T> GsonBuilder registerInstanceCreatorForTypeHierarchy(Class<?> classOfT,
+      InstanceCreator<? extends T> instanceCreator) {
+    instanceCreators.registerForTypeHierarchy(classOfT, instanceCreator);
+    return this;
+  }
+
+  private <T> GsonBuilder registerSerializerForTypeHierarchy(Class<?> classOfT,
+      final JsonSerializer<T> serializer) {
+    serializers.registerForTypeHierarchy(classOfT, serializer);
+    return this;
+  }
+
+  private <T> GsonBuilder registerDeserializerForTypeHierarchy(Class<?> classOfT,
+      JsonDeserializer<T> deserializer) {
+    deserializers.registerForTypeHierarchy(classOfT,
+        new JsonDeserializerExceptionWrapper<T>(deserializer));
+    return this;
+  }
+
+  /**
    * Section 2.4 of <a href="http://www.ietf.org/rfc/rfc4627.txt">JSON specification</a> disallows
    * special double values (NaN, Infinity, -Infinity). However,
    * <a href="http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf">Javascript
diff --git a/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java b/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java
index 388ccdc..e7cab3f 100644
--- a/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java
+++ b/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java
@@ -17,10 +17,10 @@
 package com.google.gson;
 
 import java.lang.reflect.Type;
-import java.util.Collection;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -31,19 +31,37 @@
  * @author Inderjeet Singh
  * @author Joel Leitch
  * 
- * @param <T>
- *          The handler that will be looked up by type
+ * @param <T> The handler that will be looked up by type
  */
 final class ParameterizedTypeHandlerMap<T> {
   private static final Logger logger =
       Logger.getLogger(ParameterizedTypeHandlerMap.class.getName());
   private final Map<Type, T> map = new HashMap<Type, T>();
+  private final List<Pair<Class<?>, T>> typeHierarchyList = new ArrayList<Pair<Class<?>, T>>();
   private boolean modifiable = true;
 
+  public synchronized void registerForTypeHierarchy(Class<?> typeOfT, T value) {
+    Pair<Class<?>, T> pair = new Pair<Class<?>, T>(typeOfT, value);
+    registerForTypeHierarchy(pair);
+  }
+
+  public synchronized void registerForTypeHierarchy(Pair<Class<?>, T> pair) {
+    if (!modifiable) {
+      throw new IllegalStateException("Attempted to modify an unmodifiable map.");
+    }
+    int index = getIndexOfSpecificHandlerForTypeHierarchy(pair.first);
+    if (index >= 0) {
+      logger.log(Level.WARNING, "Overriding the existing type handler for {0}", pair.first);
+      typeHierarchyList.remove(index);
+    }
+    // We want stack behavior for adding to this list. A type adapter added subsequently should
+    // override a previously registered one.
+    typeHierarchyList.add(0, pair);
+  }
+
   public synchronized void register(Type typeOfT, T value) {
     if (!modifiable) {
-      throw new IllegalStateException(
-          "Attempted to modify an unmodifiable map.");
+      throw new IllegalStateException("Attempted to modify an unmodifiable map.");
     }
     if (hasSpecificHandlerFor(typeOfT)) {
       logger.log(Level.WARNING, "Overriding the existing type handler for {0}", typeOfT);
@@ -53,20 +71,24 @@
 
   public synchronized void registerIfAbsent(ParameterizedTypeHandlerMap<T> other) {
     if (!modifiable) {
-      throw new IllegalStateException(
-          "Attempted to modify an unmodifiable map.");
+      throw new IllegalStateException("Attempted to modify an unmodifiable map.");
     }
-    for (Map.Entry<Type, T> entry : other.entrySet()) {
+    for (Map.Entry<Type, T> entry : other.map.entrySet()) {
       if (!map.containsKey(entry.getKey())) {
         register(entry.getKey(), entry.getValue());
       }
     }
+    for (Pair<Class<?>, T> entry : other.typeHierarchyList) {
+      int index = getIndexOfSpecificHandlerForTypeHierarchy(entry.first);
+      if (index < 0) {
+        registerForTypeHierarchy(entry);
+      }
+    }
   }
 
   public synchronized void registerIfAbsent(Type typeOfT, T value) {
     if (!modifiable) {
-      throw new IllegalStateException(
-          "Attempted to modify an unmodifiable map.");
+      throw new IllegalStateException("Attempted to modify an unmodifiable map.");
     }
     if (!map.containsKey(typeOfT)) {
       register(typeOfT, value);
@@ -84,41 +106,63 @@
       if (rawClass != type) {
         handler = getHandlerFor(rawClass);
       }
-
-      // Check for map or collection
       if (handler == null) {
-        if (Map.class.isAssignableFrom(rawClass)) {
-          handler = map.get(Map.class);
-        } else if (Collection.class.isAssignableFrom(rawClass)) {
-          handler = map.get(Collection.class);
-        } else if (Enum.class.isAssignableFrom(rawClass)) {
-          handler = map.get(Enum.class);
-        }
+        // check if something registered for type hierarchy
+        handler = getHandlerForTypeHierarchy(rawClass);
       }
     }
     return handler;
   }
 
+  @SuppressWarnings("unchecked")
+  private T getHandlerForTypeHierarchy(Class<?> type) {
+    for (Pair<Class<?>, T> entry : typeHierarchyList) {
+      if (entry.first.isAssignableFrom(type)) {
+        return entry.second;
+      }
+    }
+    return null;
+  }
+
   public synchronized boolean hasSpecificHandlerFor(Type type) {
     return map.containsKey(type);
   }
 
+  private synchronized int getIndexOfSpecificHandlerForTypeHierarchy(Class<?> type) {
+    for (int i = typeHierarchyList.size()-1; i >= 0; --i) {
+      if (type.equals(typeHierarchyList.get(i).first)) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
   public synchronized ParameterizedTypeHandlerMap<T> copyOf() {
     ParameterizedTypeHandlerMap<T> copy = new ParameterizedTypeHandlerMap<T>();
     for (Map.Entry<Type, T> entry : map.entrySet()) {
       copy.register(entry.getKey(), entry.getValue());
     }
+    for (Pair<Class<?>, T> entry : typeHierarchyList) {
+      copy.registerForTypeHierarchy(entry);
+    }
     return copy;
   }
 
-  public synchronized Set<Map.Entry<Type, T>> entrySet() {
-    return map.entrySet();
-  }
-
   @Override
   public String toString() {
-    StringBuilder sb = new StringBuilder("{");
+    StringBuilder sb = new StringBuilder("{mapForTypeHierarchy:{");
     boolean first = true;
+    for (Pair<Class<?>, T> entry : typeHierarchyList) {
+      if (first) {
+        first = false;
+      } else {
+        sb.append(',');
+      }
+      sb.append(typeToString(entry.first)).append(':');
+      sb.append(entry.second);
+    }
+    sb.append("},map:{");
+    first = true;
     for (Map.Entry<Type, T> entry : map.entrySet()) {
       if (first) {
         first = false;
@@ -128,6 +172,7 @@
       sb.append(typeToString(entry.getKey())).append(':');
       sb.append(entry.getValue());
     }
+    sb.append("}");
     return sb.toString();
   }
 
diff --git a/gson/src/test/java/com/google/gson/ParameterizedTypeHandlerMapTest.java b/gson/src/test/java/com/google/gson/ParameterizedTypeHandlerMapTest.java
index cd05b1c..82c19ae 100644
--- a/gson/src/test/java/com/google/gson/ParameterizedTypeHandlerMapTest.java
+++ b/gson/src/test/java/com/google/gson/ParameterizedTypeHandlerMapTest.java
@@ -16,6 +16,8 @@
 
 package com.google.gson;
 
+import com.google.gson.common.TestTypes.Base;
+import com.google.gson.common.TestTypes.Sub;
 import com.google.gson.reflect.TypeToken;
 
 import junit.framework.TestCase;
@@ -84,4 +86,29 @@
      fail("Can not register handlers when map is unmodifiable");
     } catch (IllegalStateException expected) { }
   }
+
+  public void testTypeHierarchy() {
+    paramMap.registerForTypeHierarchy(Base.class, "baseHandler");
+    String handler = paramMap.getHandlerFor(Sub.class);
+    assertEquals("baseHandler", handler);
+  }
+
+  public void testTypeHierarchyMultipleHandlers() {
+    paramMap.registerForTypeHierarchy(Base.class, "baseHandler");
+    paramMap.registerForTypeHierarchy(Sub.class, "subHandler");
+    String handler = paramMap.getHandlerFor(SubOfSub.class);
+    assertEquals("subHandler", handler);
+  }
+
+  public void testTypeHierarchyRegisterIfAbsent() {
+    paramMap.registerForTypeHierarchy(Base.class, "baseHandler");
+    ParameterizedTypeHandlerMap<String> otherMap = new ParameterizedTypeHandlerMap<String>();
+    otherMap.registerForTypeHierarchy(Base.class, "baseHandler2");
+    paramMap.registerIfAbsent(otherMap);
+    String handler = paramMap.getHandlerFor(Base.class);
+    assertEquals("baseHandler", handler);
+  }
+
+  private static class SubOfSub extends Sub {
+  }
 }
diff --git a/gson/src/test/java/com/google/gson/functional/MapTest.java b/gson/src/test/java/com/google/gson/functional/MapTest.java
index 6e084e0..909641f 100755
--- a/gson/src/test/java/com/google/gson/functional/MapTest.java
+++ b/gson/src/test/java/com/google/gson/functional/MapTest.java
@@ -183,6 +183,14 @@
     assertTrue(json.contains("\"a\":\"b\""));
   }
   
+  public void testMapStandardSubclassDeserialization() {
+    String json = "{a:'1',b:'2'}";
+    Type type = new TypeToken<LinkedHashMap<String, String>>() {}.getType();
+    LinkedHashMap<String, Integer> map = gson.fromJson(json, type);
+    assertEquals("1", map.get("a")); 
+    assertEquals("2", map.get("b")); 
+  }
+  
   public void disable_testMapSubclassDeserialization() {
     Gson gson = new GsonBuilder().registerTypeAdapter(MyMap.class, new InstanceCreator<MyMap>() {
       public MyMap createInstance(Type type) {