/* * Copyright (C) 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.smartandroid.sa.json; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import com.smartandroid.sa.json.internal.$Gson$Types; /** * A map that provides ability to associate handlers for a specific type or all * of its sub-types * * @author Inderjeet Singh * @author Joel Leitch * * @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()); /** * Map that is meant for storing default type adapters */ private final Map<Type, T> systemMap = new HashMap<Type, T>(); private final Map<Type, T> userMap = new HashMap<Type, T>(); /** * List of default type hierarchy adapters */ private final List<Pair<Class<?>, T>> systemTypeHierarchyList = new ArrayList<Pair<Class<?>, T>>(); private final List<Pair<Class<?>, T>> userTypeHierarchyList = new ArrayList<Pair<Class<?>, T>>(); private boolean modifiable = true; public synchronized void registerForTypeHierarchy(Class<?> typeOfT, T value, boolean isSystem) { Pair<Class<?>, T> pair = new Pair<Class<?>, T>(typeOfT, value); registerForTypeHierarchy(pair, isSystem); } public synchronized void registerForTypeHierarchy(Pair<Class<?>, T> pair, boolean isSystem) { if (!modifiable) { throw new IllegalStateException( "Attempted to modify an unmodifiable map."); } List<Pair<Class<?>, 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, typeHierarchyList); 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 static <T> int getIndexOfAnOverriddenHandler(Class<?> type, List<Pair<Class<?>, T>> typeHierarchyList) { 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, 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<Type, T> map = isSystem ? systemMap : userMap; map.put(typeOfT, value); } public synchronized void registerIfAbsent( ParameterizedTypeHandlerMap<T> other) { if (!modifiable) { throw new IllegalStateException( "Attempted to modify an unmodifiable map."); } for (Map.Entry<Type, T> entry : other.userMap.entrySet()) { if (!userMap.containsKey(entry.getKey())) { register(entry.getKey(), entry.getValue(), false); } } for (Map.Entry<Type, T> 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.userTypeHierarchyList.size() - 1; i >= 0; --i) { Pair<Class<?>, T> entry = other.userTypeHierarchyList.get(i); int index = getIndexOfSpecificHandlerForTypeHierarchy(entry.first, userTypeHierarchyList); if (index < 0) { registerForTypeHierarchy(entry, false); } } for (int i = other.systemTypeHierarchyList.size() - 1; i >= 0; --i) { Pair<Class<?>, T> entry = other.systemTypeHierarchyList.get(i); int index = getIndexOfSpecificHandlerForTypeHierarchy(entry.first, systemTypeHierarchyList); if (index < 0) { registerForTypeHierarchy(entry, true); } } } public synchronized void register(ParameterizedTypeHandlerMap<T> other) { if (!modifiable) { throw new IllegalStateException( "Attempted to modify an unmodifiable map."); } for (Map.Entry<Type, T> entry : other.userMap.entrySet()) { register(entry.getKey(), entry.getValue(), false); } for (Map.Entry<Type, T> 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.userTypeHierarchyList.size() - 1; i >= 0; --i) { Pair<Class<?>, T> entry = other.userTypeHierarchyList.get(i); registerForTypeHierarchy(entry, false); } for (int i = other.systemTypeHierarchyList.size() - 1; i >= 0; --i) { Pair<Class<?>, T> entry = other.systemTypeHierarchyList.get(i); registerForTypeHierarchy(entry, true); } } public synchronized void registerIfAbsent(Type typeOfT, T value) { if (!modifiable) { throw new IllegalStateException( "Attempted to modify an unmodifiable map."); } if (!userMap.containsKey(typeOfT)) { register(typeOfT, value, false); } } public synchronized void makeUnmodifiable() { modifiable = false; } public synchronized T getHandlerFor(Type type, boolean systemOnly) { T handler; if (!systemOnly) { 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, systemOnly); if (handler != null) { return handler; } } // check if something registered for type hierarchy handler = getHandlerForTypeHierarchy(rawClass, systemOnly); return handler; } private T getHandlerForTypeHierarchy(Class<?> type, boolean systemOnly) { if (!systemOnly) { for (Pair<Class<?>, T> entry : userTypeHierarchyList) { if (entry.first.isAssignableFrom(type)) { return entry.second; } } } for (Pair<Class<?>, T> entry : systemTypeHierarchyList) { if (entry.first.isAssignableFrom(type)) { return entry.second; } } return null; } public synchronized boolean hasSpecificHandlerFor(Type type) { return userMap.containsKey(type) || systemMap.containsKey(type); } private static <T> int getIndexOfSpecificHandlerForTypeHierarchy( Class<?> type, List<Pair<Class<?>, T>> typeHierarchyList) { 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>(); // Instead of individually registering entries in the map, make an // efficient copy // of the list and map // 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("{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<Pair<Class<?>, T>> list) { boolean first = true; for (Pair<Class<?>, T> entry : list) { if (first) { first = false; } else { sb.append(','); } sb.append(typeToString(entry.first)).append(':'); sb.append(entry.second); } } private void appendMap(StringBuilder sb, Map<Type, T> map) { boolean first = true; for (Map.Entry<Type, T> entry : map.entrySet()) { if (first) { first = false; } else { sb.append(','); } sb.append(typeToString(entry.getKey())).append(':'); sb.append(entry.getValue()); } } private String typeToString(Type type) { return $Gson$Types.getRawType(type).getSimpleName(); } }