001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.collections.map;
018
019 import java.io.IOException;
020 import java.io.ObjectInputStream;
021 import java.io.ObjectOutputStream;
022 import java.io.Serializable;
023 import java.util.Map;
024
025 /**
026 * A case-insensitive <code>Map</code>.
027 * <p>
028 * As entries are added to the map, keys are converted to all lowercase. A new
029 * key is compared to existing keys by comparing <code>newKey.toString().toLower()</code>
030 * to the lowercase values in the current <code>KeySet.</code>
031 * <p>
032 * Null keys are supported.
033 * <p>
034 * The <code>keySet()</code> method returns all lowercase keys, or nulls.
035 * <p>
036 * Example:
037 * <pre><code>
038 * Map map = new CaseInsensitiveMap();
039 * map.put("One", "One");
040 * map.put("Two", "Two");
041 * map.put(null, "Three");
042 * map.put("one", "Four");
043 * </code></pre>
044 * creates a <code>CaseInsensitiveMap</code> with three entries.<br>
045 * <code>map.get(null)</code> returns <code>"Three"</code> and <code>map.get("ONE")</code>
046 * returns <code>"Four".</code> The <code>Set</code> returned by <code>keySet()</code>
047 * equals <code>{"one", "two", null}.</code>
048 * <p>
049 * <strong>Note that CaseInsensitiveMap is not synchronized and is not thread-safe.</strong>
050 * If you wish to use this map from multiple threads concurrently, you must use
051 * appropriate synchronization. The simplest approach is to wrap this map
052 * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw
053 * exceptions when accessed by concurrent threads without synchronization.
054 *
055 * @since Commons Collections 3.0
056 * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $
057 *
058 * @author Commons-Collections team
059 */
060 public class CaseInsensitiveMap extends AbstractHashedMap implements Serializable, Cloneable {
061
062 /** Serialisation version */
063 private static final long serialVersionUID = -7074655917369299456L;
064
065 /**
066 * Constructs a new empty map with default size and load factor.
067 */
068 public CaseInsensitiveMap() {
069 super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD);
070 }
071
072 /**
073 * Constructs a new, empty map with the specified initial capacity.
074 *
075 * @param initialCapacity the initial capacity
076 * @throws IllegalArgumentException if the initial capacity is less than one
077 */
078 public CaseInsensitiveMap(int initialCapacity) {
079 super(initialCapacity);
080 }
081
082 /**
083 * Constructs a new, empty map with the specified initial capacity and
084 * load factor.
085 *
086 * @param initialCapacity the initial capacity
087 * @param loadFactor the load factor
088 * @throws IllegalArgumentException if the initial capacity is less than one
089 * @throws IllegalArgumentException if the load factor is less than zero
090 */
091 public CaseInsensitiveMap(int initialCapacity, float loadFactor) {
092 super(initialCapacity, loadFactor);
093 }
094
095 /**
096 * Constructor copying elements from another map.
097 * <p>
098 * Keys will be converted to lower case strings, which may cause
099 * some entries to be removed (if string representation of keys differ
100 * only by character case).
101 *
102 * @param map the map to copy
103 * @throws NullPointerException if the map is null
104 */
105 public CaseInsensitiveMap(Map map) {
106 super(map);
107 }
108
109 //-----------------------------------------------------------------------
110 /**
111 * Overrides convertKey() from {@link AbstractHashedMap} to convert keys to
112 * lower case.
113 * <p>
114 * Returns null if key is null.
115 *
116 * @param key the key convert
117 * @return the converted key
118 */
119 protected Object convertKey(Object key) {
120 if (key != null) {
121 return key.toString().toLowerCase();
122 } else {
123 return AbstractHashedMap.NULL;
124 }
125 }
126
127 //-----------------------------------------------------------------------
128 /**
129 * Clones the map without cloning the keys or values.
130 *
131 * @return a shallow clone
132 */
133 public Object clone() {
134 return super.clone();
135 }
136
137 /**
138 * Write the map out using a custom routine.
139 */
140 private void writeObject(ObjectOutputStream out) throws IOException {
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 in.defaultReadObject();
150 doReadObject(in);
151 }
152
153 }