/*
* 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.impl;
import java.util.*;
import org.apache.jena.graph.* ;
import org.apache.jena.reasoner.* ;
import org.apache.jena.reasoner.rulesys.* ;
import org.apache.jena.util.OneToManyMap ;
/**
* Indexes a collection of rule. The currently implementation is
* a crude first version aimed at supporting the backchaining
* interpreter. It only indexes on predicate.
* <p>
* The rules are normalized to only contain a single head element
* by duplicating any multi headed rules.
* </p>
*/
public class RuleStore {
/** The set of rules indexed by head predicate */
protected OneToManyMap<Node, Rule> goalMap = new OneToManyMap<>();
/** The list of all rules in the store */
protected List<Rule> allRules;
/** Index of the rules, used to block multiple entries */
protected Set<Rule> ruleIndex = new HashSet<>();
/**
* Constructor. Create an empty rule store.
*/
public RuleStore() {
allRules = new ArrayList<>();
}
/**
* Constructor. Stores and indexes a list of rules.
*/
public RuleStore(List<Rule> rules) {
for ( Rule rule : rules )
{
addRule( rule );
}
allRules = rules;
}
/**
* Add all the rules and from an existing rulestore into this one.
*/
public void addAll(RuleStore store) {
for ( Rule rule : store.getAllRules() )
{
addRule( rule );
}
}
/**
* Add a single rule to the store.
*/
public void addRule(Rule rule) {
addRemoveRule(rule, true);
}
/**
* Remove a single rule from the store
*/
public void deleteRule(Rule rule) {
addRemoveRule(rule, false);
}
/**
* Add a single/remove a compound rule from the store.
* @param rule the rule, may have multiple heads
* @param isAdd true to add, false to remove
*/
private void addRemoveRule(Rule rule, boolean isAdd) {
if (rule.headLength() != 1) {
for (int j = 0; j < rule.headLength(); j++) {
Rule newRule = new Rule(rule.getName(),
new ClauseEntry[] {rule.getHeadElement(j)},
rule.getBody() );
newRule.setNumVars(rule.getNumVars());
doAddRemoveRule(newRule, isAdd);
}
} else {
doAddRemoveRule(rule, isAdd);
}
}
/**
* Add/remove a single rule from the store.
* @param rule the rule, single headed only
* @param isAdd true to add, false to remove
*/
protected void doAddRemoveRule(Rule rule, boolean isAdd) {
if (isAdd && ruleIndex.contains(rule)) return;
if (isAdd) {
ruleIndex.add(rule);
if (allRules != null) allRules.add(rule);
} else {
ruleIndex.remove(rule);
if (allRules != null) allRules.remove(rule);
}
Object headClause = rule.getHeadElement(0);
if (headClause instanceof TriplePattern) {
TriplePattern headpattern = (TriplePattern)headClause;
Node predicate = headpattern.getPredicate();
if (predicate.isVariable()) {
if (isAdd) {
goalMap.put(Node.ANY, rule);
} else {
goalMap.remove(Node.ANY, rule);
}
} else {
if (isAdd) {
goalMap.put(predicate, rule);
} else {
goalMap.remove(predicate, rule);
}
}
}
}
/**
* Return a list of rules that match the given goal pattern
* @param goal the goal being matched
*/
public List<Rule> rulesFor(TriplePattern goal) {
List<Rule> rules = new ArrayList<>();
if (goal.getPredicate().isVariable()) {
checkAll(goalMap.values().iterator(), goal, rules);
} else {
checkAll(goalMap.getAll(goal.getPredicate()), goal, rules);
checkAll(goalMap.getAll(Node.ANY), goal, rules);
}
return rules;
}
/**
* Return an ordered list of all registered rules.
*/
public List<Rule> getAllRules() {
return allRules;
}
/**
* Delete all the rules.
*/
public void deleteAllRules() {
allRules.clear();
goalMap.clear();
ruleIndex.clear();
}
/**
* Helper method to extract all matching clauses from an
* iterator over rules
*/
private void checkAll(Iterator<Rule> candidates, TriplePattern goal, List<Rule> matchingRules) {
while (candidates.hasNext()) {
Rule r = candidates.next();
if ( ((TriplePattern)r.getHeadElement(0)).compatibleWith(goal) ) {
matchingRules.add(r);
}
}
}
}