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 }