/* * (C) Copyright 2016 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 static org.opendaylight.yangtools.triemap.LookupResult.RESTART; import com.google.common.annotations.Beta; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.AbstractMap; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentMap; /** * This is a port of Scala's TrieMap class from the Scala Collections library. This implementation does not support * null keys nor null values. * * @author Aleksandar Prokopec (original Scala implementation) * @author Roman Levenstein (original Java 6 port) * @author Robert Varga * * @param <K> the type of keys maintained by this map * @param <V> the type of mapped values */ @Beta public abstract class TrieMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K,V>, Serializable { private static final long serialVersionUID = 1L; private final Equivalence<? super K> equiv; private AbstractEntrySet<K, V> entrySet; private AbstractKeySet<K> keySet; TrieMap(final Equivalence<? super K> equiv) { this.equiv = equiv; } public static <K, V> TrieMap<K, V> create() { return new MutableTrieMap<>(Equivalence.equals()); } /** * Returns a snapshot of this TrieMap. This operation is lock-free and * linearizable. * * <p> * The snapshot is lazily updated - the first time some branch in the * snapshot or this TrieMap are accessed, they are rewritten. This means * that the work of rebuilding both the snapshot and this TrieMap is * distributed across all the threads doing updates or accesses subsequent * to the snapshot creation. */ public abstract TrieMap<K, V> mutableSnapshot(); /** * Returns a read-only snapshot of this TrieMap. This operation is lock-free * and linearizable. * * <p> * The snapshot is lazily updated - the first time some branch of this * TrieMap are accessed, it is rewritten. The work of creating the snapshot * is thus distributed across subsequent updates and accesses on this * TrieMap by all threads. Note that the snapshot itself is never rewritten * unlike when calling the `snapshot` method, but the obtained snapshot * cannot be modified. * * <p> * This method is used by other methods such as `size` and `iterator`. */ public abstract ImmutableTrieMap<K, V> immutableSnapshot(); @Override public final boolean containsKey(final Object key) { return get(key) != null; } @Override public final boolean containsValue(final Object value) { return super.containsValue(checkNotNull(value)); } @Override public final Set<Entry<K, V>> entrySet() { final AbstractEntrySet<K, V> ret; return (ret = entrySet) != null ? ret : (entrySet = createEntrySet()); } @Override public final Set<K> keySet() { final AbstractKeySet<K> ret; return (ret = keySet) != null ? ret : (keySet = createKeySet()); } @Override public final V get(final Object key) { @SuppressWarnings("unchecked") final K k = (K) checkNotNull(key); return lookuphc(k, computeHash(k)); } @Override public abstract void clear(); @Override public abstract V put(K key, V value); @Override public abstract V putIfAbsent(K key, V value); @Override public abstract V remove(Object key); @Override public abstract boolean remove(Object key, Object value); @Override public abstract boolean replace(K key, V oldValue, V newValue); @Override public abstract V replace(K key, V value); @Override public abstract int size(); /* internal methods implemented by subclasses */ abstract AbstractEntrySet<K, V> createEntrySet(); abstract AbstractKeySet<K> createKeySet(); abstract boolean isReadOnly(); abstract INode<K, V> RDCSS_READ_ROOT(boolean abort); /** * Return an iterator over a TrieMap. * * <p> * If this is a read-only snapshot, it would return a read-only iterator. * * <p> * If it is the original TrieMap or a non-readonly snapshot, it would return * an iterator that would allow for updates. * * @return An iterator. */ abstract AbstractIterator<K, V> iterator(); /* internal methods provided for subclasses */ /** * Return an iterator over a TrieMap. This is a read-only iterator. * * @return A read-only iterator. */ final ImmutableIterator<K, V> immutableIterator() { return new ImmutableIterator<>(immutableSnapshot()); } @SuppressWarnings("null") static <V> V toNullable(final Optional<V> opt) { return opt.orElse(null); } final int computeHash(final K key) { return equiv.hash(key); } final Object writeReplace() throws ObjectStreamException { return new SerializationProxy(immutableSnapshot(), isReadOnly()); } /* package-protected utility methods */ final Equivalence<? super K> equiv() { return equiv; } final INode<K, V> readRoot() { return RDCSS_READ_ROOT(false); } // FIXME: abort = false by default final INode<K, V> readRoot(final boolean abort) { return RDCSS_READ_ROOT(abort); } final INode<K, V> RDCSS_READ_ROOT() { return RDCSS_READ_ROOT(false); } final boolean equal(final K k1, final K k2) { return equiv.equivalent(k1, k2); } /* private implementation methods */ @SuppressWarnings("unchecked") private V lookuphc(final K key, final int hc) { Object res; do { // Keep looping as long as RESTART is being indicated res = RDCSS_READ_ROOT().rec_lookup(key, hc, 0, null, this); } while (res == RESTART); return (V) res; } }