package spimedb.index.oct; import spimedb.util.geom.Vec3D; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.logging.Logger; /** TODO extract infinispan to subclass allowing any Map impl for index */ public class OctMap<K,V extends OctBox.IdBB> implements Map<K,V> { private static final Logger logger = Logger.getLogger(OctMap.class.getSimpleName()); // + ":" + id); /** holder for _oct for infinispan persistence */ protected final Map<Long, OctBox<K>> box; protected final Map<K, V> map; protected final OctBox<K> root; boolean startupCheck = true; public OctMap(Map<K, V> items, Map<Long, OctBox<K>> boxes, Vec3D center, Vec3D radius, Vec3D resolution) { this.map = items; this.box = boxes; if (box.isEmpty()) { OctBox newBox = this.root = new OctBox(center, radius, resolution); box.put(0L, newBox); logger.info("new octree created: " + root); } else { this.root = box.get(0L); if (this.root == null) { throw new RuntimeException("Unable to load persisted OctBox:" + this.box); } logger.info("existing octbox loaded: " + root); if (startupCheck) { if (!validate()) { reindex(); flush(); } } } } @Override public int size() { return map.size(); } @Override public boolean isEmpty() { return map.isEmpty(); } @Override public boolean containsKey(Object key) { return map.containsKey(key); } @Override final public boolean containsValue(Object value) { return map.containsValue(value); } @Override final public V get(Object key) { return map.get(key); } final public V put(V value) { return put((K) value.id(), value); } @Override public V put(K key, V value) { V removed = map.put(key, value); if (removed!=null) { root.remove(removed); } if (root.put(value)==null) { throw new RuntimeException("Octree rejected value=" + value + ", key=" + key ); } return removed; } @Override public V remove(Object key) { V removed = map.remove(key); if (removed!=null) { if (!root.remove(removed)) { throw new RuntimeException("Octree missing value for key=" + key + '=' + removed); } } return removed; } @Override public void putAll(Map<? extends K, ? extends V> m) { map.putAll(m); root.putAll(m.values()); } @Override public void clear() { map.clear(); root.zero(); root.clear(); } @Override public Set<K> keySet() { return map.keySet(); } @Override public Collection<V> values() { return map.values(); } @Override public Set<Entry<K, V>> entrySet() { return map.entrySet(); } public void reindex() { logger.info("re-indexing " + map.size() + " items"); root.zero(); root.putAll(map.values()); validate(); } /** manually flush the octree to persistence */ public boolean flush() { box.put(0L, root); return validate(); } public boolean validate() { int e = root.itemCountRecursively(); int msize = map.size(); boolean consistent = (e == msize); logger.info("octbox contains " + e + " entries. consistent with map=" + msize + " is " + consistent); return consistent; } public OctBox box() { return root; } }