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 }