TypeMap.java
001 package net.sourceforge.pmd.util;
002 
003 import java.util.HashMap;
004 import java.util.Iterator;
005 import java.util.Map;
006 
007 /**
008  * A specialized map that stores types by both their full and short (without package prefixes) names.
009  * If an incoming type shares the same name (but different package/prefix) with a type already in the
010  * map then an IllegalArgumentException will be thrown since any subsequent retrievals by said short
011  * name could be in error.
012  *
013  @author Brian Remedios
014  */
015 public class TypeMap {
016 
017     private Map<String, Class<?>> typesByName;
018 
019     /**
020      * Constructor for TypeMap.
021      *
022      @param initialSize int
023      */
024     public TypeMap(int initialSize) {
025         typesByName = new HashMap<String, Class<?>>(initialSize);
026     }
027 
028     /**
029      * Constructor for TypeMap that takes in an initial set of types.
030      *
031      @param types Class[]
032      */
033     public TypeMap(Class<?>... types) {
034         this(types.length);
035         add(types);
036     }
037 
038     /**
039      * Adds a type to the receiver and stores it keyed by both its full and
040      * short names. Throws an exception if the short name of the argument
041      * matches an existing one already in the map for a different class.
042      *
043      @param type Class
044      @throws IllegalArgumentException
045      */
046     @SuppressWarnings("PMD.CompareObjectsWithEquals")
047     public void add(Class<?> type) {
048         final String shortName = ClassUtil.withoutPackageName(type.getName());
049         Class<?> existingType = typesByName.get(shortName);
050         if (existingType == null) {
051             typesByName.put(type.getName(), type);
052             typesByName.put(shortName, type);
053             return;
054         }
055 
056         if (existingType != type) {
057             throw new IllegalArgumentException("Short name collision between existing " + existingType + " and new "
058                     + type);
059         }
060     }
061 
062     /**
063      * Returns whether the type is known to the receiver.
064      *
065      @param type Class
066      @return boolean
067      */
068     public boolean contains(Class<?> type) {
069         return typesByName.containsValue(type);
070     }
071 
072     /**
073      * Returns whether the typeName is known to the receiver.
074      *
075      @param typeName String
076      @return boolean
077      */
078     public boolean contains(String typeName) {
079         return typesByName.containsKey(typeName);
080     }
081 
082     /**
083      * Returns the type for the typeName specified.
084      *
085      @param typeName String
086      @return Class
087      */
088     public Class<?> typeFor(String typeName) {
089         return typesByName.get(typeName);
090     }
091 
092     /**
093      * Adds an array of types to the receiver at once.
094      *
095      @param types Class[]
096      */
097     public void add(Class<?>... types) {
098         for (Class<?> element : types) {
099             add(element);
100         }
101     }
102 
103     /**
104      * Creates and returns a map of short type names (without the package
105      * prefixes) keyed by the classes themselves.
106      *
107      @return Map
108      */
109     public Map<Class<?>, String> asInverseWithShortName() {
110         Map<Class<?>, String> inverseMap = new HashMap<Class<?>, String>(typesByName.size() 2);
111 
112         Iterator iter = typesByName.entrySet().iterator();
113         while (iter.hasNext()) {
114             Map.Entry entry = (Map.Entryiter.next();
115             storeShortest(inverseMap, entry.getValue()(Stringentry.getKey());
116         }
117 
118         return inverseMap;
119     }
120 
121     /**
122      * Returns the total number of entries in the receiver. This will be exactly
123      * twice the number of types added.
124      *
125      @return the total number of entries in the receiver
126      */
127     public int size() {
128         return typesByName.size();
129     }
130 
131     /**
132      * Store the shorter of the incoming value or the existing value in the map
133      * at the key specified.
134      *
135      @param map
136      @param key
137      @param value
138      */
139     private void storeShortest(Map map, Object key, String value) {
140         String existingValue = (Stringmap.get(key);
141 
142         if (existingValue == null) {
143             map.put(key, value);
144             return;
145         }
146 
147         if (existingValue.length() < value.length()) {
148             return;
149         }
150 
151         map.put(key, value);
152     }
153 }