/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package org.fcrepo.server.resourceIndex;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jrdf.graph.GraphElementFactory;
import org.jrdf.graph.GraphElementFactoryException;
import org.jrdf.graph.Literal;
import org.jrdf.graph.Node;
import org.jrdf.graph.ObjectNode;
import org.jrdf.graph.PredicateNode;
import org.jrdf.graph.SubjectNode;
import org.jrdf.graph.Triple;
import org.jrdf.graph.URIReference;
import org.trippi.FlushErrorHandler;
import org.trippi.RDFFormat;
import org.trippi.TripleIterator;
import org.trippi.TripleUpdate;
import org.trippi.TriplestoreConnector;
import org.trippi.TriplestoreWriter;
import org.trippi.TrippiException;
import org.trippi.TupleIterator;
import org.fcrepo.server.errors.ResourceIndexException;
import org.fcrepo.server.storage.DOReader;
/**
* Implementation of the <code>ResourceIndex</code>.
*
* @author Chris Wilper
*/
public class ResourceIndexImpl
implements ResourceIndex {
/** Interface to the underlying triplestore. */
private final TriplestoreConnector _connector;
/** Writer for the underlying triplestore. */
private final TriplestoreWriter _writer;
/** The TripleGenerator this instance will use. */
private final TripleGenerator _generator;
/** The current index level. */
private final int _indexLevel;
/**
* Whether triples should be flushed to storage before returning from each
* object modification method.
*/
private final boolean _syncUpdates;
////////////////////
// Initialization //
////////////////////
public ResourceIndexImpl(TriplestoreConnector connector,
TripleGenerator generator,
int indexLevel,
boolean syncUpdates) {
_connector = connector;
_writer = _connector.getWriter();
_generator = generator;
_indexLevel = indexLevel;
_syncUpdates = syncUpdates;
}
///////////////////////////
// ResourceIndex methods //
///////////////////////////
/**
* {@inheritDoc}
*/
public int getIndexLevel() {
return _indexLevel;
}
/**
* {@inheritDoc}
*/
public boolean getSync() {
return _syncUpdates;
}
/**
* {@inheritDoc}
*/
public void addObject(DOReader reader) throws ResourceIndexException {
if (_indexLevel > INDEX_LEVEL_OFF) {
updateTriples(_generator.getTriplesForObject(reader), false);
}
}
/**
* {@inheritDoc}
*/
public void modifyObject(DOReader oldReader, DOReader newReader)
throws ResourceIndexException {
if (_indexLevel > INDEX_LEVEL_OFF) {
updateTripleDiffs(_generator.getTriplesForObject(oldReader),
_generator.getTriplesForObject(newReader));
}
}
/**
* {@inheritDoc}
*/
public void deleteObject(DOReader oldReader) throws ResourceIndexException {
if (_indexLevel > INDEX_LEVEL_OFF) {
updateTriples(_generator.getTriplesForObject(oldReader), true);
}
}
/**
* {@inheritDoc}
*/
public List<Triple> exportObject(DOReader object) throws ResourceIndexException {
Set<Triple> triples = _generator.getTriplesForObject(object);
ArrayList<Triple> list = new ArrayList<Triple>(triples.size());
for (Triple triple:triples) {
list.add(getLocalizedTriple(triple));
}
return list;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("deprecation")
public void export(OutputStream out, RDFFormat format)
throws ResourceIndexException {
try {
TripleIterator it = _writer.findTriples(null, null, null, 0);
it.setAliasMap(_writer.getAliasMap());
it.toStream(out, format);
} catch (TrippiException e) {
throw new ResourceIndexException("Unable to export RI", e);
}
}
/////////////////////
// Private Methods //
/////////////////////
/**
* Applies the given adds or deletes to the triplestore. If _syncUpdates is
* true, changes will be flushed before returning.
*/
private void updateTriples(Set<Triple> set, boolean delete)
throws ResourceIndexException {
try {
if (delete) {
_writer.delete(getTripleIterator(set), _syncUpdates);
} else {
_writer.add(getTripleIterator(set), _syncUpdates);
}
} catch (Exception e) {
throw new ResourceIndexException("Error updating triples", e);
}
}
/**
* Computes the difference between the given sets and applies the
* appropriate deletes and adds to the triplestore. If _syncUpdates is true,
* changes will be flushed before returning.
*/
private void updateTripleDiffs(Set<Triple> existing, Set<Triple> desired)
throws ResourceIndexException {
// Delete any existing triples that are no longer desired,
// leaving the ones we want in place
HashSet<Triple> obsoleteTriples = new HashSet<Triple>(existing);
obsoleteTriples.removeAll(desired);
updateTriples(obsoleteTriples, true);
// Add only new desired triples
HashSet<Triple> newTriples = new HashSet<Triple>(desired);
newTriples.removeAll(existing);
updateTriples(newTriples, false);
}
/**
* Gets a Trippi TripleIterator for the given set.
*/
private TripleIterator getTripleIterator(final Set<Triple> set) {
return new TripleIterator() {
private final Iterator<Triple> _iter = set.iterator();
@Override
public boolean hasNext() {
return _iter.hasNext();
}
@Override
public Triple next() {
return getLocalizedTriple(_iter.next());
}
@Override
public void close() {
}
};
}
/**
* Gets a Triple appropriate for writing to the store.
*/
private Triple getLocalizedTriple(Triple triple) {
try {
return _connector.getElementFactory().createTriple(
getLocalizedResource(triple.getSubject()),
getLocalizedResource(triple.getPredicate()),
getLocalizedObject(triple.getObject()));
} catch (GraphElementFactoryException e) {
throw new RuntimeException("Error localizing triple", e);
}
}
/**
* Gets a localized URIReference based on the given Node.
*/
private URIReference getLocalizedResource(Node n)
throws GraphElementFactoryException {
if (n instanceof URIReference) {
URIReference u = (URIReference) n;
return _connector.getElementFactory().createResource(u.getURI());
} else {
throw new RuntimeException("Error localizing triple; "
+ n.getClass().getName() + " is not a URIReference");
}
}
/**
* Gets a localized URIReference or Literal based on the given Node.
*/
private ObjectNode getLocalizedObject(Node n)
throws GraphElementFactoryException {
if (n instanceof URIReference) {
return getLocalizedResource(n);
} else if (n instanceof Literal) {
Literal l = (Literal) n;
GraphElementFactory elementFactory = _connector.getElementFactory();
if (l.getDatatypeURI() != null) {
return elementFactory.createLiteral(l.getLexicalForm(),
l.getDatatypeURI());
} else if (l.getLanguage() != null) {
return elementFactory.createLiteral(l.getLexicalForm(),
l.getLanguage());
} else {
return elementFactory.createLiteral(l.getLexicalForm());
}
} else {
throw new RuntimeException("Error localizing triple; "
+ n.getClass().getName() + " is not a URIReference "
+ "or a Literal");
}
}
///////////////////////////////
// TriplestoreReader methods //
///////////////////////////////
/**
* {@inheritDoc}
*/
public void setAliasMap(Map<String, String> aliasToPrefix)
throws TrippiException {
_writer.setAliasMap(aliasToPrefix);
}
/**
* {@inheritDoc}
*/
public Map<String, String> getAliasMap() throws TrippiException {
return _writer.getAliasMap();
}
/**
* {@inheritDoc}
*/
public TupleIterator findTuples(String queryLang,
String tupleQuery,
int limit,
boolean distinct) throws TrippiException {
return _writer.findTuples(queryLang, tupleQuery, limit, distinct);
}
/**
* {@inheritDoc}
*/
public int countTuples(String queryLang,
String tupleQuery,
int limit,
boolean distinct) throws TrippiException {
return _writer.countTuples(queryLang, tupleQuery, limit, distinct);
}
/**
* {@inheritDoc}
*/
public TripleIterator findTriples(String queryLang,
String tripleQuery,
int limit,
boolean distinct) throws TrippiException {
return _writer.findTriples(queryLang, tripleQuery, limit, distinct);
}
/**
* {@inheritDoc}
*/
public int countTriples(String queryLang,
String tripleQuery,
int limit,
boolean distinct) throws TrippiException {
return _writer.countTriples(queryLang, tripleQuery, limit, distinct);
}
/**
* {@inheritDoc}
*/
public TripleIterator findTriples(SubjectNode subject,
PredicateNode predicate,
ObjectNode object,
int limit) throws TrippiException {
return _writer.findTriples(subject, predicate, object, limit);
}
/**
* {@inheritDoc}
*/
public int countTriples(SubjectNode subject,
PredicateNode predicate,
ObjectNode object,
int limit) throws TrippiException {
return _writer.countTriples(subject, predicate, object, limit);
}
/**
* {@inheritDoc}
*/
public TripleIterator findTriples(String queryLang,
String tupleQuery,
String tripleTemplate,
int limit,
boolean distinct) throws TrippiException {
return _writer.findTriples(queryLang,
tupleQuery,
tripleTemplate,
limit,
distinct);
}
/**
* {@inheritDoc}
*/
public int countTriples(String queryLang,
String tupleQuery,
String tripleTemplate,
int limit,
boolean distinct) throws TrippiException {
return _writer.countTriples(queryLang,
tupleQuery,
tripleTemplate,
limit,
distinct);
}
/**
* {@inheritDoc}
*/
public String[] listTupleLanguages() {
return _writer.listTupleLanguages();
}
/**
* {@inheritDoc}
*/
public String[] listTripleLanguages() {
return _writer.listTripleLanguages();
}
/**
* {@inheritDoc}
*/
public void close() throws TrippiException {
_connector.close();
}
///////////////////////////////
// TriplestoreWriter methods //
///////////////////////////////
/**
* {@inheritDoc}
*/
public void add(List<Triple> triples, boolean flush) throws IOException,
TrippiException {
_writer.add(triples, flush);
}
/**
* {@inheritDoc}
*/
public void add(TripleIterator triples, boolean flush) throws IOException,
TrippiException {
_writer.add(triples, flush);
}
/**
* {@inheritDoc}
*/
public void add(Triple triple, boolean flush) throws IOException,
TrippiException {
_writer.add(triple, flush);
}
/**
* {@inheritDoc}
*/
public void delete(List<Triple> triples, boolean flush) throws IOException,
TrippiException {
_writer.delete(triples, flush);
}
/**
* {@inheritDoc}
*/
public void delete(TripleIterator triples, boolean flush)
throws IOException, TrippiException {
_writer.delete(triples, flush);
}
/**
* {@inheritDoc}
*/
public void delete(Triple triple, boolean flush) throws IOException,
TrippiException {
_writer.delete(triple, flush);
}
/**
* {@inheritDoc}
*/
public void flushBuffer() throws IOException, TrippiException {
_writer.flushBuffer();
}
/**
* {@inheritDoc}
*/
public void setFlushErrorHandler(FlushErrorHandler h) {
_writer.setFlushErrorHandler(h);
}
/**
* {@inheritDoc}
*/
public int getBufferSize() {
return _writer.getBufferSize();
}
/**
* {@inheritDoc}
*/
public List<TripleUpdate> findBufferedUpdates(SubjectNode subject,
PredicateNode predicate,
ObjectNode object,
int updateType) {
return _writer.findBufferedUpdates(subject,
predicate,
object,
updateType);
}
}