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: [email protected]
For additional commands, e-mail: [email protected]