Repository: tapestry-5 Updated Branches: refs/heads/master 65b9671c2 -> 0a1993457
TAP5-2452: prevent modifications to CaseInsensitiveMap's key set Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/0a199345 Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/0a199345 Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/0a199345 Branch: refs/heads/master Commit: 0a19934571247b7642fb6b0a6899e2c368b1d0b9 Parents: 65b9671 Author: Jochen Kemnade <[email protected]> Authored: Wed Feb 18 10:08:32 2015 +0100 Committer: Jochen Kemnade <[email protected]> Committed: Thu Mar 19 16:06:08 2015 +0100 ---------------------------------------------------------------------- .../tapestry5/ioc/util/CaseInsensitiveMap.java | 73 +++++++++++++++++--- .../ioc/specs/CaseInsensitiveMapSpec.groovy | 23 ++++++ 2 files changed, 87 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/0a199345/commons/src/main/java/org/apache/tapestry5/ioc/util/CaseInsensitiveMap.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/tapestry5/ioc/util/CaseInsensitiveMap.java b/commons/src/main/java/org/apache/tapestry5/ioc/util/CaseInsensitiveMap.java index f5aff7e..7862f9f 100644 --- a/commons/src/main/java/org/apache/tapestry5/ioc/util/CaseInsensitiveMap.java +++ b/commons/src/main/java/org/apache/tapestry5/ioc/util/CaseInsensitiveMap.java @@ -29,7 +29,8 @@ import java.util.*; */ public class CaseInsensitiveMap<V> extends AbstractMap<String, V> implements Serializable { - private static final long serialVersionUID = 3362718337611953298L; + + private static final long serialVersionUID = -3162531976817110908L; private static final int NULL_HASH = Integer.MIN_VALUE; @@ -91,9 +92,17 @@ public class CaseInsensitiveMap<V> extends AbstractMap<String, V> implements Ser { return value == this.value || (value != null && value.equals(this.value)); } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("CIMEntry [key=").append(key).append(", value=").append(value).append("]"); + return builder.toString(); + } + } - private class EntrySetIterator implements Iterator + private class EntrySetIterator implements Iterator<Entry<String, V>> { int expectedModCount = modCount; @@ -108,7 +117,7 @@ public class CaseInsensitiveMap<V> extends AbstractMap<String, V> implements Ser } @Override - public Object next() + public Entry<String, V> next() { check(); @@ -135,13 +144,17 @@ public class CaseInsensitiveMap<V> extends AbstractMap<String, V> implements Ser { if (expectedModCount != modCount) throw new ConcurrentModificationException(); } + + @Override + public String toString() { + return "EntrySetIterator, current key: "+entries[current].key; + } } - @SuppressWarnings("unchecked") - private class EntrySet extends AbstractSet + private class EntrySet extends AbstractSet<Entry<String, V>> { @Override - public Iterator iterator() + public Iterator<Entry<String, V>> iterator() { return new EntrySetIterator(); } @@ -163,6 +176,7 @@ public class CaseInsensitiveMap<V> extends AbstractMap<String, V> implements Ser { if (!(o instanceof Map.Entry)) return false; + @SuppressWarnings("rawtypes") Map.Entry e = (Map.Entry) o; Position position = select(e.getKey()); @@ -175,6 +189,7 @@ public class CaseInsensitiveMap<V> extends AbstractMap<String, V> implements Ser { if (!(o instanceof Map.Entry)) return false; + @SuppressWarnings("rawtypes") Map.Entry e = (Map.Entry) o; Position position = select(e.getKey()); @@ -352,7 +367,6 @@ public class CaseInsensitiveMap<V> extends AbstractMap<String, V> implements Ser return size; } - @SuppressWarnings("unchecked") @Override public V put(String key, V value) { @@ -379,7 +393,6 @@ public class CaseInsensitiveMap<V> extends AbstractMap<String, V> implements Ser return select(key).remove(); } - @SuppressWarnings("unchecked") @Override public Set<Map.Entry<String, V>> entrySet() { @@ -388,6 +401,48 @@ public class CaseInsensitiveMap<V> extends AbstractMap<String, V> implements Ser return entrySet; } + @Override + public Set<String> keySet() { + + AbstractSet<String> set = new AbstractSet<String>() { + + @Override + public Iterator<String> iterator() { + + final Iterator<java.util.Map.Entry<String, V>> entrySetIterator = entrySet().iterator(); + + return new Iterator<String>() { + @Override + public boolean hasNext() { + return entrySetIterator.hasNext(); + } + @Override + public String next() { + String nextKey = entrySetIterator.next().getKey(); + return nextKey; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Modifications to the key set are not allowed."); + } + }; + } + + @Override + public boolean contains(Object o) { + return containsKey(o); + } + + @Override + public int size() { + return size; + } + }; + + return Collections.unmodifiableSet(set); + } + private Position select(Object key) { if (key == null || key instanceof String) @@ -416,7 +471,7 @@ public class CaseInsensitiveMap<V> extends AbstractMap<String, V> implements Ser { cursor = (low + high) >> 1; - CIMEntry e = entries[cursor]; + CIMEntry<V> e = entries[cursor]; if (e.hashCode < hashCode) { http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/0a199345/tapestry-ioc/src/test/groovy/ioc/specs/CaseInsensitiveMapSpec.groovy ---------------------------------------------------------------------- diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/CaseInsensitiveMapSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/CaseInsensitiveMapSpec.groovy index c4e43f0..4813003 100644 --- a/tapestry-ioc/src/test/groovy/ioc/specs/CaseInsensitiveMapSpec.groovy +++ b/tapestry-ioc/src/test/groovy/ioc/specs/CaseInsensitiveMapSpec.groovy @@ -1,6 +1,8 @@ package ioc.specs import org.apache.tapestry5.ioc.util.CaseInsensitiveMap + +import spock.lang.Issue; import spock.lang.Specification class CaseInsensitiveMapSpec extends Specification { @@ -300,4 +302,25 @@ class CaseInsensitiveMapSpec extends Specification { copy == map } + + @Issue('https://issues.apache.org/jira/browse/TAP5-2452') + def "Modifications to key set are not allowed"(){ + setup: + def map = new CaseInsensitiveMap<String>() + map.put('1', '1') + map.put('2', '2') + map.put('3', '3') + def keysToRetain = ['3', '4', '5'] + expect: + map.keySet().size() == 3 + map.keySet() == ['1', '2', '3'] as Set + when: + map.keySet().retainAll(keysToRetain) + then: + thrown(UnsupportedOperationException) + when: + map.keySet().remove("Zaphod") + then: + thrown(UnsupportedOperationException) + } }
