All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.stringtemplate.v4.misc.TypeRegistry Maven / Gradle / Ivy

There is a newer version: 2.12.15
Show newest version
/*
 * [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
 */
public class TypeRegistry implements Map, V> {

	private final Map, V> backingStore = new HashMap, V>();
	private final Map, Class> cache = new HashMap, 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 {@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> candidates = new ArrayList>();
		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 V> m) {
		for (Map.Entry, ? extends V> entry : m.entrySet()) {
			put(entry.getKey(), entry.getValue());
		}
	}

	public void clear() {
		backingStore.clear();
		cache.clear();
	}

	public Set> keySet() {
		return Collections.unmodifiableSet(backingStore.keySet());
	}

	public Collection values() {
		return Collections.unmodifiableCollection(backingStore.values());
	}

	public Set, V>> entrySet() {
		return Collections.unmodifiableSet(backingStore.entrySet());
	}

	protected void handleAlteration(Class clazz) {
		for (Map.Entry, ?> entry : cache.entrySet()) {
			if (clazz.isAssignableFrom(entry.getKey())) {
				entry.setValue(null);
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy