/* * This file is part of the HyperGraphDB source distribution. This is copyrighted * software. For permitted uses, licensing options and redistribution, please see * the LicensingInformation file at the root level of the distribution. * * Copyright (c) 2005-2010 Kobrix Software, Inc. All rights reserved. */ package org.hypergraphdb.type; import org.hypergraphdb.HGHandle; import org.hypergraphdb.HGIndex; import org.hypergraphdb.HGSearchResult; import org.hypergraphdb.HGSearchable; import org.hypergraphdb.HGSystemFlags; import org.hypergraphdb.HGPersistentHandle; import org.hypergraphdb.HyperGraph; import org.hypergraphdb.IncidenceSetRef; import org.hypergraphdb.LazyRef; import org.hypergraphdb.atom.HGAtomRef; import org.hypergraphdb.query.impl.UnionResult; import org.hypergraphdb.storage.BAtoHandle; import org.hypergraphdb.storage.BAUtils; import org.hypergraphdb.storage.ByteArrayConverter; /** * <p> * Represents the type of a <code>HGAtomRef</code> value. This type implementation * handles the behavior of atom references depending on their mode (@see HGAtomRef.java * for a thorough description of reference modes and the relationship of an atom reference * to its referent). * </p> * * <p> * The implementation maintains a count for all types of references and triggers the correct * action (as the case may be) on the referent when all counts go to 0. * </p> * * @author Borislav Iordanov */ public class AtomRefType implements HGAtomType, HGSearchable<HGPersistentHandle, HGPersistentHandle>, ByteArrayConverter<Object> { // public static final HGPersistentHandle HGHANDLE = // HGHandleFactory.makeHandle("2ec10476-d964-11db-a08c-eb6f4c8f155a"); // // // IMPLEMENTATION NOTE: for a given referent atom, we are reference counting each // type of reference (hard, symbolic or floating). Because the 'make' operation should // be fast and the 'release' may be slower, we maintain a separate value handle for each // type of reference to a given atom. Hence 3 indices that allows us to retrieve the count // of a particular reference type from the referent's atom handle. // private static final String IDX_HARD_DB_NAME = "hg_atomrefs_hard_idx"; private static final String IDX_SYMBOLIC_DB_NAME = "hg_atomrefs_symbolic_idx"; private static final String IDX_FLOATING_DB_NAME = "hg_atomrefs_floating_idx"; private static final int MODE_OFFSET = 0; // 1 byte for the mode and 4 for the reference count private static final int REFCOUNT_OFFSET = 1; // 1 byte for the mode and 4 for the reference count private static final int ATOM_HANDLE_OFFSET = 5; // 1 byte for the mode and 4 for the reference count private HyperGraph graph; private HGIndex<HGPersistentHandle, HGPersistentHandle> hardIdx = null; private HGIndex<HGPersistentHandle, HGPersistentHandle> symbolicIdx = null; private HGIndex<HGPersistentHandle, HGPersistentHandle> floatingIdx = null; public HGIndex<HGPersistentHandle, HGPersistentHandle> getHardIdx() { if (hardIdx == null) { hardIdx = graph.getStore().getIndex(IDX_HARD_DB_NAME, BAtoHandle.getInstance(graph.getHandleFactory()), BAtoHandle.getInstance(graph.getHandleFactory()), null, true); } return hardIdx; } public HGIndex<HGPersistentHandle, HGPersistentHandle> getSymbolicIdx() { if (symbolicIdx == null) { symbolicIdx = graph.getStore().getIndex(IDX_SYMBOLIC_DB_NAME, BAtoHandle.getInstance(graph.getHandleFactory()), BAtoHandle.getInstance(graph.getHandleFactory()), null, true); } return symbolicIdx; } public HGIndex<HGPersistentHandle, HGPersistentHandle> getFloatingIdx() { if (floatingIdx == null) { floatingIdx = graph.getStore().getIndex(IDX_FLOATING_DB_NAME, BAtoHandle.getInstance(graph.getHandleFactory()), BAtoHandle.getInstance(graph.getHandleFactory()), null, true); } return floatingIdx; } // private class RemovalListener implements HGListener // { // public HGListener.Result handle(HyperGraph hg, HGEvent event) // { // HGAtomRemoveRequestEvent ev = (HGAtomRemoveRequestEvent)event; // HGPersistentHandle pHandle = hg.getPersistentHandle(ev.getAtomHandle()); // if (getHardIdx().findFirst(pHandle) != null || // getFloatingIdx().findFirst(pHandle) != null) // symbolic links don't prevent removal of atoms // return Result.cancel; // else // return Result.ok; // } // } // // private RemovalListener removalListener = new RemovalListener(); public void setHyperGraph(HyperGraph hg) { // unlikely that we would ever change the HyperGraph instance, but who knows.... // if (this.graph != null) // this.graph.getEventManager().removeListener(HGAtomRemoveRequestEvent.class, removalListener); this.graph = hg; // hg.getEventManager().addListener(HGAtomRemoveRequestEvent.class, removalListener); } public Object make(HGPersistentHandle handle, LazyRef<HGHandle[]> targetSet, IncidenceSetRef incidenceSet) { byte [] data = graph.getStore().getData(handle); HGAtomRef.Mode mode = HGAtomRef.Mode.get(data[MODE_OFFSET]); HGPersistentHandle atomHandle = graph.getHandleFactory().makeHandle(data, ATOM_HANDLE_OFFSET); return new HGAtomRef(atomHandle, mode); } public HGPersistentHandle store(Object instance) { HGAtomRef ref = (HGAtomRef)instance; HGPersistentHandle refHandle = graph.getPersistentHandle(ref.getReferent()); HGPersistentHandle valueHandle; HGIndex<HGPersistentHandle, HGPersistentHandle> idx; switch (ref.getMode()) { case hard: idx = getHardIdx(); break; case symbolic: idx = getSymbolicIdx(); break; case floating: idx = getFloatingIdx(); break; default: idx = null; // impossible } valueHandle = idx.findFirst(refHandle); int handleSize = refHandle.toByteArray().length; if (valueHandle == null) { // we store the mode followed by the handle of the referent followed by a reference count byte [] data = new byte[5 + handleSize]; data[MODE_OFFSET] = ref.getMode().getCode(); System.arraycopy(refHandle.toByteArray(), 0, data, ATOM_HANDLE_OFFSET, handleSize); BAUtils.writeInt(1, data, REFCOUNT_OFFSET); valueHandle = graph.getStore().store(data); idx.addEntry(refHandle, valueHandle); } else { byte [] data = graph.getStore().getData(valueHandle); BAUtils.writeInt(BAUtils.readInt(data, REFCOUNT_OFFSET) + 1, data, REFCOUNT_OFFSET); graph.getStore().store(valueHandle, data); } return valueHandle; } public void release(HGPersistentHandle handle) { byte [] data = graph.getStore().getData(handle); HGAtomRef.Mode mode = HGAtomRef.Mode.get(data[MODE_OFFSET]); int count = BAUtils.readInt(data, REFCOUNT_OFFSET) - 1; if (count == 0) { boolean makeManaged = false; boolean removeRef = false; HGPersistentHandle otherRef = null; HGPersistentHandle refHandle = graph.getHandleFactory().makeHandle(data, ATOM_HANDLE_OFFSET); switch (mode) { case hard: { otherRef = getFloatingIdx().findFirst(refHandle); if (otherRef != null) { makeManaged = true; removeRef = BAUtils.readInt(graph.getStore().getData(otherRef), REFCOUNT_OFFSET) == 0; } else removeRef = true; getHardIdx().removeAllEntries(refHandle); break; } case symbolic: { graph.getStore().removeData(handle); getSymbolicIdx().removeAllEntries(refHandle); break; } case floating: { makeManaged = true; otherRef = getHardIdx().findFirst(refHandle); removeRef = otherRef == null || BAUtils.readInt(graph.getStore().getData(otherRef), REFCOUNT_OFFSET) == 0; getFloatingIdx().removeAllEntries(refHandle); break; } } if (removeRef) { graph.getStore().removeData(handle); if (otherRef != null) graph.getStore().removeData(otherRef); if (makeManaged) { int flags = graph.getSystemFlags(refHandle); if ((flags & HGSystemFlags.MANAGED) == 0) graph.setSystemFlags(refHandle, flags | HGSystemFlags.MANAGED); } else graph.remove(refHandle); } } else { BAUtils.writeInt(count, data, REFCOUNT_OFFSET); graph.getStore().store(handle, data); } } public boolean subsumes(Object general, Object specific) { return ((HGAtomRef)general).getReferent().equals(((HGAtomRef)specific).getReferent()); } /** * The key is expected to be of type <code>HGAtomRef</code> OR of * type <code>HGHandle</code>. In the former case, references with the specific * mode and referent are search. In the latter or if the mode of the HGAtomRef is null, * all reference regardless of mode are searched. * */ @SuppressWarnings("unchecked") public HGSearchResult<HGPersistentHandle> find(HGPersistentHandle key) { if (key instanceof HGAtomRef) { HGAtomRef ref = (HGAtomRef)key; HGPersistentHandle pHandle = graph.getPersistentHandle(ref.getReferent()); switch (ref.getMode()) { case hard: return getHardIdx().find(pHandle); case symbolic: return getSymbolicIdx().find(pHandle); case floating: return getFloatingIdx().find(pHandle); } } HGPersistentHandle referent = graph.getPersistentHandle((HGHandle)key); return new UnionResult(getHardIdx().find(referent), new UnionResult(getSymbolicIdx().find(referent), getFloatingIdx().find(referent))); } public Object fromByteArray(byte[] byteArray, int offset, int length) { HGPersistentHandle h = BAtoHandle.getInstance(graph.getHandleFactory()).fromByteArray(byteArray, offset, length); if (h.equals(graph.getHandleFactory().nullHandle())) return null; else return graph.get(h); } public byte[] toByteArray(Object object) { if (object == null) return graph.getHandleFactory().nullHandle().toByteArray(); else return graph.getPersistentHandle(graph.getHandle(object)).toByteArray(); } }