/* * 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; import java.util.Comparator; import org.hypergraphdb.storage.ByteArrayConverter; import org.hypergraphdb.storage.HGStoreImplementation; import org.hypergraphdb.storage.StorageGraph; import org.hypergraphdb.transaction.HGTransactionFactory; import org.hypergraphdb.transaction.HGTransactionManager; /** * <p> * An instance of <code>HGStore</code> is associated with each <code>HyperGraph</code> * to manage to low-level interaction with the underlying database mechanism. * </p> * * <p> * Normally, the HyperGraphDB store is not accessed directly by applications. However, HyperGraphDB * type implementors will rely on the <code>HGStore</code> to manage the way raw data * is stored and indexed based on a particular type. * </p> * * <p>Note that a <code>HGStore</code> does not maintain any data cache, nor does it interact * in any special way with the semantic layer of HyperGraphDB and the way data and types are * laid out in the store.</p> * * <p> * The <code>HGStore</code> will use the {@link HGStorageImplementation} provided by the * {@link HGConfiguration} parameter to the constructor of this class. In fact, this class * is merely a wrapper to the underlying storage implementation. * </p> * * @author Borislav Iordanov */ public class HGStore { private String databaseLocation; private HGConfiguration config; private HGTransactionManager transactionManager = null; private HGStoreImplementation impl = null; private ThreadLocal<StorageGraph> overlayGraph = new ThreadLocal<StorageGraph>(); /** * <p>Construct a <code>HGStore</code> bound to a specific database * location.</p> * * @param database */ public HGStore(String database, HGConfiguration config) { databaseLocation = database; this.config = config; this.impl = config.getStoreImplementation(); impl.startup(this, config); transactionManager = new HGTransactionManager(impl.getTransactionFactory()); if (!config.isTransactional()) transactionManager.disable(); } /** * <p>Create and return a transaction factory for this <code>HGStore</code>.</p> */ public HGTransactionFactory getTransactionFactory() { return impl.getTransactionFactory(); } /** * <p>Return this store's <code>HGTransactionManager</code>.</p> */ public HGTransactionManager getTransactionManager() { return transactionManager; } /** * <p>Return the physical, file system location of the HyperGraph store.</p> */ public String getDatabaseLocation() { return this.databaseLocation; } /** * <p>Create a new link in the HyperGraphDB store. A new <code>HGPersistentHandle</code> * is created to refer to the link.</p> * * @param link A non-null, but possibly empty array of persistent atom handles that * constitute the link to be created. * @return The newly created <code>HGPersistentHandle</code>. */ public HGPersistentHandle store(HGPersistentHandle [] link) { return store(config.getHandleFactory().makeHandle(), link); } /** * <p>Create a new link in the HyperGraphDB store with an existing handle. It is up * to the caller of this method to ensure that the passed in handle is unique.</p> * * @param handle A unique <code>HGPersistentHandle</code> that will refer to the link * within the HyperGraphDB store. * @param link A non-null, but possibly empty array of persistent atom handles that * constitute the link to be created. * @param The <code>handle</code> parameter. */ public HGPersistentHandle store(HGPersistentHandle handle, HGPersistentHandle [] link) { if (overlayGraph.get() != null) return overlayGraph.get().store(handle, link); else return impl.store(handle, link); } /** * <p>Write raw binary data to the store. A new persistent handle is created to * refer to the data.</p> * * @param A non-null, but possibly empty <code>byte[]</code> holding the data to write. * @return The newly created <code>HGPersistentHandle</code> that refers to the recorded * data. */ public HGPersistentHandle store(byte [] data) { HGPersistentHandle handle = config.getHandleFactory().makeHandle(); store(handle, data); return handle; } /** * <p>Write raw binary data to the store using a pre-created, unique persistent handle.</p> * * @param handle A unique <code>HGPersistentHandle</code> to be recorded as the data key. * @param A non-null, but possibly empty <code>byte[]</code> holding the data to write. * @return The <code>handle</code> parameter. */ public HGPersistentHandle store(HGPersistentHandle handle, byte [] data) { if (overlayGraph.get() != null) return overlayGraph.get().store(handle, data); else return impl.store(handle, data); } /** * <p>Remove a link value associated with a <code>HGPersistentHandle</code> key.</p> */ public void removeLink(HGPersistentHandle handle) { impl.removeLink(handle); } /** * <p>Remove a raw data value associated with a <code>HGPersistentHandle</code> key.</p> */ public void removeData(HGPersistentHandle handle) { impl.removeData(handle); } /** * <p>Retrieve an existing link by its handle.</p> * * @param handle The persistent handle of the link. A <code>NullPointerException</code> is * thrown if this parameter is <code>null</code>. * @return An array of handles forming the link or <code>null</code> if there is no * link with that <code>HGPersistentHandle</code> in the database. Note that if the passed * in handle points, the behavior is undefined - the method might throw an exception or return * an array of invalid links. */ public HGPersistentHandle [] getLink(HGPersistentHandle handle) { if (handle == null) throw new NullPointerException("HGStore.getLink called with a null handle."); if (overlayGraph.get() != null) { HGPersistentHandle [] result = null; if ( (result = overlayGraph.get().getLink(handle)) != null) return result; } return impl.getLink(handle); } /** * <p> * Return <code>true</code> if there is a storage link bound to the passed in * handle parameter and <code>false</code> otherwise. * </p> */ public boolean containsLink(HGPersistentHandle handle) { if (handle == null) throw new NullPointerException("HGStore.getLink called with a null handle."); if (overlayGraph.get() != null && overlayGraph.get().getLink(handle) != null) return true; return impl.containsLink(handle); } /** * <p> * Return <code>true</code> if there is a data item bound to the passed in * handle parameter and <code>false</code> otherwise. This may or may not be * any faster then calling the {@link getData} method and checking for null. * It depends no the implementation. * </p> */ public boolean containsData(HGPersistentHandle handle) { if (handle == null) throw new NullPointerException("HGStore.getLink called with a null handle."); if (overlayGraph.get() != null && overlayGraph.get().getLink(handle) != null) return true; return impl.containsData(handle); } /** * <p>Retrieve the raw data buffer stored at <code>handle</code>.</p> * * @param handle The <code>HGPersistentHandle</code> of the data. Cannot * be <code>null</code>. * @return The data pointed to by <code>handle</code> or <code>null</code> * if it could not be found. */ public byte [] getData(HGPersistentHandle handle) { if (handle == null) throw new NullPointerException("HGStore.getData called with a null handle."); if (overlayGraph.get() != null) { byte [] result = null; if ( (result = overlayGraph.get().getData(handle)) != null) return result; } return impl.getData(handle); } /** * <p>Return a <code>HGSearchResult</code> of atom handles in a given atom's incidence * set.</p> * * @param handle The <code>HGPersistentHandle</code> of the atom whose incidence set * is desired. * @return The <code>HGSearchResult</code> iterating over the incidence set. */ public HGRandomAccessResult<HGPersistentHandle> getIncidenceResultSet(HGPersistentHandle handle) { return impl.getIncidenceResultSet(handle); } /** * <p>Return the number of atoms in the incidence set of a given atom. That is, * return the number of links pointing to the atom.</p> */ public long getIncidenceSetCardinality(HGPersistentHandle handle) { return impl.getIncidenceSetCardinality(handle); } /** * <p>Update the incidence set of an atom with a newly created link pointing to it. * This method is only to be used internally by hypergraph. * </p> * * <p> * If the link is already part of the incidence set of this atom, it will not be added * again. * </p> * * @param targetHandle The <code>HGPersistentHandle</code> of the atom whose incidence set * is to be updated. * @param linkHandle The <code>HGPersistentHandle</code> of the new link pointing to that * atom. */ public void addIncidenceLink(HGPersistentHandle targetHandle, HGPersistentHandle linkHandle) { impl.addIncidenceLink(targetHandle, linkHandle); } /** * <p>Update the incidence set of an atom by removing a link that no longer points * to it. This method is only to be used internally by hypergraph. * </p> * * <p> * If the link is not part of the incidence set of this atom, nothing will be done. * </p> * * @param handle The <code>HGPersistentHandle</code> of the atom whose incidence set * is to be updated. * @param oldLink The <code>HGPersistentHandle</code> of the old link that no longer * points to that atom. */ public void removeIncidenceLink(HGPersistentHandle handle, HGPersistentHandle oldLink) { impl.removeIncidenceLink(handle, oldLink); } /** * <p>Remove the whole incidence set of a given handle. This method is * normally used only when an atom is being removed from the hypergraph DB.</p> * * @param handle The handle of the atom whose incidence set must be removed. */ public void removeIncidenceSet(HGPersistentHandle handle) { if (handle == null) throw new NullPointerException("HGStore.removeIncidenceSet called with a null handle."); impl.removeIncidenceSet(handle); } /** * <p> * Retrieve an <code>HGIndex</code> by its name. An index will not * be automatically created if it does not exists. To create an index, * use the <code>createIndex</code> method. * </p> * * @param name The name of the desired index. * @return The <code>HGIndex</code> with the given name or <code>null</code> * if no such index exists. */ public <KeyType, ValueType> HGIndex<KeyType, ValueType> getIndex(String name, ByteArrayConverter<KeyType> keyConverter, ByteArrayConverter<ValueType> valueConverter, Comparator<byte[]> comparator, boolean allowCreate) { return impl.getIndex(name, keyConverter, valueConverter, comparator, false, allowCreate); } /** * <p>Retrieve an existing <code>HGBidirectionalIndex</code> by its name.</p> * * @throws ClassCastException is the index with the specified name is not * bidirectional. */ public <KeyType, ValueType> HGBidirectionalIndex<KeyType, ValueType> getBidirectionalIndex(String name, ByteArrayConverter<KeyType> keyConverter, ByteArrayConverter<ValueType> valueConverter, Comparator<byte[]> comparator, boolean allowCreate) { return (HGBidirectionalIndex<KeyType, ValueType>) impl.getIndex(name, keyConverter, valueConverter, comparator, true, allowCreate); } /** * <p> * Remove an index from the database. Note that all entries in this index will * be lost. * </p> */ public void removeIndex(String name) { impl.removeIndex(name); } /** * <p> * Mainly for internal use - invoked by the main {@link HyperGraph} instance. * </p> */ public void close() { impl.shutdown(); } /** * <p> * Reserved to internal use. * </p> */ public void attachOverlayGraph(StorageGraph sgraph) { overlayGraph.set(sgraph); } /** * <p> * Reserved to internal use. * </p> */ public void detachOverlayGraph() { overlayGraph.set(null); } /** * <p> * Reserved to internal use. * </p> */ public boolean hasOverlayGraph() { return overlayGraph.get() != null; } }