/*
* Copyright 2013 Samppa Saarela
*
* 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.javersion.util;
import static com.google.common.collect.Iterators.transform;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import org.javersion.util.AbstractHashMap.EntryNode;
import com.google.common.base.Function;
public abstract class AbstractHashMap<K, V, This extends AbstractHashMap<K, V, This>>
extends AbstractHashTrie<K, EntryNode<K,V>, AbstractHashMap<K, V, This>>
implements Iterable<Map.Entry<K, V>> {
@SuppressWarnings("rawtypes")
private static final Function TO_ENTRY = (Object input) -> toEntry((Map.Entry) input);
public This assoc(K key, V value) {
return assoc(new EntryNode<K, V>(key, value));
}
private This assoc(java.util.Map.Entry<? extends K, ? extends V> entry) {
return merge(entry, null);
}
public This assocAll(Map<? extends K, ? extends V> map) {
return mergeAll(map, null);
}
public This assocAll(Iterable<Map.Entry<K, V>> entries) {
return mergeAll(entries, null);
}
public This merge(K key, V value, Merger<Map.Entry<K, V>> merger) {
return doMerge(new EntryNode<K, V>(key, value), merger);
}
public This merge(Map.Entry<? extends K, ? extends V> entry, Merger<Map.Entry<K, V>> merger) {
return doMerge(toEntry(entry), merger);
}
@SuppressWarnings("unchecked")
protected This doMerge(EntryNode<K, V> entry, Merger<Map.Entry<K, V>> merger) {
final UpdateContext<Map.Entry<K, V>> updateContext = updateContext(1, merger);
return (This) doAdd(updateContext, entry);
}
@SuppressWarnings("unchecked")
public This mergeAll(Map<? extends K, ? extends V> map, Merger<Map.Entry<K, V>> merger) {
final UpdateContext<Map.Entry<K, V>> updateContext = updateContext(map.size(), merger);
return (This) doAddAll(updateContext, transform(map.entrySet().iterator(), TO_ENTRY));
}
@SuppressWarnings("unchecked")
public This mergeAll(Iterable<Map.Entry<K, V>> entries, Merger<Map.Entry<K, V>> merger) {
final UpdateContext<Map.Entry<K, V>> updateContext = updateContext(32, merger);
return (This) doAddAll(updateContext, transform(entries.iterator(), TO_ENTRY));
}
protected UpdateContext<Map.Entry<K, V>> updateContext(int expectedSize, Merger<Map.Entry<K, V>> merger) {
return new UpdateContext<>(expectedSize, merger);
}
public This dissoc(Object key) {
return dissoc(key, null);
}
@SuppressWarnings("unchecked")
public This dissoc(Object key, Merger<Map.Entry<K, V>> merger) {
final UpdateContext<Map.Entry<K, V>> updateContext = updateContext(1, merger);
return (This) doRemove(updateContext, key);
}
public V get(Object key) {
EntryNode<K, V> entry = root().find(key);
return entry != null ? entry.getValue() : null;
}
public boolean containsKey(Object key) {
return root().find(key) != null;
}
public Iterator<Map.Entry<K, V>> iterator() {
return transform(doIterator(), Map.Entry.class::cast);
}
@SuppressWarnings("unchecked")
protected static <K, V> EntryNode<K, V> toEntry(Map.Entry<? extends K, ? extends V> entry) {
if (entry instanceof EntryNode) {
return (EntryNode<K, V>) entry;
} else {
return new EntryNode<>(entry.getKey(), entry.getValue());
}
}
public static final class EntryNode<K, V> extends AbstractHashTrie.EntryNode<K, EntryNode<K, V>> implements Map.Entry<K, V> {
final V value;
public EntryNode(K key, V value) {
super(key);
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V value) {
throw new UnsupportedOperationException();
}
public String toString() {
return "" + key + ": " + value;
}
@Override
public Node<K, EntryNode<K, V>> assocInternal(final UpdateContext<? super EntryNode<K, V>> currentContext, final int shift, final int hash, final EntryNode<K, V> newEntry) {
if (Objects.equals(key, newEntry.key)) {
if (Objects.equals(value, newEntry.value)) {
return this;
}
return currentContext.merge(this, newEntry) ? newEntry : this;
}
return split(currentContext, shift, hash, newEntry);
}
}
static class EntrySpliterator<K, V> extends NodeSpliterator<Map.Entry<K, V>, K, EntryNode<K, V>> {
public EntrySpliterator(Node<K, EntryNode<K, V>> node, int sizeEstimate, boolean immutable) {
super(node, sizeEstimate, DISTINCT | (immutable ? IMMUTABLE : 0));
}
private EntrySpliterator(Node<K, EntryNode<K, V>>[] array, int pos, int limit, int sizeEstimate, boolean immutable) {
super(array, pos, limit, sizeEstimate, DISTINCT | (immutable ? IMMUTABLE : 0));
}
@Override
protected NodeSpliterator<Map.Entry<K, V>, K, EntryNode<K, V>> newSubSpliterator(Node<K, EntryNode<K, V>>[] array, int pos, int limit, int sizeEstimate) {
return new EntrySpliterator<>(array, pos, limit, sizeEstimate, hasCharacteristics(IMMUTABLE));
}
@Override
protected Map.Entry<K, V> apply(EntryNode<K, V> entry) {
return entry;
}
}
static class KeySpliterator<K, V> extends NodeSpliterator<K, K, EntryNode<K, V>> {
public KeySpliterator(Node<K, EntryNode<K, V>> node, int sizeEstimate, boolean immutable) {
super(node, sizeEstimate, DISTINCT | (immutable ? IMMUTABLE : 0));
}
private KeySpliterator(Node<K, EntryNode<K, V>>[] array, int pos, int limit, int sizeEstimate, boolean immutable) {
super(array, pos, limit, sizeEstimate, DISTINCT | (immutable ? IMMUTABLE : 0));
}
@Override
protected NodeSpliterator<K, K, EntryNode<K, V>> newSubSpliterator(Node<K, EntryNode<K, V>>[] array, int pos, int limit, int sizeEstimate) {
return new KeySpliterator<>(array, pos, limit, sizeEstimate, hasCharacteristics(IMMUTABLE));
}
@Override
protected K apply(EntryNode<K, V> entry) {
return entry.getKey();
}
}
static class ValueSpliterator<K, V> extends NodeSpliterator<V, K, EntryNode<K, V>> {
public ValueSpliterator(Node<K, EntryNode<K, V>> node, int sizeEstimate, boolean immutable) {
super(node, sizeEstimate, (immutable ? IMMUTABLE : 0));
}
private ValueSpliterator(Node<K, EntryNode<K, V>>[] array, int pos, int limit, int sizeEstimate, boolean immutable) {
super(array, pos, limit, sizeEstimate, (immutable ? IMMUTABLE : 0));
}
@Override
protected NodeSpliterator<V, K, EntryNode<K, V>> newSubSpliterator(Node<K, EntryNode<K, V>>[] array, int pos, int limit, int sizeEstimate) {
return new ValueSpliterator<>(array, pos, limit, sizeEstimate, hasCharacteristics(IMMUTABLE));
}
@Override
protected V apply(EntryNode<K, V> entry) {
return entry.getValue();
}
}
}