Author: markt
Date: Fri Nov 28 14:53:15 2014
New Revision: 1642307

URL: http://svn.apache.org/r1642307
Log:
Add a map that supports case insensitive keys. E.g. for use with HTTP headers.

Added:
    tomcat/trunk/java/org/apache/tomcat/websocket/CaseInsensitiveKeyMap.java   
(with props)
    
tomcat/trunk/test/org/apache/tomcat/websocket/TestCaseInsensitiveKeyMap.java   
(with props)

Added: tomcat/trunk/java/org/apache/tomcat/websocket/CaseInsensitiveKeyMap.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/CaseInsensitiveKeyMap.java?rev=1642307&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/CaseInsensitiveKeyMap.java 
(added)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/CaseInsensitiveKeyMap.java 
Fri Nov 28 14:53:15 2014
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.websocket;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A Map implementation that uses case-insensitive (using {@link 
Locale#ENGLISH}
+ * strings as keys.
+ * <p>
+ * This implementation is not thread-safe.
+ *
+ * @param <V> Type of values placed in this Map.
+ */
+public class CaseInsensitiveKeyMap<V> extends AbstractMap<String,V> {
+
+    private final Map<Key,V> map = new HashMap<>();
+
+
+    @Override
+    public V get(Object key) {
+        return map.get(Key.getInstance(key));
+    }
+
+
+    @Override
+    public V put(String key, V value) {
+        return map.put(Key.getInstance(key), value);
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * <b>Use this method with caution</b>. If the input Map contains duplicate
+     * keys when the keys are compared in a case insensitive manner then some
+     * values will be lost when inserting via this method.
+     */
+    @Override
+    public void putAll(Map<? extends String, ? extends V> m) {
+        super.putAll(m);
+    }
+
+
+    @Override
+    public boolean containsKey(Object key) {
+        return map.containsKey(Key.getInstance(key));
+    }
+
+
+    @Override
+    public V remove(Object key) {
+        return map.remove(Key.getInstance(key));
+    }
+
+
+    @Override
+    public Set<Entry<String, V>> entrySet() {
+        return new EntrySet<>(map.entrySet());
+    }
+
+
+    private static class EntrySet<V> extends AbstractSet<Entry<String,V>> {
+
+        private final Set<Entry<Key,V>> entrySet;
+
+        public EntrySet(Set<Map.Entry<Key,V>> entrySet) {
+            this.entrySet = entrySet;
+        }
+
+        @Override
+        public Iterator<Entry<String,V>> iterator() {
+            return new EntryIterator<>(entrySet.iterator());
+        }
+
+        @Override
+        public int size() {
+            return entrySet.size();
+        }
+    }
+
+
+    private static class EntryIterator<V> implements Iterator<Entry<String,V>> 
{
+
+        private final Iterator<Entry<Key,V>> iterator;
+
+        public EntryIterator(Iterator<Entry<Key,V>> iterator) {
+            this.iterator = iterator;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return iterator.hasNext();
+        }
+
+        @Override
+        public Entry<String,V> next() {
+            Entry<Key,V> entry = iterator.next();
+            return new EntryImpl<>(entry.getKey().getKey(), entry.getValue());
+        }
+
+        @Override
+        public void remove() {
+            iterator.remove();
+        }
+    }
+
+
+    private static class EntryImpl<V> implements Entry<String,V> {
+
+        private final String key;
+        private final V value;
+
+        public EntryImpl(String key, V value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        @Override
+        public String getKey() {
+            return key;
+        }
+
+        @Override
+        public V getValue() {
+            return value;
+        }
+
+        @Override
+        public V setValue(V value) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    private static class Key {
+
+        private final String key;
+
+        private Key(String key) {
+            this.key = key;
+        }
+
+        public String getKey() {
+            return key;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result +
+                    ((key == null) ? 0 : 
key.toLowerCase(Locale.ENGLISH).hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null) {
+                return false;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            Key other = (Key) obj;
+            if (key == null) {
+                if (other.key != null) {
+                    return false;
+                }
+            } else if (!key.toLowerCase(Locale.ENGLISH).equals(
+                    other.key.toLowerCase(Locale.ENGLISH))) {
+                return false;
+            }
+            return true;
+        }
+
+        public static Key getInstance(Object o) {
+            if (o instanceof String) {
+                return new Key((String) o);
+            }
+            return null;
+        }
+    }
+}

Propchange: 
tomcat/trunk/java/org/apache/tomcat/websocket/CaseInsensitiveKeyMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
tomcat/trunk/test/org/apache/tomcat/websocket/TestCaseInsensitiveKeyMap.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/TestCaseInsensitiveKeyMap.java?rev=1642307&view=auto
==============================================================================
--- 
tomcat/trunk/test/org/apache/tomcat/websocket/TestCaseInsensitiveKeyMap.java 
(added)
+++ 
tomcat/trunk/test/org/apache/tomcat/websocket/TestCaseInsensitiveKeyMap.java 
Fri Nov 28 14:53:15 2014
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.websocket;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestCaseInsensitiveKeyMap {
+
+    @Test
+    public void testPut() {
+        Object o1 = new Object();
+        Object o2 = new Object();
+
+        CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+        map.put("a", o1);
+        Object o = map.put("A", o2);
+
+        Assert.assertEquals(o1,  o);
+
+        Assert.assertEquals(o2, map.get("a"));
+        Assert.assertEquals(o2, map.get("A"));
+    }
+
+
+    @Test
+    public void testGet() {
+        Object o1 = new Object();
+
+        CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+        map.put("a", o1);
+
+        Assert.assertEquals(o1, map.get("a"));
+        Assert.assertEquals(o1, map.get("A"));
+    }
+
+
+    @Test
+    public void testContainsKey() {
+        Object o1 = new Object();
+
+        CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+        map.put("a", o1);
+
+        Assert.assertTrue(map.containsKey("a"));
+        Assert.assertTrue(map.containsKey("A"));
+    }
+
+
+    @Test
+    public void testContainsValue() {
+        Object o1 = new Object();
+
+        CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+        map.put("a", o1);
+
+        Assert.assertTrue(map.containsValue(o1));
+    }
+
+
+    @Test
+    public void testRemove() {
+        Object o1 = new Object();
+
+        CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+        map.put("a", o1);
+        Assert.assertFalse(map.isEmpty());
+        map.remove("A");
+        Assert.assertTrue(map.isEmpty());
+
+        map.put("A", o1);
+        Assert.assertFalse(map.isEmpty());
+        map.remove("a");
+        Assert.assertTrue(map.isEmpty());
+    }
+
+
+    @Test
+    public void testClear() {
+        Object o1 = new Object();
+
+        CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+        for (int i = 0; i < 10; i++) {
+            map.put(Integer.toString(i), o1);
+        }
+        Assert.assertEquals(10, map.size());
+        map.clear();
+        Assert.assertEquals(0, map.size());
+    }
+
+
+    @Test
+    public void testPutAll() {
+        Object o1 = new Object();
+        Object o2 = new Object();
+
+        Map<String,Object> source = new HashMap<>();
+        source.put("a", o1);
+        source.put("A", o2);
+
+        CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+        map.putAll(source);
+
+        Assert.assertEquals(1, map.size());
+        Assert.assertTrue(map.containsValue(o1) != map.containsValue(o2));
+    }
+
+
+    @Test
+    public void testKeySetContains() {
+        Object o1 = new Object();
+
+        CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+        map.put("a", o1);
+
+        Set<String> keys = map.keySet();
+
+        Assert.assertTrue(keys.contains("a"));
+        Assert.assertTrue(keys.contains("A"));
+    }
+
+
+    @Test
+    public void testKeySetRemove() {
+        Object o1 = new Object();
+
+        CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+        map.put("a", o1);
+
+        Iterator<String> iter = map.keySet().iterator();
+        Assert.assertTrue(iter.hasNext());
+        iter.next();
+        iter.remove();
+        Assert.assertTrue(map.isEmpty());
+    }
+
+
+    @Test
+    public void testEntrySetRemove() {
+        Object o1 = new Object();
+
+        CaseInsensitiveKeyMap<Object> map = new CaseInsensitiveKeyMap<>();
+        map.put("a", o1);
+
+        Iterator<Entry<String,Object>> iter = map.entrySet().iterator();
+        Assert.assertTrue(iter.hasNext());
+        Entry<String,Object> entry = iter.next();
+        Assert.assertEquals("a", entry.getKey());
+        Assert.assertEquals(o1, entry.getValue());
+        iter.remove();
+        Assert.assertTrue(map.isEmpty());
+    }
+}

Propchange: 
tomcat/trunk/test/org/apache/tomcat/websocket/TestCaseInsensitiveKeyMap.java
------------------------------------------------------------------------------
    svn:eol-style = native



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to