// Portions Copyright (c) 2006 - 2008, Clark & Parsia, LLC. <http://www.clarkparsia.com>
// Clark & Parsia, LLC parts of this source code are available under the terms of the Affero General Public License v3.
//
// Please see LICENSE.txt for full license terms, including the availability of proprietary exceptions.
// Questions, comments, or requests for clarification: licensing@clarkparsia.com
package org.mindswap.pellet.jena;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.mindswap.pellet.KnowledgeBase;
import org.mindswap.pellet.PelletOptions;
import org.mindswap.pellet.utils.iterator.IteratorUtils;
import aterm.ATermAppl;
import com.clarkparsia.pellet.utils.CollectionUtils;
import com.hp.hpl.jena.graph.Graph;
import com.hp.hpl.jena.graph.GraphListener;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.graph.Triple;
import com.hp.hpl.jena.graph.compose.Dyadic;
import com.hp.hpl.jena.graph.compose.MultiUnion;
import com.hp.hpl.jena.graph.compose.Polyadic;
import com.hp.hpl.jena.reasoner.InfGraph;
import com.hp.hpl.jena.vocabulary.RDF;
/**
* A graph listener that listens to graph change events and if possible
* processes the change event. The listener is passed a possibly union
* graph but the listener is attached only to leaf (non-union) graphs.
* The listener keeps track which graph is changed and also checks
* if subgraphs are added or removed from the root graph.
*
* @author Evren Sirin
*/
public class PelletGraphListener implements GraphListener {
// KB object - used for incremental ABox changes
private KnowledgeBase kb;
private Graph rootGraph;
private Set<Graph> leafGraphs;
private Set<Graph> changedGraphs;
private boolean statementDeleted;
private boolean enabled;
public PelletGraphListener(Graph rootGraph, KnowledgeBase kb, boolean enabled) {
this.rootGraph = rootGraph;
this.kb = kb;
this.enabled = enabled;
leafGraphs = CollectionUtils.makeSet();
changedGraphs = CollectionUtils.makeSet();
statementDeleted = false;
if (enabled) {
collectLeafGraphs( rootGraph, Collections.<Graph>emptySet() );
}
}
public void setEnabled(boolean enabled) {
if (this.enabled == enabled) {
return;
}
this.enabled = enabled;
leafGraphs.clear();
changedGraphs.clear();
statementDeleted = false;
if (enabled) {
collectLeafGraphs( rootGraph, Collections.<Graph>emptySet() );
}
else {
for( Graph graph : leafGraphs ) {
graph.getEventManager().unregister( this );
}
}
}
private void addABoxTriple(Triple t) {
// Convert the Jena nodes to ATermAppl
ATermAppl s = JenaUtils.makeATerm( t.getSubject() );
ATermAppl o = JenaUtils.makeATerm( t.getObject() );
// check if this is a type assertion
if( t.getPredicate().equals( RDF.type.asNode() ) ) {
// check if this is a new individual
if( !kb.getIndividuals().contains( s ) )
kb.addIndividual( s );
// add the type
kb.addType( s, o );
}
else {
// check if the subject is a new individual
if( !kb.getIndividuals().contains( s ) )
kb.addIndividual( s );
// check if the object is a new individual
if( !t.getObject().isLiteral() && !kb.getIndividuals().contains( o ) )
kb.addIndividual( o );
ATermAppl p = JenaUtils.makeATerm( t.getPredicate() );
// add the property value
kb.addPropertyValue( p, s, o );
}
}
private void deleteABoxTriple(Triple t) {
ATermAppl s = JenaUtils.makeATerm( t.getSubject() );
ATermAppl o = JenaUtils.makeATerm( t.getObject() );
// check if this is a type assertion
if( t.getPredicate().equals( RDF.type.asNode() ) ) {
if( kb.isIndividual( s ) )
kb.removeType( s, o );
}
else {
// check if the subject is a new individual
if( kb.isIndividual( s ) && kb.isIndividual( o ) ) {
ATermAppl p = JenaUtils.makeATerm( t.getPredicate() );
// add the property value
kb.removePropertyValue( p, s, o );
}
}
}
private void collectLeafGraphs(Graph graph, Set<Graph> prevLeaves) {
if( graph instanceof Polyadic ) {
Polyadic union = ((Polyadic) graph);
if( union.getBaseGraph() != null )
collectLeafGraphs( union.getBaseGraph(), prevLeaves );
for( Iterator<Graph> i = union.getSubGraphs().iterator(); i.hasNext(); )
collectLeafGraphs( i.next(), prevLeaves );
}
else if( graph instanceof Dyadic ) {
Dyadic dyadic = ((Dyadic) graph);
if( dyadic.getL() instanceof Graph )
collectLeafGraphs( (Graph) dyadic.getL(), prevLeaves );
if( dyadic.getR() instanceof Graph )
collectLeafGraphs( (Graph) dyadic.getR(), prevLeaves );
}
else if( graph instanceof InfGraph ) {
collectLeafGraphs( ((InfGraph) graph).getRawGraph(), prevLeaves );
}
else if( leafGraphs.add( graph ) && !prevLeaves.contains( graph ) ) {
changedGraphs.add( graph );
graph.getEventManager().register( this );
}
}
/**
* Checks if the graph can be u[dated incrementally
*
* @return
*/
private boolean canUpdateIncrementally(Graph g) {
return PelletOptions.PROCESS_JENA_UPDATES_INCREMENTALLY && !statementDeleted && !changedGraphs.contains( g );
}
public void dispose() {
for( Graph graph : leafGraphs ) {
graph.getEventManager().unregister( this );
}
leafGraphs.clear();
changedGraphs.clear();
statementDeleted = false;
}
public boolean isChanged() {
if( statementDeleted || !changedGraphs.isEmpty() ) {
return true;
}
getChangedGraphs();
return statementDeleted || !changedGraphs.isEmpty();
}
public Set<Graph> getChangedGraphs() {
Set<Graph> prevLeaves = leafGraphs;
leafGraphs = CollectionUtils.makeSet();
collectLeafGraphs( rootGraph, prevLeaves );
for( Graph prevLeaf : prevLeaves ) {
if( !leafGraphs.contains( prevLeaf ) ) {
statementDeleted = true;
prevLeaf.getEventManager().unregister( this );
}
}
if( statementDeleted ) {
return null;
}
return changedGraphs;
}
/**
* @return
*/
public Set<Graph> getLeafGraphs() {
return leafGraphs;
}
/**
* Checks if the given triple is an ABox assertion. Currently, only type
* assertions with atomic concepts are detected and property assertions
*
* @param t
* @return
*/
private boolean isABoxChange(Triple t) {
Node o = t.getObject();
Node p = t.getPredicate();
// detect if this is a supported ABox type assertion
if( p.equals( RDF.type.asNode() ) ) {
// check if the object is a bnode to detect complex concepts
if( o.isBlank() ) {
return false;
}
// check that the object is an atomic concept that exists in the KB
ATermAppl object = JenaUtils.makeATerm( o );
if( !kb.isClass( object ) ) {
return false;
}
// Note: we do not check if the subject already exists,
// as it could be a newly added individual
}
else {
// detect ABox property assertions
ATermAppl prop = JenaUtils.makeATerm( p );
// check if the role is this is a defined role
if( !kb.isProperty( prop ) ) {
return false;
}
// Note: we do not check if the subject and object already exists,
// as they
// could be a newly added individuals
}
return true;
}
public void notifyAddArray(Graph g, Triple[] triples) {
notifyAddIterator( g, IteratorUtils.iterator( triples ) );
}
public void notifyAddGraph(Graph g, Graph added) {
notifyAddIterator( g, added.find( Triple.ANY ) );
}
public void notifyAddIterator(Graph g, Iterator<Triple> it) {
boolean canUpdateIncrementally = canUpdateIncrementally( g );
if( canUpdateIncrementally ) {
while( it.hasNext() ) {
Triple t = it.next();
if( !isABoxChange( t ) ) {
canUpdateIncrementally = false;
break;
}
addABoxTriple( t );
}
}
if( !canUpdateIncrementally ) {
changedGraphs.add( g );
}
}
public void notifyAddList(Graph g, List<Triple> triples) {
notifyAddIterator( g, triples.iterator() );
}
public void notifyAddTriple(Graph g, Triple t) {
if( canUpdateIncrementally( g ) && isABoxChange( t ) ) {
addABoxTriple( t );
}
else {
changedGraphs.add( g );
}
}
public void notifyDeleteArray(Graph g, Triple[] triples) {
notifyDeleteIterator( g, IteratorUtils.iterator( triples ) );
}
public void notifyDeleteGraph(Graph g, Graph removed) {
notifyDeleteIterator( g, removed.find( Triple.ANY ) );
}
public void notifyDeleteIterator(Graph g, Iterator<Triple> it) {
boolean canUpdateIncrementally = canUpdateIncrementally( g );
if( canUpdateIncrementally ) {
while( it.hasNext() ) {
Triple t = it.next();
if( !isABoxChange( t ) ) {
canUpdateIncrementally = false;
break;
}
deleteABoxTriple( t );
}
}
if( !canUpdateIncrementally ) {
statementDeleted = true;
changedGraphs.add( g );
}
}
public void notifyDeleteList(Graph g, List<Triple> list) {
notifyDeleteIterator( g, list.iterator() );
}
public void notifyDeleteTriple(Graph g, Triple t) {
if( canUpdateIncrementally( g ) && isABoxChange( t ) ) {
deleteABoxTriple( t );
}
else {
statementDeleted = true;
changedGraphs.add( g );
}
}
public void notifyEvent(Graph source, Object value) {
statementDeleted = true;
}
public void reset() {
changedGraphs.clear();
//leafGraphs.clear();
statementDeleted = false;
}
}