View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.util;
8   
9   import java.io.IOException;
10  import java.io.ObjectInputStream;
11  import java.io.ObjectOutputStream;
12  import java.io.Serializable;
13  import java.util.Map;
14  
15  import org.apache.commons.collections.map.AbstractHashedMap;
16  
17  /**
18   * A case-insensitive <code>Map</code>.
19   * <p/>
20   * As entries are added to the map, keys hash values are lowercase hash codes of the key.  the
21   * Real key case is preserved.
22   * <p/>
23   * <p/>
24   * The <code>keySet()</code> method returns all keys in their original case
25   * <p/>
26   * <strong>Note that CaseInsensitiveMap is not synchronized and is not thread-safe.</strong>
27   * If you wish to use this map from multiple threads concurrently, you must use
28   * appropriate synchronization. The simplest approach is to wrap this map
29   * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw
30   * exceptions when accessed by concurrent threads without synchronization.
31   *
32   * @since 3.0.0
33   */
34  public class CaseInsensitiveHashMap extends AbstractHashedMap implements Serializable
35  {
36      /**
37       * Serialisation version
38       */
39      private static final long serialVersionUID = -7074633917369299456L;
40  
41      /**
42       * Constructs a new empty map with default size and load factor.
43       */
44      public CaseInsensitiveHashMap()
45      {
46          super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD);
47      }
48  
49  
50      /**
51       * Constructs a new, empty map with the specified initial capacity.
52       *
53       * @param initialCapacity the initial capacity
54       * @throws IllegalArgumentException if the initial capacity is less than one
55       */
56      public CaseInsensitiveHashMap(int initialCapacity) throws IllegalArgumentException
57      {
58          super(initialCapacity);
59      }
60  
61      /**
62       * Constructs a new, empty map with the specified initial capacity and
63       * load factor.
64       *
65       * @param initialCapacity the initial capacity
66       * @param loadFactor      the load factor
67       * @throws IllegalArgumentException if the initial capacity is less than one
68       * @throws IllegalArgumentException if the load factor is less than zero
69       */
70      public CaseInsensitiveHashMap(int initialCapacity, float loadFactor) throws IllegalArgumentException
71      {
72          super(initialCapacity, loadFactor);
73      }
74  
75      /**
76       * Constructor copying elements from another map.
77       * <p/>
78       * Keys will be converted to lower case strings, which may cause
79       * some entries to be removed (if string representation of keys differ
80       * only by character case).
81       *
82       * @param map the map to copy
83       * @throws NullPointerException if the map is null
84       */
85      public CaseInsensitiveHashMap(Map map)
86      {
87          super(map);
88      }
89  
90      /**
91       * Creates a hash value from the lower case value of the key. The same function will be used when querying
92       * a value in the map also
93       *
94       * @param key the key value to hash
95       * @return a hash value for the lower case key
96       */
97      @Override
98      protected int hash(Object key)
99      {
100         return super.hash(key.toString().toLowerCase());
101     }
102 
103     /**
104      * Overloads the default behaviour to compare the keys without case sensitivity
105      *
106      * @param key1 the first key
107      * @param key2 the key to compare against
108      * @return true is the keys match
109      */
110     @Override
111     protected boolean isEqualKey(Object key1, Object key2)
112     {
113         if (key1 instanceof String && key2 instanceof String)
114         {
115             return (((String) key1).equalsIgnoreCase((String) key2));
116         }
117         else
118         {
119             return super.isEqualKey(key1, key2);
120         }
121     }
122 
123     //-----------------------------------------------------------------------
124 
125     /**
126      * Clones the map without cloning the keys or values.
127      *
128      * @return a shallow clone
129      */
130     @Override
131     public Object clone()
132     {
133         return super.clone();
134     }
135 
136     /**
137      * Write the map out using a custom routine.
138      */
139     private void writeObject(ObjectOutputStream out) throws IOException
140     {
141         out.defaultWriteObject();
142         doWriteObject(out);
143     }
144 
145     /**
146      * Read the map in using a custom routine.
147      */
148     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
149     {
150         in.defaultReadObject();
151         doReadObject(in);
152     }
153 }