/*
* Copyright (c) 2009, MediaEvent Services GmbH & Co. KG
* http://mediaeventservices.com
*
* This file is part of Marbles.
*
* Marbles is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Marbles is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Marbles. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.fuberlin.wiwiss.marbles;
import info.aduna.iteration.CloseableIteration;
import java.util.Iterator;
import org.openrdf.model.Graph;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.Value;
import org.openrdf.model.impl.GraphImpl;
import org.openrdf.model.vocabulary.OWL;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.sail.Sail;
import org.openrdf.sail.SailConnection;
import org.openrdf.sail.SailConnectionListener;
import org.openrdf.sail.SailException;
import org.openrdf.sail.helpers.SailWrapper;
import org.openrdf.sail.inferencer.InferencerConnection;
import org.openrdf.sail.inferencer.InferencerConnectionWrapper;
/**
* Mirrors statements among URI aliases that are linked by <code>owl:sameAs</code> triples.
*
* In automatic mode (<code>setAutoInference(true)</code>; default), new statements are processed as they are added:
* New statements with owl:sameAs predicates cause statements pertaining to
* the two resources (i.e. subject and object) to be applied to one another.
* New triples with predicates other than owl:sameAs initiate a lookup for URI Aliases, to
* whom the statement will then be applied.
*
* In manual mode (<code>setAutoInference(false)</code>), the method <code>addInferredForResource</code>
* may be used to selectively initiate inferencing for a given resource.
*
* Note: Because the inferencer does not implement handling for the removal of triples,
* inferred statements are not removed.
*
* @author Christian Becker
*/
public class SameAsInferencer extends SailWrapper {
private Graph newStatements;
private InferencerConnection con;
private Resource[] contexts = new Resource[]{};
/**
* Constructor
* @param baseSail
*/
public SameAsInferencer(Sail baseSail) {
super(baseSail);
}
/**
* Limits the generation of inferred statements to specific contexts
*
* @param context List of contexts. An empty list may be passed to permit all contexts.
* @return
*/
public void setContext(Resource ... context) {
this.contexts = context;
}
@Override
public SailConnection getConnection()
throws SailException {
try {
InferencerConnection con = (InferencerConnection)super.getConnection();
this.con = new SameAsInferencerConnection(con);
return this.con;
}
catch (ClassCastException e) {
throw new SailException(e);
}
}
public boolean hasNewStatements() {
return newStatements != null && !newStatements.isEmpty();
}
public boolean doInferencing(InferencerConnection con)
throws SailException {
if (!hasNewStatements()) {
/* There's nothing to do */
return false;
}
Graph iterationStatements = newStatements;
newStatements = new GraphImpl();
Iterator<Statement> iter = iterationStatements.iterator();
while (iter.hasNext()) {
Statement s = iter.next();
if (s.getPredicate().equals(OWL.SAMEAS) && s.getObject() instanceof Resource)
processSameAs(con, s.getSubject(), (Resource) s.getObject());
else
processRegularStatement(con, s);
}
return true;
}
/**
* Manually initiates inferencing for a given resource
*
* @param base The resource for which aliases are to be found
* @throws SailException
* @throws RepositoryException
*/
public void addInferredForResource(Resource base) throws SailException, RepositoryException {
/* Find alias resources */
SailConnection userConn = this.getConnection();
CloseableIteration<? extends Statement, SailException> iterA = userConn.getStatements(base, OWL.SAMEAS, null, false, contexts);
CloseableIteration<? extends Statement, SailException> iterB = userConn.getStatements(null, OWL.SAMEAS, base, false, contexts);
while (iterA.hasNext() || iterB.hasNext()) {
boolean isA = iterA.hasNext();
Statement st = isA ? iterA.next() : iterB.next();
Value obj = isA ? st.getObject() : st.getSubject();
if (obj instanceof Resource) {
processSameAs((InferencerConnection) userConn, base, (Resource) obj);
}
}
userConn.commit();
userConn.close();
}
/**
* Processes an <code>owl:sameAs</code> statement
*
* @param con
* @param base
* @param alias
* @throws SailException
*/
public void processSameAs(InferencerConnection con, Resource base, Resource alias) throws SailException {
/* Add inferred statements for all statements where base or alias are subject */
CloseableIteration<? extends Statement, SailException> iterA = con.getStatements(base, null, null, false /* don't include inferred statements ! */, contexts);
CloseableIteration<? extends Statement, SailException> iterB = con.getStatements(alias, null, null, false /* don't include inferred statements ! */, contexts);
while (iterA.hasNext() || iterB.hasNext()) {
boolean isA = iterA.hasNext();
Statement resSt = isA ? iterA.next() : iterB.next();
/* Add as statements of the base resource */
Resource subject = isA ? alias : base;
/* Prevent self-reference */
if (!(resSt.getPredicate().equals(OWL.SAMEAS) && subject.equals(resSt.getObject())))
con.addInferredStatement(subject, resSt.getPredicate(), resSt.getObject(), resSt.getContext());
else
con.addInferredStatement(subject, OWL.SAMEAS, isA ? base : alias, resSt.getContext()); /* instead, mirror owl:sameAs */
}
/* Add inferred statements for all statements where base or alias are object */
CloseableIteration<? extends Statement, SailException> iterC = con.getStatements(null, null, base, false /* don't include inferred statements ! */, contexts);
CloseableIteration<? extends Statement, SailException> iterD = con.getStatements(null, null, alias, false /* don't include inferred statements ! */, contexts);
while (iterC.hasNext() || iterD.hasNext()) {
boolean isC = iterC.hasNext();
Statement resSt = isC ? iterC.next() : iterD.next();
/* Add as statements of the base resource */
Resource obj = isC ? alias : base;
/* Prevent self-reference */
if (!(resSt.getPredicate().equals(OWL.SAMEAS) && obj.equals(resSt.getSubject())))
con.addInferredStatement(resSt.getSubject(), resSt.getPredicate(), obj, resSt.getContext());
else
con.addInferredStatement(isC ? base : alias, OWL.SAMEAS, obj, resSt.getContext()); /* instead, mirror owl:sameAs */
}
}
/**
* Processes statements other than <code>owl:sameAs</code>
*
* @param con
* @param st
* @throws SailException
*/
public void processRegularStatement(InferencerConnection con, Statement st) throws SailException {
/* Find aliases for the subject */
CloseableIteration<? extends Statement, SailException> iterA = con.getStatements(st.getSubject(), OWL.SAMEAS, null, false /* don't include inferred statements ! */, contexts);
CloseableIteration<? extends Statement, SailException> iterB = con.getStatements(null, OWL.SAMEAS, st.getSubject(), false /* don't include inferred statements ! */, contexts);
while (iterA.hasNext() || iterB.hasNext()) {
boolean isA = iterA.hasNext();
Statement resSt = isA ? iterA.next() : iterB.next();
Value obj = isA ? resSt.getObject() : resSt.getSubject();
/* Add as statements of the base resource */
if (obj instanceof Resource) {
/* Prevent self-reference */
if (!(st.getPredicate().equals(OWL.SAMEAS) && ((Resource)obj).equals(st.getObject())))
con.addInferredStatement((Resource)obj, st.getPredicate(), st.getObject(), st.getContext());
else
con.addInferredStatement((Resource)obj, OWL.SAMEAS, isA ? resSt.getSubject() : resSt.getObject(), st.getContext()); /* instead, mirror owl:sameAs */
}
}
if (!(st.getObject() instanceof Resource))
return;
/* Find aliases for the object */
CloseableIteration<? extends Statement, SailException> iterC = con.getStatements(null, OWL.SAMEAS, st.getObject(), false /* don't include inferred statements ! */, contexts);
CloseableIteration<? extends Statement, SailException> iterD = con.getStatements((Resource) st.getObject(), OWL.SAMEAS, null, false /* don't include inferred statements ! */, contexts);
while (iterC.hasNext() || iterD.hasNext()) {
boolean isC = iterC.hasNext();
Statement resSt = isC ? iterC.next() : iterD.next();
Resource subj = isC ? resSt.getSubject() : (Resource)resSt.getObject();
/* Add as statements of the base resource */
/* Prevent self-reference */
if (!(st.getPredicate().equals(OWL.SAMEAS) && subj.equals(st.getObject())))
con.addInferredStatement(st.getSubject(), st.getPredicate(), subj, st.getContext());
else
con.addInferredStatement(isC ? (Resource)resSt.getObject() : resSt.getSubject(), OWL.SAMEAS, subj, st.getContext()); /* instead, mirror owl:sameAs */
}
}
boolean autoInference = true;
public void setAutoInference(boolean autoInference) {
this.autoInference = autoInference;
}
private class SameAsInferencerConnection extends InferencerConnectionWrapper implements SailConnectionListener {
public SameAsInferencerConnection(InferencerConnection con) {
super(con);
con.addConnectionListener(this);
}
@Override
public void commit() throws SailException {
super.commit();
if (doInferencing(this))
super.commit();
}
public void statementAdded(Statement st) {
if (!autoInference)
return;
if (contexts.length > 0) {
boolean found = false;
for (Resource context : contexts) {
if (st.getContext().equals(context)) {
found = true;
break;
}
}
if (!found)
return;
}
if (newStatements == null) {
newStatements = new GraphImpl();
}
newStatements.add(st);
}
/**
* Not implemented
*/
public void statementRemoved(Statement st) {
}
} // end inner class
}