/* * Copyright (C) 2015 higherfrequencytrading.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ package net.openhft.chronicle.map; import net.openhft.chronicle.hash.function.SerializableFunction; import net.openhft.chronicle.hash.impl.util.CharSequences; import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.*; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; import static java.util.Collections.emptyList; import static net.openhft.chronicle.hash.impl.util.Objects.requireNonNull; interface AbstractChronicleMap extends ChronicleMap, Serializable { @Override default R getMapped(K key, @NotNull SerializableFunction function) { requireNonNull(function); try (ExternalMapQueryContext c = queryContext(key)) { MapEntry entry = c.entry(); return entry != null ? function.apply(entry.value().get()) : null; } } @Override default void getAll(File toFile) throws IOException { synchronized (this) { JsonSerializer.getAll(toFile, this, emptyList()); } } @Override default void putAll(File fromFile) throws IOException { synchronized (this) { JsonSerializer.putAll(fromFile, this, emptyList()); } } @Override default boolean containsValue(Object value) { return !forEachEntryWhile(c -> !c.value().equals(c.context().wrapValueAsData((V) value))); } @Override default boolean isEmpty() { return size() == 0; } @Override default void forEach(BiConsumer action) { forEachEntry(c -> action.accept(c.key().get(), c.value().get())); } @Override default void putAll(Map m) { m.forEach(this::put); } @NotNull @Override default Collection values() { // todo cache view object return new AbstractCollection() { @Override public Iterator iterator() { return new Iterator() { private Iterator> i = entrySet().iterator(); @Override public boolean hasNext() { return i.hasNext(); } @Override public V next() { return i.next().getValue(); } @Override public void remove() { i.remove(); } }; } @Override public int size() { return AbstractChronicleMap.this.size(); } @Override public boolean isEmpty() { return AbstractChronicleMap.this.isEmpty(); } @Override public void clear() { AbstractChronicleMap.this.clear(); } @Override public boolean contains(Object v) { return AbstractChronicleMap.this.containsValue(v); } @Override public void forEach(java.util.function.Consumer action) { AbstractChronicleMap.this.forEachEntry(c -> action.accept(c.value().get())); } }; } @NotNull @Override default Set keySet() { // todo cache view object return new AbstractSet() { @Override public Iterator iterator() { return new ChronicleMapIterator.OfKeys<>(AbstractChronicleMap.this); } @Override public int size() { return AbstractChronicleMap.this.size(); } public boolean isEmpty() { return AbstractChronicleMap.this.isEmpty(); } public void clear() { AbstractChronicleMap.this.clear(); } public boolean contains(Object k) { return AbstractChronicleMap.this.containsKey(k); } @Override public void forEach(java.util.function.Consumer action) { AbstractChronicleMap.this.forEachEntry(c -> action.accept(c.key().get())); } }; } default boolean mapEquals(Object o) { if (o == this) return true; if (!(o instanceof Map)) return false; Map m = (Map) o; if ((m instanceof ChronicleMap ? ((ChronicleMap) m).longSize() : m.size()) != longSize()) return false; try { return forEachEntryWhile(c -> { K k = c.key().get(); V v = (V) m.get(k instanceof CharSequence ? k.toString() : k); return v != null && c.value().equals(c.context().wrapValueAsData(v)); }); } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; } } default int mapHashCode() { int[] h = new int[1]; forEach((k, v) -> h[0] += hashCode(k) ^ hashCode(v)); return h[0]; } // TODO quick and dirty. Think about how generic guava/koloboke equivalence interface could be // used. See also BytesInterop.equivalent() and hash(). public static int hashCode(Object obj) { if (!(obj instanceof CharSequence)) { return obj.hashCode(); } else { return CharSequences.hash((CharSequence) obj); } } default String mapToString() { if (isEmpty()) return "{}"; StringBuilder sb = new StringBuilder(); sb.append('{'); forEach((k, v) -> sb .append(k != AbstractChronicleMap.this ? k : "(this Map)") .append('=') .append(v != AbstractChronicleMap.this ? v : "(this Map)") .append(',').append(' ')); if (sb.length() > 2) sb.setLength(sb.length() - 2); sb.append('}'); return sb.toString(); } default Set> newEntrySet() { return new ChronicleMapEntrySet<>(this); } @Override default void forEachEntry(final Consumer> action) { forEachEntryWhile(c -> { action.accept(c); return true; }); } @Override default boolean forEachEntryWhile(final Predicate> action) { boolean interrupt = false; for (int i = segments() - 1; i >= 0; i--) { try (MapSegmentContext c = segmentContext(i)) { if (!c.forEachSegmentEntryWhile(action)) { interrupt = true; break; } } } return !interrupt; } }