/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * CaseInsensitiveMap.java * Created: 17-Dec-2002 * By: Rick Cameron */ package org.openquark.util; import java.util.Collection; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Set; public final class CaseInsensitiveMap<V> implements Map<String, V> { /* * Implementation note: * * The backing map for CaseInsensitiveMap is a hashmap. * Lookups are done in the hashmap by converting the key to uppercase, then to lowercase. * * Using these rules: * 'I', 'i', and their dotted and dotless forms respectively (from the Turkish alphabet) are all considered equal. * '�', 'ss' and 'SS' are all considered equal. * * If TS raises an escalation about the first outcome, we can consider using the Turkish locale where it affects this. * */ /** * The backing map. * This maps the lower-case version of any key to the object. * This will cause strings to act as keys in the map in a case-insensitive way. */ private final Map<String, V> hashMap; /** * Constructor for a CaseInsensitiveMap. */ public CaseInsensitiveMap () { hashMap = new HashMap<String, V>(); } /** * Copy constructor for CaseInsensitiveMap. * @param mapToCopy */ public CaseInsensitiveMap(Map<String, V> mapToCopy) { // Note: this constructor is faster than using the arg-less constructor and calling putAll(). hashMap = new HashMap<String, V>(mapToCopy); } /** * @see java.lang.Object#equals(Object) */ @Override public boolean equals(Object obj) { if (obj == null || getClass() != obj.getClass()) { return false; } CaseInsensitiveMap<?> other = (CaseInsensitiveMap<?>)obj; return hashMap.equals(other.hashMap); } /** * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return hashMap.hashCode(); } /** * Returns the number of items in the map. */ public int size() { return hashMap.size(); } /** * Removes all elements from this map. */ public void clear() { hashMap.clear(); } /** * Returns a set view of the keys contained in this map. * @return Set */ public Set<String> keySet() { return hashMap.keySet(); } public boolean containsKey (String key) { return hashMap.containsKey(getCaseInsensitiveKey(key)); } public V get (String key) { return hashMap.get(getCaseInsensitiveKey(key)); } public V put (String key, V value) { return hashMap.put(getCaseInsensitiveKey(key), value); } public V remove(String key) { return hashMap.remove(getCaseInsensitiveKey(key)); } public Collection<V> values () { return hashMap.values(); } /** * Method isEmpty. * @return whether the map is empty. */ public boolean isEmpty () { return hashMap.isEmpty(); } /** * Convert the given string to a key which will be equals() to other keys generated by calling this method with * strings which differ only in case from the one which was passed in. * @param s the string to convert. * @return a corresponding key which will be equals() to other keys generated by calling this method with * strings which differ only in case from the one which was passed in. */ private static final String getCaseInsensitiveKey(String s) { return s.toUpperCase(Locale.ENGLISH).toLowerCase(Locale.ENGLISH); } /** * Check that the given key is valid for this map. In other words, validates that * the key is a String. * @param key */ private static final void validateKey(Object key) { if (!(key instanceof String)) { throw new ClassCastException("This Map only accepts keys of type java.lang.String"); } } /** * Determine whether two strings are equal in a case-insensitive manner. * @param s1 * @param s2 * @return whether two strings are equal in a case-insensitive manner. */ public static boolean equalsCaseInsensitive(String s1, String s2) { // Check that both arguments are non null if (s1 != null && s2 != null) { // Note: do not perform a quick length comparison, as "�" is equivalent to "ss" return getCaseInsensitiveKey(s1).equals(getCaseInsensitiveKey(s2)); } else { // One of the arguments is null. They are only equal if they are both null. return s1 == s2; } } /** * Get a hash code on the string s which treats strings which differ only in case as being the same. * Note: this method may be relatively expensive. * @param s the string to hash. * @return a hash code for the string, treating it in a case-insensitive way. */ public static int caseInsensitiveHashCode(String s) { return getCaseInsensitiveKey(s).hashCode(); } /** * @see java.util.Map#entrySet() */ public Set<Map.Entry<String, V>> entrySet() { return hashMap.entrySet(); } /* (non-Javadoc) * @see java.util.Map#containsKey(java.lang.Object) */ public boolean containsKey(Object key) { validateKey(key); return containsKey((String)key); } /* (non-Javadoc) * @see java.util.Map#containsValue(java.lang.Object) */ public boolean containsValue(Object value) { return hashMap.containsValue(value); } /** * @see java.util.Map#putAll(java.util.Map) */ public void putAll(Map<? extends String, ? extends V> t) { for (final Map.Entry<? extends String, ? extends V> entry : t.entrySet()) { put(entry.getKey(), entry.getValue()); } } /** * @see java.util.Map#get(java.lang.Object) */ public V get(Object key) { validateKey(key); return get((String)key); } /** * @see java.util.Map#remove(java.lang.Object) */ public V remove(Object key) { validateKey(key); return remove((String)key); } }