/*
* (c) Copyright 2009 Talis Information Ltd.
* All rights reserved.
* [See end of file]
*/
package dev;
import java.util.ArrayList ;
import java.util.HashMap ;
import java.util.List ;
import java.util.Map ;
import org.openjena.atlas.lib.Sink ;
import com.hp.hpl.jena.graph.Node ;
import com.hp.hpl.jena.graph.Triple ;
import com.hp.hpl.jena.query.Query ;
import com.hp.hpl.jena.query.QueryExecution ;
import com.hp.hpl.jena.query.QueryExecutionFactory ;
import com.hp.hpl.jena.query.QueryFactory ;
import com.hp.hpl.jena.query.QuerySolution ;
import com.hp.hpl.jena.query.ResultSet ;
import com.hp.hpl.jena.query.Syntax ;
import com.hp.hpl.jena.rdf.model.Model ;
import com.hp.hpl.jena.sparql.util.StrUtils ;
import com.hp.hpl.jena.vocabulary.RDF ;
/** Apply a fixed set of inference rules to a stream of triples.
* This is inference on the A-Box (the data) with respect to a fixed T-Box
* (the vocabulary, ontology).
*
* - rdfs:subClassOf (transitive)
* - rdfs:subPropertyOf (transitive)
* - rdfs:domain
* - rdfs:range
*
*/
public class InferenceExpander implements Sink
{
// Calculates hierarchies (subclass, subproperty) from a model.
// Assumes that model has no metavocabulary (use an inferencer on the model first if necessary).
// Todo:
// rdfs:member
// list:member ???
// Expanded hierarchy:
// If C < C1 < C2 then C2 is in the list for C
private final Sink output ;
private final Map> transClasses ;
private final Map> transProperties;
private final Map> domainList ;
private final Map> rangeList ;
static final Node rdfType = RDF.type.asNode() ;
public InferenceExpander(Sink output, Model vocab)
{
this.output = output ;
transClasses = new HashMap>() ;
transProperties = new HashMap>() ;
domainList = new HashMap>() ;
rangeList = new HashMap>() ;
// Find classes - uses property paths
exec("SELECT ?x ?y { ?x rdfs:subClassOf+ ?y }", vocab, transClasses) ;
// Find properties
exec("SELECT ?x ?y { ?x rdfs:subPropertyOf+ ?y }", vocab, transProperties) ;
// Find domain
exec("SELECT ?x ?y { ?x rdfs:domain ?y }", vocab, domainList) ;
// Find range
exec("SELECT ?x ?y { ?x rdfs:range ?y }", vocab, rangeList) ;
}
private static void exec(String qs, Model model, Map> multimap)
{
String preamble = StrUtils.strjoinNL("PREFIX rdf: ",
"PREFIX rdfs: ",
"PREFIX xsd: ",
"PREFIX owl: ",
"PREFIX skos: ") ;
Query query = QueryFactory.create(preamble+"\n"+qs, Syntax.syntaxARQ) ;
QueryExecution qexec = QueryExecutionFactory.create(query, model) ;
ResultSet rs = qexec.execSelect() ;
for ( ; rs.hasNext() ; )
{
QuerySolution soln= rs.next() ;
Node x = soln.get("x").asNode() ;
Node y = soln.get("y").asNode() ;
if ( ! multimap.containsKey(x) )
multimap.put(x, new ArrayList()) ;
multimap.get(x).add(y) ;
}
}
public void send(Triple triple)
{
output.send(triple) ;
Node s = triple.getSubject() ;
Node p = triple.getPredicate() ;
Node o = triple.getObject() ;
subClass(s,p,o) ;
subProperty(s,p,o) ;
domain(s,p,o) ;
// Beware of literal subjects.
range(s,p,o) ;
}
/*
[rdfs8: (?a rdfs:subClassOf ?b), (?b rdfs:subClassOf ?c) -> (?a rdfs:subClassOf ?c)]
[rdfs9: (?x rdfs:subClassOf ?y), (?a rdf:type ?x) -> (?a rdf:type ?y)]
*/
final private void subClass(Node s, Node p, Node o)
{
if ( p.equals(rdfType) )
{
List x = transClasses.get(o) ;
if ( x != null )
for ( Node c : x )
output.send(new Triple(s,p,c)) ;
}
}
// Rule extracts from Jena's RDFS rules etc/rdfs.rules
/*
[rdfs5a: (?a rdfs:subPropertyOf ?b), (?b rdfs:subPropertyOf ?c) -> (?a rdfs:subPropertyOf ?c)]
[rdfs6: (?a ?p ?b), (?p rdfs:subPropertyOf ?q) -> (?a ?q ?b)]
*/
private void subProperty(Node s, Node p, Node o)
{
List x = transProperties.get(p) ;
if ( x != null )
{
for ( Node p2 : x )
output.send(new Triple(s,p2,o)) ;
}
}
/*
[rdfs2: (?p rdfs:domain ?c) -> [(?x rdf:type ?c) <- (?x ?p ?y)] ]
*/
final private void domain(Node s, Node p, Node o)
{
List x = domainList.get(p) ;
if ( x != null )
{
for ( Node c : x )
{
output.send(new Triple(s,rdfType,c)) ;
subClass(s, rdfType, c) ;
}
}
}
/*
[rdfs3: (?p rdfs:range ?c) -> [(?y rdf:type ?c) <- (?x ?p ?y)] ]
*/
final private void range(Node s, Node p, Node o)
{
// Mask out literal subjects
if ( o.isLiteral() )
return ;
// Range
List x = rangeList.get(p) ;
if ( x != null )
{
for ( Node c : x )
{
output.send(new Triple(o,rdfType,c)) ;
subClass(o, rdfType, c) ;
}
}
}
public void flush()
{ output.flush(); }
public void close()
{ output.close(); }
}
/*
* (c) Copyright 2009 Talis Information Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/