/* * 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.javaprimitive; import java.util.Comparator; import org.hypergraphdb.HGHandle; import org.hypergraphdb.HGIndex; import org.hypergraphdb.HGPersistentHandle; import org.hypergraphdb.HGSearchResult; import org.hypergraphdb.HGSortIndex; import org.hypergraphdb.HGStore; import org.hypergraphdb.HyperGraph; import org.hypergraphdb.HGOrderedSearchable; import org.hypergraphdb.HGException; import org.hypergraphdb.IncidenceSetRef; import org.hypergraphdb.LazyRef; import org.hypergraphdb.storage.BAtoBA; import org.hypergraphdb.storage.BAtoHandle; import org.hypergraphdb.storage.BAUtils; import org.hypergraphdb.type.HGPrimitiveType; import org.hypergraphdb.type.HGRefCountedType; /** * <p> * A generic, base implementation of the primitive Java types. * It maintains an index of all primitive values added for faster lookup. It also shares * primitive values for more compact storage - that is, the <code>store</code> * method will always return the same handle for an already added primitive value. * </p> * * <p> * Concrete classes for concrete primitive types must provide a name for the * managed index as well as a comparator class for their instance values. * </p> * * <p> * A primitively typed object is translated to a byte [] as * follows: * * <ul> * <li>The first 4 bytes consitute an unsigned integer - the reference count * for the string. Storing a value that's already in the store only * increments this reference count and conversely, removing a value decrements * it. Actual removal occurs when the count reaches 0.</li> * <li>The rest of the bytes constitute the actual value of the primitive type.</li> * </ul> * * </p> * * @author Borislav Iordanov */ public abstract class PrimitiveTypeBase<JavaType> implements HGPrimitiveType<JavaType>, HGOrderedSearchable<JavaType, HGPersistentHandle>, //Comparator<byte[]>, HGRefCountedType { protected HyperGraph graph = null; protected HGSortIndex<byte[], HGPersistentHandle> valueIndex = null; /** * <p>Return the <code>Comparator</code> class used for the order relation * of the primitive values.</p> */ /** * <p>Return the name of the DB index to create for the primitive values.</p> */ protected abstract String getIndexName(); /** * <p>The offset in the final <code>byte [] </code> of the actual * primitive value.</p> */ protected final static int dataOffset = 4; protected final HGSortIndex<byte[], HGPersistentHandle> getIndex() { if (valueIndex == null) { Comparator<byte[]> comparator = getComparator(); valueIndex = (HGSortIndex<byte[], HGPersistentHandle>)graph.getStore().getIndex(getIndexName(), BAtoBA.getInstance(), BAtoHandle.getInstance(graph.getHandleFactory()), comparator, true); } return valueIndex; } protected final int getRefCount(byte [] buf) { return BAUtils.readInt(buf, 0); } protected final void putRefCount(int c, byte [] buf) { BAUtils.writeInt(c, buf, 0); } protected final HGPersistentHandle storeImpl(byte [] data) { HGStore store = graph.getStore(); if (store.hasOverlayGraph()) { return store.store(data); } HGPersistentHandle handle = null; // // First lookup that string value in the DB and return its // handle if available. // HGIndex<byte[], HGPersistentHandle> idx = getIndex(); handle = idx.findFirst(data); if (handle == null) { handle = graph.getHandleFactory().makeHandle(); putRefCount(1, data); store.store(handle, data); idx.addEntry(data, handle); } else { byte [] ref_counted_data = store.getData(handle); putRefCount(getRefCount(ref_counted_data) + 1, ref_counted_data); store.store(handle, ref_counted_data); } return handle; } protected abstract byte [] writeBytes(JavaType value); protected abstract JavaType readBytes(byte [] data, int offset); public int compare(byte [] left, byte []right) { return getComparator().compare(left, right); } public final void setHyperGraph(HyperGraph hg) { this.graph = hg; } public final void release(HGPersistentHandle handle) { HGStore store = graph.getStore(); byte [] ref_counted_data = store.getData(handle); int refCnt = getRefCount(ref_counted_data); if (--refCnt > 0) { putRefCount(refCnt, ref_counted_data); store.store(handle, ref_counted_data); } else { store.removeData(handle); getIndex().removeEntry(ref_counted_data, handle); } } public Object make(HGPersistentHandle handle, LazyRef<HGHandle[]> targetSet, IncidenceSetRef incidenceSet) { byte [] asBytes = graph.getStore().getData(handle); if (asBytes == null) throw new HGException("Could not find data for handle: " + handle.toString()); return readBytes(asBytes, dataOffset); } private byte [] objectAsBytes(JavaType instance) { byte data [] = writeBytes(instance); byte full [] = new byte[dataOffset + data.length]; System.arraycopy(data, 0, full, dataOffset, data.length); return full; } public JavaType fromByteArray(byte[] byteArray, int offset, int length) { return readBytes(byteArray, dataOffset + offset); } public byte[] toByteArray(JavaType object) { return objectAsBytes(object); } @SuppressWarnings("unchecked") public HGPersistentHandle store(Object instance) { return storeImpl(objectAsBytes((JavaType)instance)); } public HGSearchResult<HGPersistentHandle> find(JavaType key) { return getIndex().find(objectAsBytes(key)); } public HGSearchResult<HGPersistentHandle> findGT(JavaType key) { return getIndex().findGT(objectAsBytes(key)); } public HGSearchResult<HGPersistentHandle> findGTE(JavaType key) { return getIndex().findGTE(objectAsBytes(key)); } public HGSearchResult<HGPersistentHandle> findLT(JavaType key) { return getIndex().findLT(objectAsBytes(key)); } public HGSearchResult<HGPersistentHandle> findLTE(JavaType key) { return getIndex().findLTE(objectAsBytes(key)); } public boolean subsumes(Object l, Object r) { if (l == null || r == null) return l == null; else return l.equals(r); } public int getRefCountFor(JavaType o) { byte [] B = objectAsBytes(o); HGPersistentHandle handle = (HGPersistentHandle)this.getIndex().findFirst(B); if (handle == null) return 0; byte [] ref_counted_data = graph.getStore().getData(handle); int refCnt = getRefCount(ref_counted_data); return refCnt; } }