blob: 7ddf0c695b865819059b3b27e2fb28aed8dd7c09 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jena.reasoner.rulesys;
import java.util.*;
import org.apache.jena.graph.* ;
import org.apache.jena.rdf.model.* ;
import org.apache.jena.reasoner.* ;
import org.apache.jena.reasoner.rulesys.impl.RDFSCMPPreprocessHook ;
import org.apache.jena.vocabulary.ReasonerVocabulary ;
/**
* A full implementation of RDFS reasoning using a hybrid rule system, together
* with optimized subclass/subproperty closure using the transitive graph caches.
* Implements the container membership property rules using an optional
* data scanning hook. Implements datatype range validation.
*/
public class RDFSRuleReasoner extends GenericRuleReasoner {
/** Constant: used to indicate default RDFS processing level */
public static final String DEFAULT_RULES = "default";
/** Constant: used to indicate full RDFS processing level */
public static final String FULL_RULES = "full";
/** Constant: used to indicate minimal RDFS processing level */
public static final String SIMPLE_RULES = "simple";
/** The location of the default RDFS rule definitions on the class path */
protected static final String RULE_FILE = "etc/rdfs-fb-tgc-noresource.rules";
/** The location of the full RDFS rule definitions on the class path */
protected static final String FULL_RULE_FILE = "etc/rdfs-fb-tgc.rules";
/** The location of the simple RDFS rule definitions on the class path */
protected static final String SIMPLE_RULE_FILE = "etc/rdfs-fb-tgc-simple.rules";
/** The cached rule sets, indexed by processing level */
protected static Map<String, List<Rule>> ruleSets = new HashMap<>();
/** The rule file names, indexed by processing level */
protected static Map<String, String> ruleFiles = Map.of
(DEFAULT_RULES, RULE_FILE,
FULL_RULES, FULL_RULE_FILE,
SIMPLE_RULES, SIMPLE_RULE_FILE);
/** The (stateless) preprocessor for container membership properties */
protected static RulePreprocessHook cmpProcessor = new RDFSCMPPreprocessHook();
/**
* Constructor
*/
public RDFSRuleReasoner( ReasonerFactory parent ) {
super(loadRulesLevel(DEFAULT_RULES), parent);
setMode(HYBRID);
setTransitiveClosureCaching(true);
//addPreprocessingHook(new RDFSCMPPreprocessHook());
}
/**
* Constructor
* @param factory the parent reasoner factory which is consulted to answer capability questions
* @param configuration RDF information to configure the rule set and mode, can be null
*/
public RDFSRuleReasoner(ReasonerFactory factory, Resource configuration) {
this(factory);
if (configuration != null) {
StmtIterator i = configuration.listProperties();
while (i.hasNext()) {
Statement st = i.nextStatement();
doSetParameter(st.getPredicate(), st.getObject());
}
}
}
/**
* Internal constructor, used to generated a partial binding of a schema
* to a rule reasoner instance.
*/
protected RDFSRuleReasoner(FBRuleInfGraph schemaGraph, ReasonerFactory factory) {
super(schemaGraph.getRules(), factory);
this.schemaGraph = schemaGraph;
}
/**
* Internal version of setParameter that does not directly raise an
* exception on parameters it does not recognize.
* @return false if the parameter was not recognized
*/
@Override
protected boolean doSetParameter(Property parameter, Object value) {
if ( value instanceof Literal )
// Without risk of quoting
value = ((Literal)value).getString();
if (parameter.equals(ReasonerVocabulary.PROPenableCMPScan)) {
boolean scanProperties = Util.convertBooleanPredicateArg(parameter, value);
if (scanProperties) {
addPreprocessingHook(cmpProcessor);
} else {
removePreprocessingHook(cmpProcessor);
}
return true;
} else if (parameter.equals(ReasonerVocabulary.PROPsetRDFSLevel)) {
String level = ((String)value).toLowerCase();
setRules(loadRulesLevel(level));
if (level.equals(FULL_RULES)) {
addPreprocessingHook(cmpProcessor);
} else {
removePreprocessingHook(cmpProcessor);
}
return true;
} else {
return super.doSetParameter(parameter, value);
}
}
/**
* Attach the reasoner to a set of RDF data to process.
* The reasoner may already have been bound to specific rules or ontology
* axioms (encoded in RDF) through earlier bindRuleset calls.
*
* @param data the RDF data to be processed, some reasoners may restrict
* the range of RDF which is legal here (e.g. syntactic restrictions in OWL).
* @return an inference graph through which the data+reasoner can be queried.
* @throws ReasonerException if the data is ill-formed according to the
* constraints imposed by this reasoner.
*/
@Override
public InfGraph bind(Graph data) throws ReasonerException {
Graph schemaArg = schemaGraph == null ? getPreload() : schemaGraph;
InfGraph graph = null;
List<Rule> ruleSet = ((FBRuleInfGraph)schemaArg).getRules();
FBRuleInfGraph fbgraph = new RDFSRuleInfGraph(this, ruleSet, schemaArg);
graph = fbgraph;
if (enableTGCCaching) fbgraph.setUseTGCCache();
fbgraph.setTraceOn(traceOn);
if (preprocessorHooks!= null) {
for (RulePreprocessHook rulePreprocessHook : preprocessorHooks)
{
fbgraph.addPreprocessingHook(rulePreprocessHook);
}
}
graph.setDerivationLogging(recordDerivations);
graph.rebind(data);
return graph;
}
/**
* Precompute the implications of a schema graph. The statements in the graph
* will be combined with the data when the final InfGraph is created.
*/
@Override
public Reasoner bindSchema(Graph tbox) throws ReasonerException {
if (schemaGraph != null) {
throw new ReasonerException("Can only bind one schema at a time to an RDFSRuleReasoner");
}
FBRuleInfGraph graph = new FBRuleInfGraph(this, rules, getPreload(), tbox);
if (enableTGCCaching) (graph).setUseTGCCache();
graph.prepare();
RDFSRuleReasoner grr = new RDFSRuleReasoner(graph, factory);
grr.setDerivationLogging(recordDerivations);
grr.setTraceOn(traceOn);
grr.setTransitiveClosureCaching(enableTGCCaching);
grr.setFunctorFiltering(filterFunctors);
if (preprocessorHooks != null) {
for (RulePreprocessHook rulePreprocessHook : preprocessorHooks)
{
grr.addPreprocessingHook(rulePreprocessHook);
}
}
return grr;
}
/**
* Return the RDFS rule set, loading it in if necessary.
* @param level a string defining the processing level required
*/
public static List<Rule> loadRulesLevel(String level) {
List<Rule> ruleSet = ruleSets.get(level);
if (ruleSet == null) {
String file = ruleFiles.get(level);
if (file == null) {
throw new ReasonerException("Illegal RDFS conformance level: " + level);
}
ruleSet = loadRules( file );
ruleSets.put(level, ruleSet);
}
return ruleSet;
}
/**
* Return the Jena Graph Capabilities that the inference graphs generated
* by this reasoner are expected to conform to.
*/
@Override
public Capabilities getGraphCapabilities() {
return BaseInfGraph.reasonerInfCapabilities;
}
}