Use same logic to remove blank nodes from reasoner's internal model as used in RDFService (#397)
* Refactor blank-nodes-as-variables behavior from RDFServiceJena to JenaModelUtils; use this logic when removing triples from the TBox reasoner's internal assertions model. Add unit test. * Remove complication of datasets and remove blank nodes directly from models * Remove temporary debugging logging * Simplify original blank node tree logic * Remove unneeded imports
This commit is contained in:
parent
3a88c451ab
commit
fb9d86a57d
7 changed files with 326 additions and 251 deletions
|
@ -2,15 +2,17 @@
|
||||||
|
|
||||||
package edu.cornell.mannlib.vitro.webapp.dao.jena;
|
package edu.cornell.mannlib.vitro.webapp.dao.jena;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.jena.graph.Triple;
|
||||||
import org.apache.jena.ontology.Individual;
|
import org.apache.jena.ontology.Individual;
|
||||||
import org.apache.jena.ontology.OntClass;
|
import org.apache.jena.ontology.OntClass;
|
||||||
import org.apache.jena.ontology.OntModel;
|
import org.apache.jena.ontology.OntModel;
|
||||||
|
@ -21,12 +23,16 @@ import org.apache.jena.query.Query;
|
||||||
import org.apache.jena.query.QueryExecution;
|
import org.apache.jena.query.QueryExecution;
|
||||||
import org.apache.jena.query.QueryExecutionFactory;
|
import org.apache.jena.query.QueryExecutionFactory;
|
||||||
import org.apache.jena.query.QueryFactory;
|
import org.apache.jena.query.QueryFactory;
|
||||||
|
import org.apache.jena.query.QuerySolution;
|
||||||
|
import org.apache.jena.query.ResultSet;
|
||||||
import org.apache.jena.rdf.model.Model;
|
import org.apache.jena.rdf.model.Model;
|
||||||
import org.apache.jena.rdf.model.ModelFactory;
|
import org.apache.jena.rdf.model.ModelFactory;
|
||||||
import org.apache.jena.rdf.model.Property;
|
import org.apache.jena.rdf.model.Property;
|
||||||
import org.apache.jena.rdf.model.RDFNode;
|
import org.apache.jena.rdf.model.RDFNode;
|
||||||
import org.apache.jena.rdf.model.Resource;
|
import org.apache.jena.rdf.model.Resource;
|
||||||
import org.apache.jena.rdf.model.ResourceFactory;
|
import org.apache.jena.rdf.model.ResourceFactory;
|
||||||
|
import org.apache.jena.rdf.model.Statement;
|
||||||
|
import org.apache.jena.rdf.model.StmtIterator;
|
||||||
import org.apache.jena.shared.Lock;
|
import org.apache.jena.shared.Lock;
|
||||||
import org.apache.jena.vocabulary.OWL;
|
import org.apache.jena.vocabulary.OWL;
|
||||||
import org.apache.jena.vocabulary.RDF;
|
import org.apache.jena.vocabulary.RDF;
|
||||||
|
@ -39,6 +45,8 @@ import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactoryConfig;
|
||||||
|
|
||||||
public class JenaModelUtils {
|
public class JenaModelUtils {
|
||||||
|
|
||||||
|
public static final String BNODE_ROOT_QUERY =
|
||||||
|
"SELECT DISTINCT ?s WHERE { ?s ?p ?o OPTIONAL { ?ss ?pp ?s } FILTER (!isBlank(?s) || !bound(?ss)) }";
|
||||||
private static final Log log = LogFactory.getLog(JenaModelUtils.class.getName());
|
private static final Log log = LogFactory.getLog(JenaModelUtils.class.getName());
|
||||||
|
|
||||||
private static final Set<String> nonIndividualTypeURIs ;
|
private static final Set<String> nonIndividualTypeURIs ;
|
||||||
|
@ -399,4 +407,189 @@ public class JenaModelUtils {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove statements from a model by separating statements
|
||||||
|
* containing blank nodes from those that have no blank nodes.
|
||||||
|
* The blank node statements are removed by treating blank nodes as variables and
|
||||||
|
* constructing the matching subgraphs for deletion.
|
||||||
|
* The other statements are removed normally.
|
||||||
|
* @param toRemove containing statements to be removed
|
||||||
|
* @param removeFrom from which statements should be removed
|
||||||
|
*/
|
||||||
|
public static void removeWithBlankNodesAsVariables(Model toRemove, Model removeFrom) {
|
||||||
|
List<Statement> blankNodeStatements = new ArrayList<Statement>();
|
||||||
|
List<Statement> nonBlankNodeStatements = new ArrayList<Statement>();
|
||||||
|
StmtIterator stmtIt = toRemove.listStatements();
|
||||||
|
while (stmtIt.hasNext()) {
|
||||||
|
Statement stmt = stmtIt.nextStatement();
|
||||||
|
if (stmt.getSubject().isAnon() || stmt.getObject().isAnon()) {
|
||||||
|
blankNodeStatements.add(stmt);
|
||||||
|
} else {
|
||||||
|
nonBlankNodeStatements.add(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!blankNodeStatements.isEmpty()) {
|
||||||
|
Model blankNodeModel = ModelFactory.createDefaultModel();
|
||||||
|
blankNodeModel.add(blankNodeStatements);
|
||||||
|
removeBlankNodesUsingSparqlConstruct(blankNodeModel, removeFrom);
|
||||||
|
}
|
||||||
|
if(!nonBlankNodeStatements.isEmpty()) {
|
||||||
|
try {
|
||||||
|
removeFrom.enterCriticalSection(Lock.WRITE);
|
||||||
|
removeFrom.remove(nonBlankNodeStatements);
|
||||||
|
} finally {
|
||||||
|
removeFrom.leaveCriticalSection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void removeBlankNodesUsingSparqlConstruct(Model blankNodeModel,
|
||||||
|
Model removeFrom) {
|
||||||
|
log.debug("blank node model size " + blankNodeModel.size());
|
||||||
|
if (blankNodeModel.size() == 1) {
|
||||||
|
log.debug("Deleting single triple with blank node: " + blankNodeModel);
|
||||||
|
log.debug("This could result in the deletion of multiple triples"
|
||||||
|
+ " if multiple blank nodes match the same triple pattern.");
|
||||||
|
}
|
||||||
|
Query rootFinderQuery = QueryFactory.create(BNODE_ROOT_QUERY);
|
||||||
|
QueryExecution qe = QueryExecutionFactory.create(rootFinderQuery, blankNodeModel);
|
||||||
|
try {
|
||||||
|
ResultSet rs = qe.execSelect();
|
||||||
|
while (rs.hasNext()) {
|
||||||
|
QuerySolution qs = rs.next();
|
||||||
|
Resource s = qs.getResource("s");
|
||||||
|
String treeFinder = makeDescribe(s);
|
||||||
|
Query treeFinderQuery = QueryFactory.create(treeFinder);
|
||||||
|
QueryExecution qee = QueryExecutionFactory.create(treeFinderQuery, blankNodeModel);
|
||||||
|
try {
|
||||||
|
Model tree = qee.execDescribe();
|
||||||
|
JenaModelUtils.removeUsingSparqlConstruct(tree, removeFrom);
|
||||||
|
} finally {
|
||||||
|
qee.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
qe.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String makeDescribe(Resource s) {
|
||||||
|
StringBuilder query = new StringBuilder("DESCRIBE <") ;
|
||||||
|
if (s.isAnon()) {
|
||||||
|
query.append("_:").append(s.getId().toString());
|
||||||
|
} else {
|
||||||
|
query.append(s.getURI());
|
||||||
|
}
|
||||||
|
query.append(">");
|
||||||
|
return query.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final boolean WHERE_CLAUSE = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove statements from a model by first constructing
|
||||||
|
* the statements to be removed with a SPARQL query that treats
|
||||||
|
* each blank node ID as a variable.
|
||||||
|
* This allows matching blank node structures to be removed even though
|
||||||
|
* the internal blank node IDs are different.
|
||||||
|
* @param toRemove containing statements to be removed
|
||||||
|
* @param removeFrom from which statements should be removed
|
||||||
|
*/
|
||||||
|
public static void removeUsingSparqlConstruct(Model toRemove, Model removeFrom) {
|
||||||
|
if(toRemove.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Statement> stmts = toRemove.listStatements().toList();
|
||||||
|
stmts = sort(stmts);
|
||||||
|
StringBuffer queryBuff = new StringBuffer();
|
||||||
|
queryBuff.append("CONSTRUCT { \n");
|
||||||
|
addStatementPatterns(stmts, queryBuff, !WHERE_CLAUSE);
|
||||||
|
queryBuff.append("} WHERE { \n");
|
||||||
|
addStatementPatterns(stmts, queryBuff, WHERE_CLAUSE);
|
||||||
|
queryBuff.append("} \n");
|
||||||
|
String queryStr = queryBuff.toString();
|
||||||
|
log.debug(queryBuff.toString());
|
||||||
|
Query construct = QueryFactory.create(queryStr);
|
||||||
|
QueryExecution qe = QueryExecutionFactory.create(construct, removeFrom);
|
||||||
|
try {
|
||||||
|
Model constructedRemovals = qe.execConstruct();
|
||||||
|
try {
|
||||||
|
removeFrom.enterCriticalSection(Lock.WRITE);
|
||||||
|
removeFrom.remove(constructedRemovals);
|
||||||
|
} finally {
|
||||||
|
removeFrom.leaveCriticalSection();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
qe.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Statement> sort(List<Statement> stmts) {
|
||||||
|
List<Statement> output = new ArrayList<Statement>();
|
||||||
|
int originalSize = stmts.size();
|
||||||
|
if(originalSize == 1) {
|
||||||
|
return stmts;
|
||||||
|
}
|
||||||
|
List <Statement> remaining = stmts;
|
||||||
|
ConcurrentLinkedQueue<Resource> subjQueue = new ConcurrentLinkedQueue<Resource>();
|
||||||
|
for(Statement stmt : remaining) {
|
||||||
|
if(stmt.getSubject().isURIResource()) {
|
||||||
|
subjQueue.add(stmt.getSubject());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (subjQueue.isEmpty()) {
|
||||||
|
log.warn("No named subject in statement patterns");
|
||||||
|
return stmts;
|
||||||
|
}
|
||||||
|
while(remaining.size() > 0) {
|
||||||
|
if(subjQueue.isEmpty()) {
|
||||||
|
subjQueue.add(remaining.get(0).getSubject());
|
||||||
|
}
|
||||||
|
while(!subjQueue.isEmpty()) {
|
||||||
|
Resource subj = subjQueue.poll();
|
||||||
|
List<Statement> temp = new ArrayList<Statement>();
|
||||||
|
for (Statement stmt : remaining) {
|
||||||
|
if(stmt.getSubject().equals(subj)) {
|
||||||
|
output.add(stmt);
|
||||||
|
if (stmt.getObject().isResource()) {
|
||||||
|
subjQueue.add((Resource) stmt.getObject());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
temp.add(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remaining = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(output.size() != originalSize) {
|
||||||
|
throw new RuntimeException("original list size was " + originalSize +
|
||||||
|
" but sorted size is " + output.size());
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addStatementPatterns(List<Statement> stmts,
|
||||||
|
StringBuffer patternBuff, boolean whereClause) {
|
||||||
|
for(Statement stmt : stmts) {
|
||||||
|
Triple t = stmt.asTriple();
|
||||||
|
patternBuff.append(SparqlGraph.sparqlNodeDelete(t.getSubject(), null));
|
||||||
|
patternBuff.append(" ");
|
||||||
|
patternBuff.append(SparqlGraph.sparqlNodeDelete(t.getPredicate(), null));
|
||||||
|
patternBuff.append(" ");
|
||||||
|
patternBuff.append(SparqlGraph.sparqlNodeDelete(t.getObject(), null));
|
||||||
|
patternBuff.append(" .\n");
|
||||||
|
if (whereClause) {
|
||||||
|
if (t.getSubject().isBlank()) {
|
||||||
|
patternBuff.append(" FILTER(isBlank(").append(
|
||||||
|
SparqlGraph.sparqlNodeDelete(t.getSubject(), null)).append(")) \n");
|
||||||
|
}
|
||||||
|
if (t.getObject().isBlank()) {
|
||||||
|
patternBuff.append(" FILTER(isBlank(").append(
|
||||||
|
SparqlGraph.sparqlNodeDelete(t.getObject(), null)).append(")) \n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,8 +45,6 @@ import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString;
|
||||||
public abstract class RDFServiceImpl implements RDFService {
|
public abstract class RDFServiceImpl implements RDFService {
|
||||||
|
|
||||||
private static final Log log = LogFactory.getLog(RDFServiceImpl.class);
|
private static final Log log = LogFactory.getLog(RDFServiceImpl.class);
|
||||||
protected static final String BNODE_ROOT_QUERY =
|
|
||||||
"SELECT DISTINCT ?s WHERE { ?s ?p ?o OPTIONAL { ?ss ?pp ?s } FILTER (!isBlank(?s) || !bound(?ss)) }";
|
|
||||||
|
|
||||||
protected String defaultWriteGraphURI;
|
protected String defaultWriteGraphURI;
|
||||||
protected List<ChangeListener> registeredListeners = new CopyOnWriteArrayList<ChangeListener>();
|
protected List<ChangeListener> registeredListeners = new CopyOnWriteArrayList<ChangeListener>();
|
||||||
|
|
|
@ -10,46 +10,40 @@ import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer;
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.apache.jena.query.QuerySolutionMap;
|
|
||||||
import org.apache.jena.query.Syntax;
|
|
||||||
import org.apache.jena.rdf.model.Literal;
|
|
||||||
import org.apache.jena.riot.RDFDataMgr;
|
|
||||||
import org.apache.log4j.lf5.util.StreamUtils;
|
|
||||||
|
|
||||||
import org.apache.jena.graph.Triple;
|
import org.apache.jena.graph.Triple;
|
||||||
import org.apache.jena.query.Dataset;
|
import org.apache.jena.query.Dataset;
|
||||||
import org.apache.jena.query.DatasetFactory;
|
|
||||||
import org.apache.jena.query.Query;
|
import org.apache.jena.query.Query;
|
||||||
import org.apache.jena.query.QueryExecution;
|
import org.apache.jena.query.QueryExecution;
|
||||||
import org.apache.jena.query.QueryExecutionFactory;
|
import org.apache.jena.query.QueryExecutionFactory;
|
||||||
import org.apache.jena.query.QueryFactory;
|
import org.apache.jena.query.QueryFactory;
|
||||||
import org.apache.jena.query.QuerySolution;
|
import org.apache.jena.query.QuerySolution;
|
||||||
|
import org.apache.jena.query.QuerySolutionMap;
|
||||||
import org.apache.jena.query.ResultSet;
|
import org.apache.jena.query.ResultSet;
|
||||||
import org.apache.jena.query.ResultSetFormatter;
|
import org.apache.jena.query.ResultSetFormatter;
|
||||||
|
import org.apache.jena.query.Syntax;
|
||||||
|
import org.apache.jena.rdf.model.Literal;
|
||||||
import org.apache.jena.rdf.model.Model;
|
import org.apache.jena.rdf.model.Model;
|
||||||
import org.apache.jena.rdf.model.ModelFactory;
|
import org.apache.jena.rdf.model.ModelFactory;
|
||||||
import org.apache.jena.rdf.model.RDFNode;
|
import org.apache.jena.rdf.model.RDFNode;
|
||||||
import org.apache.jena.rdf.model.Resource;
|
import org.apache.jena.riot.RDFDataMgr;
|
||||||
import org.apache.jena.rdf.model.Statement;
|
|
||||||
import org.apache.jena.rdf.model.StmtIterator;
|
|
||||||
import org.apache.jena.sdb.SDB;
|
import org.apache.jena.sdb.SDB;
|
||||||
import org.apache.jena.shared.Lock;
|
import org.apache.jena.shared.Lock;
|
||||||
import org.apache.jena.sparql.core.Quad;
|
import org.apache.jena.sparql.core.Quad;
|
||||||
|
import org.apache.log4j.lf5.util.StreamUtils;
|
||||||
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.jena.DatasetWrapper;
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.DatasetWrapper;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.JenaModelUtils;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset;
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.jena.SparqlGraph;
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.ModelChange;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.ModelChange;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceImpl;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceImpl;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString;
|
import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString;
|
||||||
|
@ -91,7 +85,7 @@ public abstract class RDFServiceJena extends RDFServiceImpl implements RDFServic
|
||||||
Model model = (modelChange.getGraphURI() == null) ?
|
Model model = (modelChange.getGraphURI() == null) ?
|
||||||
dataset.getDefaultModel() :
|
dataset.getDefaultModel() :
|
||||||
dataset.getNamedModel(modelChange.getGraphURI());
|
dataset.getNamedModel(modelChange.getGraphURI());
|
||||||
operateOnModel(model, modelChange, dataset);
|
operateOnModel(model, modelChange);
|
||||||
} finally {
|
} finally {
|
||||||
dataset.getLock().leaveCriticalSection();
|
dataset.getLock().leaveCriticalSection();
|
||||||
}
|
}
|
||||||
|
@ -104,7 +98,7 @@ public abstract class RDFServiceJena extends RDFServiceImpl implements RDFServic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void operateOnModel(Model model, ModelChange modelChange, Dataset dataset) {
|
protected void operateOnModel(Model model, ModelChange modelChange) {
|
||||||
model.enterCriticalSection(Lock.WRITE);
|
model.enterCriticalSection(Lock.WRITE);
|
||||||
try {
|
try {
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
|
@ -115,10 +109,7 @@ public abstract class RDFServiceJena extends RDFServiceImpl implements RDFServic
|
||||||
model.add(addition);
|
model.add(addition);
|
||||||
} else if (modelChange.getOperation() == ModelChange.Operation.REMOVE) {
|
} else if (modelChange.getOperation() == ModelChange.Operation.REMOVE) {
|
||||||
Model removal = parseModel(modelChange);
|
Model removal = parseModel(modelChange);
|
||||||
model.remove(removal);
|
JenaModelUtils.removeWithBlankNodesAsVariables(removal, model);
|
||||||
if (dataset != null) {
|
|
||||||
removeBlankNodesWithSparqlUpdate(dataset, removal, modelChange.getGraphURI());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log.error("unrecognized operation type");
|
log.error("unrecognized operation type");
|
||||||
}
|
}
|
||||||
|
@ -179,216 +170,6 @@ public abstract class RDFServiceJena extends RDFServiceImpl implements RDFServic
|
||||||
ToString.modelToString(model)));
|
ToString.modelToString(model)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeBlankNodesWithSparqlUpdate(Dataset dataset, Model model, String graphURI) {
|
|
||||||
|
|
||||||
List<Statement> blankNodeStatements = new ArrayList<Statement>();
|
|
||||||
StmtIterator stmtIt = model.listStatements();
|
|
||||||
while (stmtIt.hasNext()) {
|
|
||||||
Statement stmt = stmtIt.nextStatement();
|
|
||||||
if (stmt.getSubject().isAnon() || stmt.getObject().isAnon()) {
|
|
||||||
blankNodeStatements.add(stmt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(blankNodeStatements.size() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Model blankNodeModel = ModelFactory.createDefaultModel();
|
|
||||||
blankNodeModel.add(blankNodeStatements);
|
|
||||||
|
|
||||||
log.debug("removal model size " + model.size());
|
|
||||||
log.debug("blank node model size " + blankNodeModel.size());
|
|
||||||
|
|
||||||
if (blankNodeModel.size() == 1) {
|
|
||||||
log.debug("Deleting single triple with blank node: " + blankNodeModel);
|
|
||||||
log.debug("This could result in the deletion of multiple triples if multiple blank nodes match the same triple pattern.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Query rootFinderQuery = QueryFactory.create(BNODE_ROOT_QUERY);
|
|
||||||
QueryExecution qe = QueryExecutionFactory.create(rootFinderQuery, blankNodeModel);
|
|
||||||
try {
|
|
||||||
ResultSet rs = qe.execSelect();
|
|
||||||
if (!rs.hasNext()) {
|
|
||||||
log.warn("No rooted blank node trees; deletion is not possible.");
|
|
||||||
}
|
|
||||||
while (rs.hasNext()) {
|
|
||||||
QuerySolution qs = rs.next();
|
|
||||||
Resource s = qs.getResource("s");
|
|
||||||
String treeFinder = makeDescribe(s);
|
|
||||||
Query treeFinderQuery = QueryFactory.create(treeFinder);
|
|
||||||
QueryExecution qee = QueryExecutionFactory.create(treeFinderQuery, blankNodeModel);
|
|
||||||
try {
|
|
||||||
Model tree = qee.execDescribe();
|
|
||||||
Dataset ds = DatasetFactory.createMem();
|
|
||||||
if (graphURI == null) {
|
|
||||||
ds.setDefaultModel(dataset.getDefaultModel());
|
|
||||||
} else {
|
|
||||||
ds.addNamedModel(graphURI, dataset.getNamedModel(graphURI));
|
|
||||||
}
|
|
||||||
if (s.isAnon()) {
|
|
||||||
removeUsingSparqlUpdate(ds, tree, graphURI);
|
|
||||||
} else {
|
|
||||||
StmtIterator sit = tree.listStatements(s, null, (RDFNode) null);
|
|
||||||
while (sit.hasNext()) {
|
|
||||||
Statement stmt = sit.nextStatement();
|
|
||||||
RDFNode n = stmt.getObject();
|
|
||||||
Model m2 = ModelFactory.createDefaultModel();
|
|
||||||
if (n.isResource()) {
|
|
||||||
Resource s2 = (Resource) n;
|
|
||||||
// now run yet another describe query
|
|
||||||
String smallerTree = makeDescribe(s2);
|
|
||||||
log.debug(smallerTree);
|
|
||||||
Query smallerTreeQuery = QueryFactory.create(smallerTree);
|
|
||||||
QueryExecution qe3 = QueryExecutionFactory.create(
|
|
||||||
smallerTreeQuery, tree);
|
|
||||||
try {
|
|
||||||
qe3.execDescribe(m2);
|
|
||||||
} finally {
|
|
||||||
qe3.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m2.add(stmt);
|
|
||||||
removeUsingSparqlUpdate(ds, m2, graphURI);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
qee.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
qe.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String makeDescribe(Resource s) {
|
|
||||||
StringBuilder query = new StringBuilder("DESCRIBE <") ;
|
|
||||||
if (s.isAnon()) {
|
|
||||||
query.append("_:").append(s.getId().toString());
|
|
||||||
} else {
|
|
||||||
query.append(s.getURI());
|
|
||||||
}
|
|
||||||
query.append(">");
|
|
||||||
return query.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeUsingSparqlUpdate(Dataset dataset, Model model, String graphURI) {
|
|
||||||
StmtIterator stmtIt = model.listStatements();
|
|
||||||
|
|
||||||
if (!stmtIt.hasNext()) {
|
|
||||||
stmtIt.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuffer queryBuff = new StringBuffer();
|
|
||||||
queryBuff.append("CONSTRUCT { \n");
|
|
||||||
List<Statement> stmts = stmtIt.toList();
|
|
||||||
stmts = sort(stmts);
|
|
||||||
addStatementPatterns(stmts, queryBuff, !WHERE_CLAUSE);
|
|
||||||
queryBuff.append("} WHERE { \n");
|
|
||||||
if (graphURI != null) {
|
|
||||||
queryBuff.append(" GRAPH <").append(graphURI).append("> { \n");
|
|
||||||
}
|
|
||||||
stmtIt = model.listStatements();
|
|
||||||
stmts = stmtIt.toList();
|
|
||||||
stmts = sort(stmts);
|
|
||||||
addStatementPatterns(stmts, queryBuff, WHERE_CLAUSE);
|
|
||||||
if (graphURI != null) {
|
|
||||||
queryBuff.append(" } \n");
|
|
||||||
}
|
|
||||||
queryBuff.append("} \n");
|
|
||||||
|
|
||||||
log.debug(queryBuff.toString());
|
|
||||||
|
|
||||||
Query construct = QueryFactory.create(queryBuff.toString());
|
|
||||||
// make a plain dataset to force the query to be run in a way that
|
|
||||||
// won't overwhelm MySQL with too many joins
|
|
||||||
Dataset ds = DatasetFactory.createMem();
|
|
||||||
if (graphURI == null) {
|
|
||||||
ds.setDefaultModel(dataset.getDefaultModel());
|
|
||||||
} else {
|
|
||||||
ds.addNamedModel(graphURI, dataset.getNamedModel(graphURI));
|
|
||||||
}
|
|
||||||
QueryExecution qe = QueryExecutionFactory.create(construct, ds);
|
|
||||||
try {
|
|
||||||
Model m = qe.execConstruct();
|
|
||||||
if (graphURI != null) {
|
|
||||||
dataset.getNamedModel(graphURI).remove(m);
|
|
||||||
} else {
|
|
||||||
dataset.getDefaultModel().remove(m);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
qe.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Statement> sort(List<Statement> stmts) {
|
|
||||||
List<Statement> output = new ArrayList<Statement>();
|
|
||||||
int originalSize = stmts.size();
|
|
||||||
if(originalSize == 1) {
|
|
||||||
return stmts;
|
|
||||||
}
|
|
||||||
List <Statement> remaining = stmts;
|
|
||||||
ConcurrentLinkedQueue<Resource> subjQueue = new ConcurrentLinkedQueue<Resource>();
|
|
||||||
for(Statement stmt : remaining) {
|
|
||||||
if(stmt.getSubject().isURIResource()) {
|
|
||||||
subjQueue.add(stmt.getSubject());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (subjQueue.isEmpty()) {
|
|
||||||
log.warn("No named subject in statement patterns");
|
|
||||||
return stmts;
|
|
||||||
}
|
|
||||||
while(remaining.size() > 0) {
|
|
||||||
if(subjQueue.isEmpty()) {
|
|
||||||
subjQueue.add(remaining.get(0).getSubject());
|
|
||||||
}
|
|
||||||
while(!subjQueue.isEmpty()) {
|
|
||||||
Resource subj = subjQueue.poll();
|
|
||||||
List<Statement> temp = new ArrayList<Statement>();
|
|
||||||
for (Statement stmt : remaining) {
|
|
||||||
if(stmt.getSubject().equals(subj)) {
|
|
||||||
output.add(stmt);
|
|
||||||
if (stmt.getObject().isResource()) {
|
|
||||||
subjQueue.add((Resource) stmt.getObject());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
temp.add(stmt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
remaining = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(output.size() != originalSize) {
|
|
||||||
throw new RuntimeException("original list size was " + originalSize +
|
|
||||||
" but sorted size is " + output.size());
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final boolean WHERE_CLAUSE = true;
|
|
||||||
|
|
||||||
private void addStatementPatterns(List<Statement> stmts, StringBuffer patternBuff, boolean whereClause) {
|
|
||||||
for(Statement stmt : stmts) {
|
|
||||||
Triple t = stmt.asTriple();
|
|
||||||
patternBuff.append(SparqlGraph.sparqlNodeDelete(t.getSubject(), null));
|
|
||||||
patternBuff.append(" ");
|
|
||||||
patternBuff.append(SparqlGraph.sparqlNodeDelete(t.getPredicate(), null));
|
|
||||||
patternBuff.append(" ");
|
|
||||||
patternBuff.append(SparqlGraph.sparqlNodeDelete(t.getObject(), null));
|
|
||||||
patternBuff.append(" .\n");
|
|
||||||
if (whereClause) {
|
|
||||||
if (t.getSubject().isBlank()) {
|
|
||||||
patternBuff.append(" FILTER(isBlank(").append(SparqlGraph.sparqlNodeDelete(t.getSubject(), null)).append(")) \n");
|
|
||||||
}
|
|
||||||
if (t.getObject().isBlank()) {
|
|
||||||
patternBuff.append(" FILTER(isBlank(").append(SparqlGraph.sparqlNodeDelete(t.getObject(), null)).append(")) \n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Model parseModel(ModelChange modelChange) {
|
private Model parseModel(ModelChange modelChange) {
|
||||||
Model model = ModelFactory.createDefaultModel();
|
Model model = ModelFactory.createDefaultModel();
|
||||||
model.read(modelChange.getSerializedModel(), null,
|
model.read(modelChange.getSerializedModel(), null,
|
||||||
|
|
|
@ -95,7 +95,7 @@ public class RDFServiceModel extends RDFServiceJena implements RDFService {
|
||||||
m = dataset.getDefaultModel();
|
m = dataset.getDefaultModel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
operateOnModel(m, modelChange, null);
|
operateOnModel(m, modelChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
// notify listeners of triple changes
|
// notify listeners of triple changes
|
||||||
|
|
|
@ -38,8 +38,6 @@ import org.apache.http.message.BasicNameValuePair;
|
||||||
import org.apache.http.protocol.BasicHttpContext;
|
import org.apache.http.protocol.BasicHttpContext;
|
||||||
import org.apache.http.protocol.HttpContext;
|
import org.apache.http.protocol.HttpContext;
|
||||||
import org.apache.http.util.EntityUtils;
|
import org.apache.http.util.EntityUtils;
|
||||||
import org.apache.jena.riot.RDFDataMgr;
|
|
||||||
|
|
||||||
import org.apache.jena.graph.Triple;
|
import org.apache.jena.graph.Triple;
|
||||||
import org.apache.jena.query.Query;
|
import org.apache.jena.query.Query;
|
||||||
import org.apache.jena.query.QueryExecution;
|
import org.apache.jena.query.QueryExecution;
|
||||||
|
@ -55,8 +53,10 @@ import org.apache.jena.rdf.model.RDFNode;
|
||||||
import org.apache.jena.rdf.model.Resource;
|
import org.apache.jena.rdf.model.Resource;
|
||||||
import org.apache.jena.rdf.model.Statement;
|
import org.apache.jena.rdf.model.Statement;
|
||||||
import org.apache.jena.rdf.model.StmtIterator;
|
import org.apache.jena.rdf.model.StmtIterator;
|
||||||
|
import org.apache.jena.riot.RDFDataMgr;
|
||||||
import org.apache.jena.sparql.core.Quad;
|
import org.apache.jena.sparql.core.Quad;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.JenaModelUtils;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset;
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.jena.SparqlGraph;
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.SparqlGraph;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener;
|
||||||
|
@ -691,7 +691,7 @@ public class RDFServiceSparql extends RDFServiceImpl implements RDFService {
|
||||||
log.warn("This likely indicates a problem; excessive data may be deleted.");
|
log.warn("This likely indicates a problem; excessive data may be deleted.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Query rootFinderQuery = QueryFactory.create(BNODE_ROOT_QUERY);
|
Query rootFinderQuery = QueryFactory.create(JenaModelUtils.BNODE_ROOT_QUERY);
|
||||||
QueryExecution qe = QueryExecutionFactory.create(rootFinderQuery, blankNodeModel);
|
QueryExecution qe = QueryExecutionFactory.create(rootFinderQuery, blankNodeModel);
|
||||||
try {
|
try {
|
||||||
ResultSet rs = qe.execSelect();
|
ResultSet rs = qe.execSelect();
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
package edu.cornell.mannlib.vitro.webapp.tboxreasoner.impl.jfact;
|
package edu.cornell.mannlib.vitro.webapp.tboxreasoner.impl.jfact;
|
||||||
|
|
||||||
|
import static org.semanticweb.owlapi.vocab.OWLRDFVocabulary.OWL_AXIOM;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -9,17 +11,6 @@ import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.semanticweb.owlapi.apibinding.OWLManager;
|
|
||||||
import org.semanticweb.owlapi.model.OWLOntology;
|
|
||||||
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
|
|
||||||
import org.semanticweb.owlapi.reasoner.InferenceType;
|
|
||||||
import org.semanticweb.owlapi.reasoner.OWLReasoner;
|
|
||||||
import org.semanticweb.owlapi.reasoner.OWLReasonerConfiguration;
|
|
||||||
import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
|
|
||||||
import org.semanticweb.owlapi.reasoner.SimpleConfiguration;
|
|
||||||
|
|
||||||
import uk.ac.manchester.cs.jfact.JFactFactory;
|
|
||||||
|
|
||||||
import org.apache.jena.ontology.DatatypeProperty;
|
import org.apache.jena.ontology.DatatypeProperty;
|
||||||
import org.apache.jena.ontology.ObjectProperty;
|
import org.apache.jena.ontology.ObjectProperty;
|
||||||
import org.apache.jena.ontology.OntModel;
|
import org.apache.jena.ontology.OntModel;
|
||||||
|
@ -32,13 +23,21 @@ import org.apache.jena.rdf.model.ResourceFactory;
|
||||||
import org.apache.jena.rdf.model.Statement;
|
import org.apache.jena.rdf.model.Statement;
|
||||||
import org.apache.jena.rdf.model.StmtIterator;
|
import org.apache.jena.rdf.model.StmtIterator;
|
||||||
import org.apache.jena.vocabulary.RDF;
|
import org.apache.jena.vocabulary.RDF;
|
||||||
|
import org.semanticweb.owlapi.apibinding.OWLManager;
|
||||||
|
import org.semanticweb.owlapi.model.OWLOntology;
|
||||||
|
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
|
||||||
|
import org.semanticweb.owlapi.reasoner.InferenceType;
|
||||||
|
import org.semanticweb.owlapi.reasoner.OWLReasoner;
|
||||||
|
import org.semanticweb.owlapi.reasoner.OWLReasonerConfiguration;
|
||||||
|
import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
|
||||||
|
import org.semanticweb.owlapi.reasoner.SimpleConfiguration;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.JenaModelUtils;
|
||||||
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerStatementPattern;
|
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerStatementPattern;
|
||||||
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxChanges;
|
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxChanges;
|
||||||
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasoner;
|
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasoner;
|
||||||
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.impl.TBoxInferencesAccumulator;
|
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.impl.TBoxInferencesAccumulator;
|
||||||
|
import uk.ac.manchester.cs.jfact.JFactFactory;
|
||||||
import static org.semanticweb.owlapi.vocab.OWLRDFVocabulary.OWL_AXIOM;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of the JFact reasoner for the TBox.
|
* An implementation of the JFact reasoner for the TBox.
|
||||||
|
@ -81,7 +80,9 @@ public class JFactTBoxReasoner implements
|
||||||
log.debug("Adding " + changes.getAddedStatements().size()
|
log.debug("Adding " + changes.getAddedStatements().size()
|
||||||
+ ", removing " + changes.getRemovedStatements().size());
|
+ ", removing " + changes.getRemovedStatements().size());
|
||||||
filteredAssertionsModel.add(changes.getAddedStatements());
|
filteredAssertionsModel.add(changes.getAddedStatements());
|
||||||
filteredAssertionsModel.remove(changes.getRemovedStatements());
|
Model removals = ModelFactory.createDefaultModel();
|
||||||
|
removals.add(changes.getRemovedStatements());
|
||||||
|
JenaModelUtils.removeWithBlankNodesAsVariables(removals, filteredAssertionsModel);
|
||||||
clearEmptyAxiomStatements();
|
clearEmptyAxiomStatements();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.tboxreasoner.impl.jfact;
|
||||||
|
|
||||||
|
import java.io.StringReader;
|
||||||
|
|
||||||
|
import org.apache.jena.ontology.OntModel;
|
||||||
|
import org.apache.jena.ontology.OntModelSpec;
|
||||||
|
import org.apache.jena.rdf.model.Model;
|
||||||
|
import org.apache.jena.rdf.model.ModelFactory;
|
||||||
|
import org.apache.jena.rdf.model.RDFNode;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.JenaModelUtils;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerConfiguration;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.impl.BasicTBoxReasonerDriver;
|
||||||
|
|
||||||
|
public class JFactTBoxReasonerTest {
|
||||||
|
|
||||||
|
private final static String axioms = "@prefix obo: <http://purl.obolibrary.org/obo/> .\r\n" +
|
||||||
|
"@prefix owl: <http://www.w3.org/2002/07/owl#> .\r\n" +
|
||||||
|
"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\r\n" +
|
||||||
|
"@prefix xml: <http://www.w3.org/XML/1998/namespace> .\r\n" +
|
||||||
|
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\r\n" +
|
||||||
|
"@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"<http://vivo.mydomain.edu/individual/class_c> rdf:type owl:Class ;\r\n" +
|
||||||
|
" \r\n" +
|
||||||
|
" rdfs:subClassOf [ rdf:type owl:Class ;\r\n" +
|
||||||
|
" owl:intersectionOf ( <http://vivo.mydomain.edu/individual/class_a>\r\n" +
|
||||||
|
" <http://vivo.mydomain.edu/individual/class_b>\r\n" +
|
||||||
|
" )\r\n" +
|
||||||
|
" ] .\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"<http://vivo.mydomain.edu/individual/class_a>\r\n" +
|
||||||
|
" a owl:Class ;\r\n" +
|
||||||
|
" rdfs:label \"Class A\"@en-US .\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"<http://vivo.mydomain.edu/individual/class_b>\r\n" +
|
||||||
|
" a owl:Class ;\r\n" +
|
||||||
|
" rdfs:label \"Class B\"@en-US .\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"<http://vivo.mydomain.edu/individual/class_c>\r\n" +
|
||||||
|
" a owl:Class ;\r\n" +
|
||||||
|
" rdfs:label \"Class C\"@en-US .\r\n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that axioms containing blank nodes can be removed from the reasoner
|
||||||
|
* even if the internal blank node IDs are different from when first added.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testRemoveAxiomsWithBlankNodes() {
|
||||||
|
OntModel tboxAssertions = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
|
||||||
|
OntModel tboxInferences = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
|
||||||
|
OntModel tboxUnion = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM,
|
||||||
|
ModelFactory.createUnion(tboxAssertions, tboxInferences));
|
||||||
|
JFactTBoxReasoner reasoner = new JFactTBoxReasoner();
|
||||||
|
BasicTBoxReasonerDriver driver = new BasicTBoxReasonerDriver(
|
||||||
|
tboxAssertions, tboxInferences.getBaseModel(), tboxUnion, reasoner,
|
||||||
|
ReasonerConfiguration.DEFAULT);
|
||||||
|
Model additions = ModelFactory.createDefaultModel();
|
||||||
|
additions.read(new StringReader(axioms), null, "TTL");
|
||||||
|
// Reading again will generate new internal blank node IDs
|
||||||
|
Model subtractions = ModelFactory.createDefaultModel();
|
||||||
|
subtractions.read(new StringReader(axioms), null, "TTL");
|
||||||
|
// Confirm that directly subtracting the models doesn't work because
|
||||||
|
// the blank node IDs do not match
|
||||||
|
Model incorrectSubtraction = additions.difference(subtractions);
|
||||||
|
Assert.assertFalse(incorrectSubtraction.isEmpty());
|
||||||
|
tboxAssertions.getBaseModel().add(additions);
|
||||||
|
tboxAssertions.getBaseModel().notifyEvent(new EditEvent(null, false));
|
||||||
|
waitForTBoxReasoning(driver);
|
||||||
|
// Confirm that union model now contains inferred triples
|
||||||
|
Assert.assertTrue(tboxUnion.size() > additions.size());
|
||||||
|
JenaModelUtils.removeWithBlankNodesAsVariables(subtractions, tboxAssertions.getBaseModel());
|
||||||
|
tboxAssertions.getBaseModel().notifyEvent(new EditEvent(null, false));
|
||||||
|
waitForTBoxReasoning(driver);
|
||||||
|
// Confirm that no statements related to classes a, b or c remain in the
|
||||||
|
// TBox union model. (The inference model may not be completely empty, because
|
||||||
|
// the reasoner may supply unrelated triples related to OWL and RDFS vocabulary.)
|
||||||
|
Assert.assertFalse(tboxUnion.contains(tboxUnion.getResource(
|
||||||
|
"http://vivo.mydomain.edu/individual/class_a"), null, (RDFNode) null));
|
||||||
|
Assert.assertFalse(tboxUnion.contains(tboxUnion.getResource(
|
||||||
|
"http://vivo.mydomain.edu/individual/class_b"), null, (RDFNode) null));
|
||||||
|
Assert.assertFalse(tboxUnion.contains(tboxUnion.getResource(
|
||||||
|
"http://vivo.mydomain.edu/individual/class_c"), null, (RDFNode) null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitForTBoxReasoning(BasicTBoxReasonerDriver driver) {
|
||||||
|
int sleeps = 0;
|
||||||
|
// sleep at least once to make sure the TBox reasoning gets started
|
||||||
|
while ((0 == sleeps) || ((sleeps < 1000) && driver.getStatus().isReasoning())) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(200);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
sleeps++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue