BUG-7464: Add a dedicated ImmutableEntrySet 67/49967/13
authorRobert Varga <rovarga@cisco.com>
Tue, 3 Jan 2017 11:39:11 +0000 (12:39 +0100)
committerRobert Varga <nite@hq.sk>
Tue, 10 Jan 2017 23:44:42 +0000 (23:44 +0000)
Having immutability as a specialization is useful, as we can make explicit
assumptions about the map not moving. This will be important as we implement
specialized interators and spliterators.

Change-Id: I00a36a5bfc48dc31b088a46b07545f0442679b49
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/AbstractEntrySet.java [new file with mode: 0644]
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/ImmutableEntrySet.java [new file with mode: 0644]
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/ImmutableTrieMap.java
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/MutableEntrySet.java [moved from third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/EntrySet.java with 63% similarity]
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/MutableTrieMap.java
third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/TrieMap.java
third-party/triemap/src/test/java/org/opendaylight/yangtools/triemap/TestMapIterator.java
third-party/triemap/src/test/java/org/opendaylight/yangtools/triemap/TestReadOnlyAndUpdatableIterators.java

diff --git a/third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/AbstractEntrySet.java b/third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/AbstractEntrySet.java
new file mode 100644 (file)
index 0000000..c915945
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 2017 Pantheon Technologies, s.r.o. and others.
+ *
+ * Licensed 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.opendaylight.yangtools.triemap;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.AbstractSet;
+import java.util.Map.Entry;
+
+/**
+ * Abstract base class for implementing {@link TrieMap} entry sets.
+ *
+ * @author Robert Varga
+ *
+ * @param <K> the type of entry keys
+ * @param <V> the type of entry values
+ */
+abstract class AbstractEntrySet<K, V> extends AbstractSet<Entry<K, V>> {
+    private final TrieMap<K, V> map;
+
+    AbstractEntrySet(final TrieMap<K, V> map) {
+        this.map = checkNotNull(map);
+    }
+
+    final TrieMap<K, V> map() {
+        return map;
+    }
+
+    @Override
+    public final boolean contains(final Object o) {
+        if (!(o instanceof Entry)) {
+            return false;
+        }
+
+        final Entry<?, ?> e = (Entry<?, ?>) o;
+        final Object key = e.getKey();
+        if (key == null) {
+            return false;
+        }
+
+        final V v = map.get(key);
+        return v != null && v.equals(e.getValue());
+    }
+
+    @Override
+    public final int size() {
+        return map.size();
+    }
+}
diff --git a/third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/ImmutableEntrySet.java b/third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/ImmutableEntrySet.java
new file mode 100644 (file)
index 0000000..9f03140
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * (C) Copyright 2017 Pantheon Technologies, s.r.o. and others.
+ *
+ * Licensed 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.opendaylight.yangtools.triemap;
+
+import static org.opendaylight.yangtools.triemap.ImmutableTrieMap.unsupported;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+/**
+ * {@link AbstractEntrySet} implementation guarding against attempts to mutate the underlying map.
+ *
+ * @author Robert Varga
+ *
+ * @param <K> the type of entry keys
+ * @param <V> the type of entry values
+ */
+final class ImmutableEntrySet<K, V> extends AbstractEntrySet<K, V> {
+    ImmutableEntrySet(final TrieMap<K, V> map) {
+        super(map);
+    }
+
+    @Override
+    public void clear() {
+        throw unsupported();
+    }
+
+    @Override
+    public Iterator<Entry<K, V>> iterator() {
+        return map().immutableIterator();
+    }
+
+    @Override
+    public boolean remove(final Object o) {
+        throw unsupported();
+    }
+
+    @Override
+    public boolean removeAll(final Collection<?> c) {
+        throw unsupported();
+    }
+
+    @Override
+    public boolean retainAll(final Collection<?> c) {
+        throw unsupported();
+    }
+}
index 167a4b2a6544c12c93b9b95d5452f2620bf73e5b..6f2982ad8d285691a2c399cb41cd37bee578817b 100644 (file)
@@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.annotations.Beta;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.function.BiFunction;
 import java.util.function.Function;
@@ -118,6 +119,11 @@ public final class ImmutableTrieMap<K, V> extends TrieMap<K, V> {
         return this;
     }
 
+    @Override
+    ImmutableEntrySet<K, V> createEntrySet() {
+        return new ImmutableEntrySet<>(this);
+    }
+
     @Override
     boolean isReadOnly() {
         return true;
@@ -128,7 +134,12 @@ public final class ImmutableTrieMap<K, V> extends TrieMap<K, V> {
         return root;
     }
 
-    private static UnsupportedOperationException unsupported() {
+    static UnsupportedOperationException unsupported() {
         return new UnsupportedOperationException("Attempted to modify a read-only view");
     }
+
+    @Override
+    Iterator<Entry<K, V>> iterator() {
+        return immutableIterator();
+    }
 }
similarity index 63%
rename from third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/EntrySet.java
rename to third-party/triemap/src/main/java/org/opendaylight/yangtools/triemap/MutableEntrySet.java
index 6e7d69de2a674b49d81fcc329c1de54b466647e3..9476186ae5b6472ab34248f293ddfe8ced0371f8 100644 (file)
@@ -15,9 +15,8 @@
  */
 package org.opendaylight.yangtools.triemap;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkArgument;
 
-import java.util.AbstractSet;
 import java.util.Iterator;
 import java.util.Map.Entry;
 
@@ -27,30 +26,30 @@ import java.util.Map.Entry;
  * @param <K> the type of keys
  * @param <V> the type of values
  */
-final class EntrySet<K, V> extends AbstractSet<Entry<K, V>> {
-    private final TrieMap<K, V> map;
-
-    EntrySet(final TrieMap<K, V> map) {
-        this.map = checkNotNull(map);
+final class MutableEntrySet<K, V> extends AbstractEntrySet<K, V> {
+    MutableEntrySet(final TrieMap<K, V> map) {
+        super(map);
     }
 
     @Override
-    public Iterator<Entry<K, V>> iterator() {
-        return map.iterator();
+    public boolean add(final Entry<K, V> e) {
+        final K k = e.getKey();
+        checkArgument(k != null);
+        final V v = e.getValue();
+        checkArgument(v != null);
+
+        final V prev = map().putIfAbsent(k, v);
+        return prev == null || !v.equals(prev);
     }
 
     @Override
-    public boolean contains(final Object o) {
-        if (!(o instanceof Entry)) {
-            return false;
-        }
+    public void clear() {
+        map().clear();
+    }
 
-        final Entry<?, ?> e = (Entry<?, ?>) o;
-        if (e.getKey() == null) {
-            return false;
-        }
-        final V v = map.get(e.getKey());
-        return v != null && v.equals(e.getValue());
+    @Override
+    public Iterator<Entry<K, V>> iterator() {
+        return map().iterator();
     }
 
     @Override
@@ -69,16 +68,6 @@ final class EntrySet<K, V> extends AbstractSet<Entry<K, V>> {
             return false;
         }
 
-        return map.remove(key, value);
-    }
-
-    @Override
-    public final int size() {
-        return map.size();
-    }
-
-    @Override
-    public final void clear() {
-        map.clear();
+        return map().remove(key, value);
     }
 }
index bc461caad290715b26c7e01f6069ba34f1fa887f..9a46e2878e9e1105ab310192b3b59cfe567a9f8e 100644 (file)
@@ -23,6 +23,7 @@ import static org.opendaylight.yangtools.triemap.PresencePredicate.PRESENT;
 import com.google.common.annotations.Beta;
 import com.google.common.base.Verify;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Iterator;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 
@@ -133,6 +134,13 @@ final class MutableTrieMap<K, V> extends TrieMap<K, V> {
         }
     }
 
+    @Override
+    MutableEntrySet<K, V> createEntrySet() {
+        // FIXME: it would be nice to have a ReadWriteTrieMap with read-only iterator
+        //        if (readOnlyEntrySet) return ImmutableEntrySet(this);
+        return new MutableEntrySet<>(this);
+    }
+
     @Override
     boolean isReadOnly() {
         return false;
@@ -255,4 +263,9 @@ final class MutableTrieMap<K, V> extends TrieMap<K, V> {
             this.nv = nv;
         }
     }
+
+    @Override
+    Iterator<Entry<K, V>> iterator() {
+        return new TrieMapIterator<>(0, this);
+    }
 }
index 0aacd4f5f71109d6b8285d8ee2fe1e3c3dd547ad..b93fa4a367fea9b9043e0a4adcbb790a176e2c5b 100644 (file)
@@ -42,12 +42,10 @@ import java.util.concurrent.ConcurrentMap;
 public abstract class TrieMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K,V>, Serializable {
     private static final long serialVersionUID = 1L;
 
-    /**
-     * EntrySet
-     */
-    private final EntrySet<K, V> entrySet = new EntrySet<>(this);
     private final Equivalence<? super K> equiv;
 
+    private AbstractEntrySet<K, V> entrySet;
+
     TrieMap(final Equivalence<? super K> equiv) {
         this.equiv = equiv;
     }
@@ -95,7 +93,11 @@ public abstract class TrieMap<K, V> extends AbstractMap<K, V> implements Concurr
 
     @Override
     public final Set<Entry<K, V>> entrySet() {
-        return entrySet;
+        AbstractEntrySet<K, V> ret = entrySet;
+        if (ret == null) {
+            entrySet = ret = createEntrySet();
+        }
+        return ret;
     }
 
     @Override
@@ -131,12 +133,36 @@ public abstract class TrieMap<K, V> extends AbstractMap<K, V> implements Concurr
 
     /* internal methods implemented by subclasses */
 
+    abstract AbstractEntrySet<K, V> createEntrySet();
+
     abstract boolean isReadOnly();
 
     abstract INode<K, V> RDCSS_READ_ROOT(boolean abort);
 
+    /**
+     * Return an iterator over a TrieMap.
+     *
+     * If this is a read-only snapshot, it would return a read-only iterator.
+     *
+     * If it is the original TrieMap or a non-readonly snapshot, it would return
+     * an iterator that would allow for updates.
+     *
+     * @return
+     */
+    abstract Iterator<Entry<K, V>> iterator();
+
     /* internal methods provided for subclasses */
 
+    /**
+     * Return an iterator over a TrieMap.
+     * This is a read-only iterator.
+     *
+     * @return
+     */
+    final Iterator<Entry<K, V>> immutableIterator() {
+        return new TrieMapReadOnlyIterator<>(0, immutableSnapshot());
+    }
+
     @SuppressWarnings("null")
     static <V> V toNullable(final Optional<V> opt) {
         return opt.orElse(null);
@@ -185,29 +211,4 @@ public abstract class TrieMap<K, V> extends AbstractMap<K, V> implements Concurr
 
         return (V) res;
     }
-
-    /**
-     * Return an iterator over a TrieMap.
-     *
-     * If this is a read-only snapshot, it would return a read-only iterator.
-     *
-     * If it is the original TrieMap or a non-readonly snapshot, it would return
-     * an iterator that would allow for updates.
-     *
-     * @return
-     */
-    final Iterator<Entry<K, V>> iterator() {
-        // FIXME: it would be nice to have a ReadWriteTrieMap with read-only iterator
-        return isReadOnly() ? new TrieMapReadOnlyIterator<>(0, this) : new TrieMapIterator<>(0, this);
-    }
-
-    /**
-     * Return an iterator over a TrieMap.
-     * This is a read-only iterator.
-     *
-     * @return
-     */
-    final Iterator<Entry<K, V>> readOnlyIterator() {
-        return new TrieMapReadOnlyIterator<>(0, immutableSnapshot());
-    }
 }
index 36fc3587b145e19a9289e8d79851184d9bd2d1fd..ab6e9fdd1253da1dcfcb656fdf01dc9db969a0fe 100644 (file)
@@ -89,7 +89,7 @@ public class TestMapIterator {
 
     @Test(expected = NoSuchElementException.class)
     public void testEmptyReadOnlyIterator() {
-        failAdvance(TrieMap.create().readOnlyIterator());
+        failAdvance(TrieMap.create().immutableIterator());
     }
 
     @Test(expected = NoSuchElementException.class)
index 15a658e21e628e07560b66b41951b0adf2b605b6..fd31f798d4a8fd0f47c0deac4ab6f450d2f88cd1 100644 (file)
@@ -53,22 +53,22 @@ public class TestReadOnlyAndUpdatableIterators {
 
     @Test(expected = UnsupportedOperationException.class)
     public void testReadOnlyIteratorSet() {
-        trySet(bt.readOnlyIterator());
+        trySet(bt.immutableIterator());
     }
 
     @Test(expected = UnsupportedOperationException.class)
     public void testReadOnlyIteratorRemove() {
-        tryRemove(bt.readOnlyIterator());
+        tryRemove(bt.immutableIterator());
     }
 
     @Test(expected = UnsupportedOperationException.class)
     public void testReadOnlySnapshotReadOnlyIteratorSet() {
-        trySet(bt.immutableSnapshot().readOnlyIterator());
+        trySet(bt.immutableSnapshot().immutableIterator());
     }
 
     @Test(expected = UnsupportedOperationException.class)
     public void testReadOnlySnapshotReadOnlyIteratorRemove() {
-        tryRemove(bt.immutableSnapshot().readOnlyIterator());
+        tryRemove(bt.immutableSnapshot().immutableIterator());
     }
 
     @Test(expected = UnsupportedOperationException.class)