package org.hypergraphdb.atom;
import java.util.List;
import java.util.concurrent.Callable;
import org.hypergraphdb.HGGraphHolder;
import org.hypergraphdb.HGHandle;
import org.hypergraphdb.HGHandleHolder;
import org.hypergraphdb.HGIndex;
import org.hypergraphdb.HGPersistentHandle;
import org.hypergraphdb.HGRandomAccessResult;
import org.hypergraphdb.HGSearchResult;
import org.hypergraphdb.HyperGraph;
import org.hypergraphdb.HyperNode;
import org.hypergraphdb.IncidenceSet;
import org.hypergraphdb.HGQuery.hg;
import org.hypergraphdb.HGRandomAccessResult.GotoResult;
import org.hypergraphdb.annotation.HGIgnore;
import org.hypergraphdb.query.HGQueryCondition;
import org.hypergraphdb.query.SubgraphMemberCondition;
import org.hypergraphdb.storage.BAtoHandle;
import org.hypergraphdb.util.FilteredSortedSet;
import org.hypergraphdb.util.Mapping;
/**
*
* <p>
* A {@link HyperNode} that encapsulates a set of atoms from the global
* {@link HyperGraph} database. A subgraph can be thought of as a hyper-edge
* in the standard set-theoretic formulation of hypergraphs.
* </p>
*
* @author Borislav Iordanov
*
*/
public class HGSubgraph implements HyperNode, HGHandleHolder, HGGraphHolder
{
private static final String IDX_NAME = "subgraph.index";
private static final String REVIDX_NAME = "revsubgraph.index";
@HGIgnore
HyperGraph graph;
HGHandle thisHandle;
Mapping<HGHandle, Boolean> memberPredicate = new Mapping<HGHandle, Boolean>()
{
public Boolean eval(HGHandle h)
{
return isMember(h);
}
};
private HGQueryCondition localizeCondition(HGQueryCondition condition)
{
return hg.and(new SubgraphMemberCondition(thisHandle), condition);
}
private HGIndex<HGPersistentHandle, HGPersistentHandle> getIndex()
{
return graph.getStore().getIndex(IDX_NAME,
BAtoHandle.getInstance(graph.getHandleFactory()),
BAtoHandle.getInstance(graph.getHandleFactory()),
null,
true);
}
private HGIndex<HGPersistentHandle, HGPersistentHandle> getReverseIndex()
{
return graph.getStore().getIndex(REVIDX_NAME,
BAtoHandle.getInstance(graph.getHandleFactory()),
BAtoHandle.getInstance(graph.getHandleFactory()),
null,
true);
}
/**
* DO NOT USE: internal method, implementation dependent, may disappear at any time.
*/
public static HGIndex<HGPersistentHandle, HGPersistentHandle> getReverseIndex(HyperGraph atGraph)
{
return atGraph.getStore().getIndex(REVIDX_NAME,
BAtoHandle.getInstance(atGraph.getHandleFactory()),
BAtoHandle.getInstance(atGraph.getHandleFactory()),
null,
true);
}
/**
* DO NOT USE: internal method, implementation dependent, may disappear at any time.
*/
public static HGIndex<HGPersistentHandle, HGPersistentHandle> getIndex(HyperGraph atGraph)
{
return atGraph.getStore().getIndex(IDX_NAME,
BAtoHandle.getInstance(atGraph.getHandleFactory()),
BAtoHandle.getInstance(atGraph.getHandleFactory()),
null,
true);
}
private void index(HGHandle h)
{
getIndex().addEntry(thisHandle.getPersistent(), h.getPersistent());
getReverseIndex().addEntry(h.getPersistent(), thisHandle.getPersistent());
}
private void unindex(HGHandle h)
{
getIndex().removeEntry(thisHandle.getPersistent(), h.getPersistent());
getReverseIndex().removeEntry(h.getPersistent(), thisHandle.getPersistent());
}
public boolean isMember(HGHandle atom)
{
// it's quicker to lookup the reverse index because we expect fewer subgraphs than
// atoms in them
HGRandomAccessResult<HGPersistentHandle> rs = getReverseIndex().find(atom.getPersistent());
try
{
return rs.goTo(thisHandle.getPersistent(), true) == GotoResult.found;
}
finally
{
rs.close();
}
}
/**
* <p>
* Add an existing atom to this {@link HyperNode}. The atom may be a member
* multiple nodes at a time.
* </p>
*
* @param atom
* @return The <code>atom</code> parameter.
*/
public HGHandle add(final HGHandle atom)
{
return graph.getTransactionManager().ensureTransaction(new Callable<HGHandle>() {
public HGHandle call()
{
index(atom);
return atom;
}
});
}
/**
* Add to global graph and mark as member of this subgraph.
*/
public HGHandle add(Object atom, HGHandle type, int flags)
{
return add(graph.add(atom, type, flags));
}
public long count(HGQueryCondition condition)
{
return hg.count(graph, localizeCondition(condition));
}
/**
* Define in global graph and mark as member of this subgraph.
*/
public void define(HGHandle handle,
HGHandle type,
Object instance,
int flags)
{
graph.define(handle, type, instance, flags);
add(handle);
}
public <T> HGSearchResult<T> find(HGQueryCondition condition)
{
return graph.find(localizeCondition(condition));
}
@SuppressWarnings("unchecked")
public <T> T findOne(HGQueryCondition condition)
{
return (T)graph.findOne(localizeCondition(condition));
}
@SuppressWarnings("unchecked")
public <T> T getOne(HGQueryCondition condition)
{
return (T)graph.getOne(localizeCondition(condition));
}
public List<HGHandle> findAll(HGQueryCondition condition)
{
return graph.findAll(localizeCondition(condition));
}
public <T> List<T> getAll(HGQueryCondition condition)
{
return graph.getAll(localizeCondition(condition));
}
@SuppressWarnings("unchecked")
public <T> T get(HGHandle handle)
{
return isMember(handle) ? (T)graph.get(handle) : null;
}
/**
* Return incidence set where each element is a member of this <code>HGSubgraph</code>.
* The atom itself whose incidence set is returned doesn't have to be a member of the
* subgraph!
*/
public IncidenceSet getIncidenceSet(HGHandle handle)
{
// maybe should return empty set, instead of null?
// but if the atom is not here, one shouldn't be asking for incidence set
// so null seems more appropriate
return new IncidenceSet(handle,
new FilteredSortedSet<HGHandle>(graph.getIncidenceSet(handle),
memberPredicate));
}
public HGHandle getType(HGHandle handle)
{
return isMember(handle) ? graph.getType(handle) : null;
}
/**
* Removes the atom globally from the database as well as from the nested graph.
* @param handle The atom to remove.
* @return The result of {@link HyperGraph.remove}.
*/
public boolean removeGlobally(HGHandle handle)
{
unindex(handle);
return graph.remove(handle);
}
/**
* Removes the atom globally from the database as well as from the nested graph.
* @param handle The atom to remove.
* @param keepIncidentLinks - whether to also remove links pointing to the removed
* atom. This parameter applies recursively to the links removed.
* @return The result of {@link HyperGraph.remove}.
*/
public boolean removeGlobally(HGHandle handle, boolean keepIncidentLinks)
{
unindex(handle);
return graph.remove(handle, keepIncidentLinks);
}
/**
* Removes an atom from this scope. The atom is not deleted from the
* global {@link HyperGraph} database. If you wish to delete it globally,
* use {@link HyperGraph.remove}.
*
* @return Return value is unreliable
*/
public boolean remove(final HGHandle handle)
{
return graph.getTransactionManager().ensureTransaction(new Callable<Boolean>()
{
public Boolean call()
{
boolean ret = isMember(handle);
unindex(handle);
return ret;
}
});
}
/**
* Performs the replace in the global database as this only deals with
* an atom's value.
*/
public boolean replace(final HGHandle handle, final Object newValue, final HGHandle newType)
{
return graph.getTransactionManager().ensureTransaction(new Callable<Boolean>()
{
public Boolean call()
{
return graph.replace(handle, newValue, newType);
}
});
}
public HGHandle getAtomHandle()
{
return this.thisHandle;
}
public void setAtomHandle(HGHandle handle)
{
this.thisHandle = handle;
}
public void setHyperGraph(HyperGraph graph)
{
this.graph = graph;
}
public HyperGraph getHyperGraph()
{
return this.graph;
}
}