20fb567b90e0f282842481a433088821f6b2b516
[yangtools.git] / third-party / triemap / src / main / java / org / opendaylight / yangtools / triemap / MutableIterator.java
1 /*
2  * (C) Copyright 2017 Pantheon Technologies, s.r.o. and others.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.opendaylight.yangtools.triemap;
17
18 import static com.google.common.base.Preconditions.checkState;
19
20 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
21 import java.util.Map.Entry;
22
23 /**
24  * Specialized immutable iterator for use with {@link ImmutableEntrySet}.
25  *
26  * @author Robert Varga
27  *
28  * @param <K> the type of entry keys
29  * @param <V> the type of entry values
30  */
31 final class MutableIterator<K, V> extends AbstractIterator<K, V> {
32     private final MutableTrieMap<K, V> mutable;
33
34     private Entry<K, V> lastReturned;
35
36     MutableIterator(final MutableTrieMap<K, V> map) {
37         super(map.immutableSnapshot());
38         this.mutable = map;
39     }
40
41     @Override
42     public void remove() {
43         checkState(lastReturned != null);
44         mutable.remove(lastReturned.getKey());
45         lastReturned = null;
46     }
47
48     @Override
49     Entry<K, V> wrapEntry(final Entry<K, V> entry) {
50         lastReturned = entry;
51         return new MutableEntry<>(mutable, entry);
52     }
53
54     /**
55      * A mutable view of an entry in the map. Since the backing map is concurrent, its {@link #getValue()} and
56      * {@link #setValue(Object)} methods cannot guarantee consistency with the base map and may produce surprising
57      * results when the map is concurrently modified, either directly or via another entry/iterator.
58      *
59      * <p>
60      * The behavior is similar to what Java 8's ConcurrentHashMap does, which is probably the most consistent handling
61      * of this case without requiring expensive and revalidation.
62      */
63     private static final class MutableEntry<K, V> implements Entry<K, V> {
64         private final MutableTrieMap<K, V> map;
65         private final Entry<K, V> delegate;
66
67         @SuppressWarnings("null")
68         private V newValue = null;
69
70         MutableEntry(final MutableTrieMap<K, V> map, final Entry<K, V> delegate) {
71             this.map = map;
72             this.delegate = delegate;
73         }
74
75         @Override
76         public K getKey() {
77             return delegate.getKey();
78         }
79
80         /**
81          * {@inheritDoc}
82          *
83          * @implSpec
84          * This implementation returns the most uptodate value we have observed via this entry. It does not reflect
85          *     concurrent modifications, nor does it throw {@link IllegalStateException} if the entry is removed.
86          */
87         @Override
88         public V getValue() {
89             return newValue != null ? newValue : delegate.getValue();
90         }
91
92         /**
93          * {@inheritDoc}
94          *
95          * @implSpec
96          * This implementation returns the most uptodate value we have observed via this entry. It does not reflect
97          *     concurrent modifications, nor does it throw {@link IllegalStateException} if the entry is removed.
98          */
99         @Override
100         public V setValue(final V value) {
101             final V ret = getValue();
102             map.put(getKey(), value);
103             newValue = value;
104             return ret;
105         }
106
107         @Override
108         public int hashCode() {
109             return EntryUtil.hash(getKey(), getValue());
110         }
111
112         @SuppressFBWarnings(value = "EQ_UNUSUAL",  justification = "Equality handled by utility methods")
113         @Override
114         public boolean equals(final Object obj) {
115             return EntryUtil.equal(obj, getKey(), getValue());
116         }
117
118         @Override
119         public String toString() {
120             return EntryUtil.string(getKey(), getValue());
121         }
122     }
123 }