View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.util;
5   
6   import java.util.HashMap;
7   import java.util.Iterator;
8   import java.util.Map;
9   
10  /**
11   * A specialized map that stores types by both their full and short (without package prefixes) names.
12   * If an incoming type shares the same name (but different package/prefix) with a type already in the
13   * map then an IllegalArgumentException will be thrown since any subsequent retrievals by said short
14   * name could be in error.
15   *
16   * @author Brian Remedios
17   */
18  public class TypeMap {
19  
20      private Map<String, Class<?>> typesByName;
21  
22      /**
23       * Constructor for TypeMap.
24       *
25       * @param initialSize int
26       */
27      public TypeMap(int initialSize) {
28          typesByName = new HashMap<>(initialSize);
29      }
30  
31      /**
32       * Constructor for TypeMap that takes in an initial set of types.
33       *
34       * @param types Class[]
35       */
36      public TypeMap(Class<?>... types) {
37          this(types.length);
38          add(types);
39      }
40  
41      /**
42       * Adds a type to the receiver and stores it keyed by both its full and
43       * short names. Throws an exception if the short name of the argument
44       * matches an existing one already in the map for a different class.
45       *
46       * @param type Class
47       * @throws IllegalArgumentException
48       */
49      @SuppressWarnings("PMD.CompareObjectsWithEquals")
50      public void add(Class<?> type) {
51          final String shortName = ClassUtil.withoutPackageName(type.getName());
52          Class<?> existingType = typesByName.get(shortName);
53          if (existingType == null) {
54              typesByName.put(type.getName(), type);
55              typesByName.put(shortName, type);
56              return;
57          }
58  
59          if (existingType != type) {
60              throw new IllegalArgumentException("Short name collision between existing " + existingType + " and new "
61                      + type);
62          }
63      }
64  
65      /**
66       * Returns whether the type is known to the receiver.
67       *
68       * @param type Class
69       * @return boolean
70       */
71      public boolean contains(Class<?> type) {
72          return typesByName.containsValue(type);
73      }
74  
75      /**
76       * Returns whether the typeName is known to the receiver.
77       *
78       * @param typeName String
79       * @return boolean
80       */
81      public boolean contains(String typeName) {
82          return typesByName.containsKey(typeName);
83      }
84  
85      /**
86       * Returns the type for the typeName specified.
87       *
88       * @param typeName String
89       * @return Class
90       */
91      public Class<?> typeFor(String typeName) {
92          return typesByName.get(typeName);
93      }
94  
95      /**
96       * Adds an array of types to the receiver at once.
97       *
98       * @param types Class[]
99       */
100     public void add(Class<?>... types) {
101         for (Class<?> element : types) {
102             add(element);
103         }
104     }
105 
106     /**
107      * Creates and returns a map of short type names (without the package
108      * prefixes) keyed by the classes themselves.
109      *
110      * @return Map
111      */
112     public Map<Class<?>, String> asInverseWithShortName() {
113         Map<Class<?>, String> inverseMap = new HashMap<>(typesByName.size() / 2);
114 
115         Iterator<Map.Entry<String,Class<?>>> iter = typesByName.entrySet().iterator();
116         while (iter.hasNext()) {
117             Map.Entry<String,Class<?>> entry = iter.next();
118             storeShortest(inverseMap, entry.getValue(), entry.getKey());
119         }
120 
121         return inverseMap;
122     }
123 
124     /**
125      * Returns the total number of entries in the receiver. This will be exactly
126      * twice the number of types added.
127      *
128      * @return the total number of entries in the receiver
129      */
130     public int size() {
131         return typesByName.size();
132     }
133 
134     /**
135      * Store the shorter of the incoming value or the existing value in the map
136      * at the key specified.
137      *
138      * @param map
139      * @param key
140      * @param value
141      */
142     private void storeShortest(Map<Class<?>, String> map, Class<?> key, String value) {
143         String existingValue = map.get(key);
144 
145         if (existingValue == null) {
146             map.put(key, value);
147             return;
148         }
149 
150         if (existingValue.length() < value.length()) {
151             return;
152         }
153 
154         map.put(key, value);
155     }
156 }