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;
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);
}
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);
}
}
}
}