/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.util; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * A specialized map that stores types by both their full and short (without * package prefixes) names. If an incoming type shares the same name (but * different package/prefix) with a type already in the map then an * IllegalArgumentException will be thrown since any subsequent retrievals by * said short name could be in error. * * @author Brian Remedios */ public class TypeMap { private Map<String, Class<?>> typesByName; /** * Constructor for TypeMap. * * @param initialSize * int */ public TypeMap(int initialSize) { typesByName = new HashMap<>(initialSize); } /** * Constructor for TypeMap that takes in an initial set of types. * * @param types * Class[] */ public TypeMap(Class<?>... types) { this(types.length); add(types); } /** * Adds a type to the receiver and stores it keyed by both its full and * short names. Throws an exception if the short name of the argument * matches an existing one already in the map for a different class. * * @param type * Class * @throws IllegalArgumentException */ @SuppressWarnings("PMD.CompareObjectsWithEquals") public void add(Class<?> type) { final String shortName = ClassUtil.withoutPackageName(type.getName()); Class<?> existingType = typesByName.get(shortName); if (existingType == null) { typesByName.put(type.getName(), type); typesByName.put(shortName, type); return; } if (existingType != type) { throw new IllegalArgumentException( "Short name collision between existing " + existingType + " and new " + type); } } /** * Returns whether the type is known to the receiver. * * @param type * Class * @return boolean */ public boolean contains(Class<?> type) { return typesByName.containsValue(type); } /** * Returns whether the typeName is known to the receiver. * * @param typeName * String * @return boolean */ public boolean contains(String typeName) { return typesByName.containsKey(typeName); } /** * Returns the type for the typeName specified. * * @param typeName * String * @return Class */ public Class<?> typeFor(String typeName) { return typesByName.get(typeName); } /** * Adds an array of types to the receiver at once. * * @param types * Class[] */ public void add(Class<?>... types) { for (Class<?> element : types) { add(element); } } /** * Creates and returns a map of short type names (without the package * prefixes) keyed by the classes themselves. * * @return Map */ public Map<Class<?>, String> asInverseWithShortName() { Map<Class<?>, String> inverseMap = new HashMap<>(typesByName.size() / 2); Iterator<Map.Entry<String, Class<?>>> iter = typesByName.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<String, Class<?>> entry = iter.next(); storeShortest(inverseMap, entry.getValue(), entry.getKey()); } return inverseMap; } /** * Returns the total number of entries in the receiver. This will be exactly * twice the number of types added. * * @return the total number of entries in the receiver */ public int size() { return typesByName.size(); } /** * Store the shorter of the incoming value or the existing value in the map * at the key specified. * * @param map * @param key * @param value */ private void storeShortest(Map<Class<?>, String> map, Class<?> key, String value) { String existingValue = map.get(key); if (existingValue == null) { map.put(key, value); return; } if (existingValue.length() < value.length()) { return; } map.put(key, value); } }