View Javadoc

1   package net.sourceforge.pmd.util;
2   
3   import java.util.HashMap;
4   import java.util.Iterator;
5   import java.util.Map;
6   
7   /**
8    * A specialized map that stores types by both their full and short (without package prefixes) names.
9    * If an incoming type shares the same name (but different package/prefix) with a type already in the
10   * map then an IllegalArgumentException will be thrown since any subsequent retrievals by said short
11   * name could be in error.
12   *
13   * @author Brian Remedios
14   */
15  public class TypeMap {
16  
17      private Map<String, Class<?>> typesByName;
18  
19      /**
20       * Constructor for TypeMap.
21       *
22       * @param initialSize int
23       */
24      public TypeMap(int initialSize) {
25          typesByName = new HashMap<String, Class<?>>(initialSize);
26      }
27  
28      /**
29       * Constructor for TypeMap that takes in an initial set of types.
30       *
31       * @param types Class[]
32       */
33      public TypeMap(Class<?>... types) {
34          this(types.length);
35          add(types);
36      }
37  
38      /**
39       * Adds a type to the receiver and stores it keyed by both its full and
40       * short names. Throws an exception if the short name of the argument
41       * matches an existing one already in the map for a different class.
42       *
43       * @param type Class
44       * @throws IllegalArgumentException
45       */
46      @SuppressWarnings("PMD.CompareObjectsWithEquals")
47      public void add(Class<?> type) {
48          final String shortName = ClassUtil.withoutPackageName(type.getName());
49          Class<?> existingType = typesByName.get(shortName);
50          if (existingType == null) {
51              typesByName.put(type.getName(), type);
52              typesByName.put(shortName, type);
53              return;
54          }
55  
56          if (existingType != type) {
57              throw new IllegalArgumentException("Short name collision between existing " + existingType + " and new "
58                      + type);
59          }
60      }
61  
62      /**
63       * Returns whether the type is known to the receiver.
64       *
65       * @param type Class
66       * @return boolean
67       */
68      public boolean contains(Class<?> type) {
69          return typesByName.containsValue(type);
70      }
71  
72      /**
73       * Returns whether the typeName is known to the receiver.
74       *
75       * @param typeName String
76       * @return boolean
77       */
78      public boolean contains(String typeName) {
79          return typesByName.containsKey(typeName);
80      }
81  
82      /**
83       * Returns the type for the typeName specified.
84       *
85       * @param typeName String
86       * @return Class
87       */
88      public Class<?> typeFor(String typeName) {
89          return typesByName.get(typeName);
90      }
91  
92      /**
93       * Adds an array of types to the receiver at once.
94       *
95       * @param types Class[]
96       */
97      public void add(Class<?>... types) {
98          for (Class<?> element : types) {
99              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<Map.Entry<String,Class<?>>> iter = typesByName.entrySet().iterator();
113         while (iter.hasNext()) {
114             Map.Entry<String,Class<?>> entry = iter.next();
115             storeShortest(inverseMap, entry.getValue(), entry.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<Class<?>, String> map, Class<?> key, String value) {
140         String existingValue = map.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 }