package org.opendaylight.yangtools.util;
import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Verify.verify;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.Beta;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
public abstract class MutableOffsetMap<K, V> extends AbstractMap<K, V> implements Cloneable, ModifiableMapPhase<K, V> {
static final class Ordered<K, V> extends MutableOffsetMap<K, V> {
Ordered() {
- super(new LinkedHashMap<>());
}
Ordered(final Map<K, V> source) {
- super(OffsetMapCache.orderedOffsets(source.keySet()), source, new LinkedHashMap<>());
+ super(OffsetMapCache.orderedOffsets(source.keySet()), source);
}
Ordered(final ImmutableMap<K, Integer> offsets, final V[] objects) {
- super(offsets, objects, new LinkedHashMap<>());
+ super(offsets, objects);
}
@Override
SharedSingletonMap<K, V> singletonMap() {
return SharedSingletonMap.orderedCopyOf(this);
}
+
+ @Override
+ HashMap<K, V> createNewKeys() {
+ return new LinkedHashMap<>();
+ }
}
static final class Unordered<K, V> extends MutableOffsetMap<K, V> {
Unordered() {
- super(new HashMap<>());
}
Unordered(final Map<K, V> source) {
- super(OffsetMapCache.unorderedOffsets(source.keySet()), source, new HashMap<>());
+ super(OffsetMapCache.unorderedOffsets(source.keySet()), source);
}
Unordered(final ImmutableMap<K, Integer> offsets, final V[] objects) {
- super(offsets, objects, new HashMap<>());
+ super(offsets, objects);
}
@Override
SharedSingletonMap<K, V> singletonMap() {
return SharedSingletonMap.unorderedCopyOf(this);
}
+
+ @Override
+ HashMap<K, V> createNewKeys() {
+ return new HashMap<>();
+ }
}
private static final Object[] EMPTY_ARRAY = new Object[0];
private transient volatile int modCount;
private boolean needClone = true;
- MutableOffsetMap(final ImmutableMap<K, Integer> offsets, final V[] objects, final HashMap<K, V> newKeys) {
- verify(newKeys.isEmpty());
+ MutableOffsetMap(final ImmutableMap<K, Integer> offsets, final Object[] objects) {
this.offsets = requireNonNull(offsets);
this.objects = requireNonNull(objects);
- this.newKeys = requireNonNull(newKeys);
}
- @SuppressWarnings("unchecked")
- MutableOffsetMap(final HashMap<K, V> newKeys) {
- this(ImmutableMap.of(), (V[]) EMPTY_ARRAY, newKeys);
+ MutableOffsetMap() {
+ this(ImmutableMap.of(), EMPTY_ARRAY);
}
- @SuppressWarnings("unchecked")
- MutableOffsetMap(final ImmutableMap<K, Integer> offsets, final Map<K, V> source, final HashMap<K, V> newKeys) {
- this(offsets, (V[]) new Object[offsets.size()], newKeys);
+ MutableOffsetMap(final ImmutableMap<K, Integer> offsets, final Map<K, V> source) {
+ this(offsets, new Object[offsets.size()]);
for (Entry<K, V> e : source.entrySet()) {
objects[offsets.get(e.getKey())] = requireNonNull(e.getValue());
@Override
public final int size() {
- return offsets.size() + newKeys.size() - removed;
+ return offsets.size() - removed + (newKeys == null ? 0 : newKeys.size());
}
@Override
}
}
- return newKeys.containsKey(key);
+ return newKeys != null && newKeys.containsKey(key);
}
@Override
}
}
- return newKeys.get(key);
+ return newKeys == null ? null : newKeys.get(key);
}
private void cloneArray() {
}
}
+ if (newKeys == null) {
+ newKeys = createNewKeys();
+ }
final V ret = newKeys.put(key, value);
if (ret == null) {
modCount++;
}
}
+ if (newKeys == null) {
+ return null;
+ }
final V ret = newKeys.remove(key);
if (ret != null) {
modCount++;
@Override
public final void clear() {
if (size() != 0) {
- newKeys.clear();
+ if (newKeys != null) {
+ newKeys.clear();
+ }
cloneArray();
Arrays.fill(objects, removedObject());
removed = objects.length;
@Override
public @NonNull Map<K, V> toUnmodifiableMap() {
- if (removed == 0 && newKeys.isEmpty()) {
+ if (removed == 0 && noNewKeys()) {
// Make sure next modification clones the array, as we leak it to the map we return.
needClone = true;
} else {
keyset.addAll(offsets.keySet());
}
- keyset.addAll(newKeys.keySet());
+ if (newKeys != null) {
+ keyset.addAll(newKeys.keySet());
+ }
// Construct the values
@SuppressWarnings("unchecked")
System.arraycopy(objects, 0, values, 0, offsets.size());
offset = offsets.size();
}
- for (V v : newKeys.values()) {
- values[offset++] = v;
+ if (newKeys != null) {
+ for (V v : newKeys.values()) {
+ values[offset++] = v;
+ }
}
return modifiedMap(keyset, values);
throw new IllegalStateException("Clone is expected to work", e);
}
- ret.newKeys = (HashMap<K, V>) newKeys.clone();
+ ret.newKeys = newKeys == null ? null : (HashMap<K, V>) newKeys.clone();
ret.needClone = true;
return ret;
}
}
}
- return result + newKeys.hashCode();
+ return newKeys != null ? result + newKeys.hashCode() : result;
}
@Override
if (obj instanceof ImmutableOffsetMap) {
final ImmutableOffsetMap<?, ?> om = (ImmutableOffsetMap<?, ?>) obj;
- if (newKeys.isEmpty() && offsets.equals(om.offsets())) {
+ if (noNewKeys() && offsets.equals(om.offsets())) {
return Arrays.deepEquals(objects, om.objects());
}
} else if (obj instanceof MutableOffsetMap) {
final MutableOffsetMap<?, ?> om = (MutableOffsetMap<?, ?>) obj;
if (offsets.equals(om.offsets)) {
- return Arrays.deepEquals(objects, om.objects) && newKeys.equals(om.newKeys);
+ return Arrays.deepEquals(objects, om.objects) && equalNewKeys(om);
}
}
// Fall back to brute map compare
- final Map<?, ?> other = (Map<?, ?>)obj;
+ return mapEquals((Map<?, ?>)obj);
+ }
+
+ private boolean equalNewKeys(final MutableOffsetMap<?, ?> other) {
+ return noNewKeys() ? other.noNewKeys() : newKeys.equals(other.newKeys());
+ }
+ private boolean mapEquals(final Map<?, ?> other) {
// Size and key sets have to match
if (size() != other.size() || !keySet().equals(other.keySet())) {
return false;
}
try {
- // Ensure all newKeys are present. Note newKeys is guaranteed to
- // not contain null value.
- for (Entry<K, V> e : newKeys.entrySet()) {
- if (!e.getValue().equals(other.get(e.getKey()))) {
- return false;
+ if (newKeys != null) {
+ // Ensure all newKeys are present. Note newKeys is guaranteed to not contain a null value.
+ for (Entry<K, V> e : newKeys.entrySet()) {
+ if (!e.getValue().equals(other.get(e.getKey()))) {
+ return false;
+ }
}
}
@VisibleForTesting
final Object newKeys() {
- return newKeys;
+ return newKeys != null ? newKeys : ImmutableMap.of();
+ }
+
+ abstract HashMap<K, V> createNewKeys();
+
+ private boolean noNewKeys() {
+ return newKeys == null || newKeys.isEmpty();
}
private final class EntrySet extends AbstractSet<Entry<K, V>> {
private abstract class AbstractSetIterator<E> implements Iterator<E> {
private final Iterator<Entry<K, Integer>> oldIterator = offsets.entrySet().iterator();
- private final Iterator<K> newIterator = newKeys.keySet().iterator();
+ private final Iterator<K> newIterator = newKeys == null ? Collections.emptyIterator()
+ : newKeys.keySet().iterator();
private int expectedModCount = modCount;
private @Nullable K currentKey = null;
private @Nullable K nextKey;