/* * LazyHashMapMap.java - This file is part of the Jakstab project. * Copyright 2007-2015 Johannes Kinder <jk@jakstab.org> * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code 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 General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, see <http://www.gnu.org/licenses/>. */ package org.jakstab.util; import java.util.*; import org.jakstab.util.Logger; /** * HashMapMap with lazy copying. I.e., only copies submaps if they are modified. * * @author Johannes Kinder */ public final class LazyHashMapMap<K, L, V> implements MapMap<K,L,V> { @SuppressWarnings("unused") private static final Logger logger = Logger.getLogger(LazyHashMapMap.class); private static final class ReferenceCountingHashMapMap<M, N, W> extends HashMapMap<M, N, W> { private int referenceCount; private int hashCode = 0; public ReferenceCountingHashMapMap() { super(); referenceCount = 1; } public ReferenceCountingHashMapMap( ReferenceCountingHashMapMap<M, N, W> innerMap) { super(innerMap); referenceCount = 1; } protected Map<N, W> createSubMap() { return new LazyTreeMap<N, W>(); } protected Map<N, W> createSubMap(Map<N, W> proto) { return new LazyTreeMap<N, W>((LazyTreeMap<N, W>)proto); } public void addRef() { referenceCount++; } public void release() { referenceCount--; } public boolean isShared() { return referenceCount > 1; } } public LazyHashMapMap() { this.innerMap = new ReferenceCountingHashMapMap<K, L, V>(); } public LazyHashMapMap(LazyHashMapMap<K, L, V> proto) { this.innerMap = proto.innerMap; this.innerMap.addRef(); } private ReferenceCountingHashMapMap<K, L, V> innerMap; public boolean makeExclusive() { if (innerMap.isShared()) { innerMap.release(); innerMap = new ReferenceCountingHashMapMap<K, L, V>(innerMap); assert !innerMap.isShared(); return true; } return false; } @Override public void clear() { makeExclusive(); innerMap.clear(); } @Override public boolean containsKey(K keyLeft, L keyRight) { return innerMap.containsKey(keyLeft, keyRight); } @Override public boolean containsLeftKey(K keyLeft) { return innerMap.containsLeftKey(keyLeft); } @Override public V get(K keyLeft, L keyRight) { return innerMap.get(keyLeft, keyRight); } @Override public Map<L, V> getSubMap(K keyLeft) { Map<L, V> subMap = innerMap.getSubMap(keyLeft); if (subMap == null) return null; //return subMap; return Collections.unmodifiableMap(subMap); } public Iterator<Map.Entry<L, V>> subMapIterator(K keyLeft) { Map<L, V> subMap; if (innerMap.containsLeftKey(keyLeft)) { // Required, the caller might call remove on the iterator and // we would not notice the modification makeExclusive(); // subMap is now already from the copy subMap = innerMap.getSubMap(keyLeft); } else { subMap = Collections.emptyMap(); } return subMap.entrySet().iterator(); } @Override public boolean isEmpty() { return innerMap.isEmpty(); } @Override public Set<K> leftKeySet() { return innerMap.leftKeySet(); } @Override public V put(K keyLeft, L keyRight, V value) { makeExclusive(); return innerMap.put(keyLeft, keyRight, value); } @Override public void putAll(MapMap<K, L, V> other) { makeExclusive(); innerMap.putAll(other); } @Override public V remove(K keyLeft, L keyRight) { makeExclusive(); return innerMap.remove(keyLeft, keyRight); } @Override public void remove(K keyLeft) { makeExclusive(); innerMap.remove(keyLeft); } @Override public int size() { return innerMap.size(); } @Override public boolean equals(Object obj) { if (obj == null) return false; return innerMap.equals(((LazyHashMapMap<?,?,?>)obj).innerMap); } @Override public int hashCode() { if (innerMap.hashCode == 0) innerMap.hashCode = innerMap.hashCode(); return innerMap.hashCode; } @Override public String toString() { return innerMap.toString(); } @Override protected void finalize() throws Throwable { innerMap.release(); super.finalize(); } @Override public EntryIterator<K, L, V> entryIterator() { return innerMap.entryIterator(); } }