/*
 * [The "BSD license"]
 *  Copyright (c) 2012 Terence Parr
 *  Copyright (c) 2012 Sam Harwell
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.stringtemplate.v4.misc;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

Author:Sam Harwell
/** * * @author Sam Harwell */
public class TypeRegistry<V> implements Map<Class<?>, V> { private final Map<Class<?>, V> backingStore = new HashMap<Class<?>, V>(); private final Map<Class<?>, Class<?>> cache = new HashMap<Class<?>, Class<?>>(); public int size() { return backingStore.size(); } public boolean isEmpty() { return backingStore.isEmpty(); } public boolean containsKey(Object key) { if (cache.containsKey(key)) { return true; } if (!(key instanceof Class)) { return false; } return get(key) != null; } @SuppressWarnings("unchecked") public boolean containsValue(Object value) { return values().contains(value); }
{@inheritDoc}
Throws:
  • AmbiguousMatchException – if the registry contains more than value mapped to a maximally-specific type from which key is derived.
/** * {@inheritDoc} * * @throws AmbiguousMatchException if the registry contains more than value * mapped to a maximally-specific type from which {@code key} is derived. */
public V get(Object key) { V value = backingStore.get(key); if (value != null) { return value; } Class<?> redirect = cache.get(key); if (redirect != null) { if (redirect == Void.TYPE) { return null; } else { return backingStore.get(redirect); } } if (!(key instanceof Class)) { return null; } Class<?> keyClass = (Class<?>)key; List<Class<?>> candidates = new ArrayList<Class<?>>(); for (Class<?> clazz : backingStore.keySet()) { if (clazz.isAssignableFrom(keyClass)) { candidates.add(clazz); } } if (candidates.isEmpty()) { cache.put(keyClass, Void.TYPE); return null; } else if (candidates.size() == 1) { cache.put(keyClass, candidates.get(0)); return backingStore.get(candidates.get(0)); } else { for (int i = 0; i < candidates.size() - 1; i++) { if (candidates.get(i) == null) { continue; } for (int j = i + 1; j < candidates.size(); j++) { if (candidates.get(i).isAssignableFrom(candidates.get(j))) { candidates.set(i, null); break; } else if (candidates.get(j).isAssignableFrom(candidates.get(i))) { candidates.set(j, null); } } } int j = 0; for (int i = 0; i < candidates.size(); i++) { Class<?> current = candidates.get(i); if (current == null) { continue; } if (i != j) { candidates.set(j, current); } j++; } assert j > 0; if (j != 1) { StringBuilder builder = new StringBuilder(); builder.append(String.format("The class '%s' does not match a single item in the registry. The %d ambiguous matches are:", keyClass.getName(), j)); for (int i = 0; i < j; i++) { builder.append(String.format("%n %s", candidates.get(j).getName())); } throw new AmbiguousMatchException(builder.toString()); } cache.put(keyClass, candidates.get(0)); return backingStore.get(candidates.get(0)); } } public V put(Class<?> key, V value) { V result = get(key); backingStore.put(key, value); handleAlteration(key); return result; } public V remove(Object key) { if (!(key instanceof Class)) { return null; } Class<?> clazz = (Class<?>)key; V previous = get(clazz); if (backingStore.remove(clazz) != null) { handleAlteration(clazz); } return previous; } public void putAll(Map<? extends Class<?>, ? extends V> m) { for (Map.Entry<? extends Class<?>, ? extends V> entry : m.entrySet()) { put(entry.getKey(), entry.getValue()); } } public void clear() { backingStore.clear(); cache.clear(); } public Set<Class<?>> keySet() { return Collections.unmodifiableSet(backingStore.keySet()); } public Collection<V> values() { return Collections.unmodifiableCollection(backingStore.values()); } public Set<Entry<Class<?>, V>> entrySet() { return Collections.unmodifiableSet(backingStore.entrySet()); } protected void handleAlteration(Class<?> clazz) { for (Map.Entry<Class<?>, ?> entry : cache.entrySet()) { if (clazz.isAssignableFrom(entry.getKey())) { entry.setValue(null); } } } }