/* * 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.TriplePattern ; import org.apache.jena.reasoner.rulesys.* ; /** * Holds the set of backward rules used by an LPEngine. Is responsible * for compile the rules into internal byte codes before use. */ public class LPRuleStore extends RuleStore { /** Flag to indicate whether the rules have been compiled into code objects */ protected boolean isCompiled = false; /** A map from predicate to a list of RuleClauseCode objects for that predicate. * Uses Node_RuleVariable.WILD for wildcard predicates. */ protected Map<Node, List<RuleClauseCode>> predicateToCodeMap; /** The list of all RuleClauseCode objects, used to implement wildcard queries */ protected ArrayList<RuleClauseCode> allRuleClauseCodes; /** Two level index map - index on predicate then on object */ protected Map<Node, Map<Node, List<RuleClauseCode>>> indexPredicateToCodeMap; /** Set of predicates which should be tabled */ protected HashSet<Node> tabledPredicates = new HashSet<>(); /** Threshold for number of rule entries in a predicate bucket before second level indexing kicks in */ private static final int INDEX_THRESHOLD = 20; /** True if all goals should be treated as tabled */ protected boolean allTabled = false; /** * Construct a rule store containing the given rules. * @param rules the rules to initialize the store with. */ public LPRuleStore(List<Rule> rules) { super(rules); } /** * Construct an empty rule store */ public LPRuleStore() { super(); } /** * Add all the rules and tabling instructions from an existing rulestore into this one. */ public void addAll(LPRuleStore store) { super.addAll(store); tabledPredicates.addAll(store.tabledPredicates); allTabled = tabledPredicates.contains(Node.ANY); } /** * Register an RDF predicate as one whose presence in a goal should force * the goal to be tabled. */ public synchronized void tablePredicate(Node predicate) { tabledPredicates.add(predicate); if (predicate == Node.ANY) allTabled = true; } /** * Return an ordered list of RuleClauseCode objects to implement the given * predicate. * @param predicate the predicate node or Node_RuleVariable.WILD for wildcards. */ public List<RuleClauseCode> codeFor(Node predicate) { if (!isCompiled) { compileAll(); } if (predicate.isVariable()) { return allRuleClauseCodes; } else { List<RuleClauseCode> codeList = predicateToCodeMap.get(predicate); if (codeList == null) { // Uknown predicate, so only the wildcard rules apply codeList = predicateToCodeMap.get(Node_RuleVariable.WILD); } return codeList; } } /** * Return an ordered list of RuleClauseCode objects to implement the given * query pattern. This may use indexing to narrow the rule set more that the predicate-only case. * @param goal the triple pattern that makes up the query */ public List<RuleClauseCode> codeFor(TriplePattern goal) { List<RuleClauseCode> allRules = codeFor(goal.getPredicate()); if (allRules == null) { return allRules; } Map<Node, List<RuleClauseCode>> indexedCodeTable = indexPredicateToCodeMap.get(goal.getPredicate()); if (indexedCodeTable != null) { List<RuleClauseCode> indexedCode = indexedCodeTable.get(goal.getObject()); if (indexedCode != null) { return indexedCode; } } return allRules; } /** * Return true if the given predicate is indexed. */ public boolean isIndexedPredicate(Node predicate) { return (indexPredicateToCodeMap.get(predicate) != null); } /** * Return true if the given goal is tabled, currently this is true if the * predicate is a tabled predicate or the predicate is a wildcard and some * tabled predictes exist. */ public boolean isTabled(TriplePattern goal) { return isTabled(goal.getPredicate()); } /** * Return true if the given predicated is tabled, currently this is true if the * predicate is a tabled predicate or the predicate is a wildcard and some * tabled predictes exist. */ public boolean isTabled(Node predicate) { if (allTabled) return true; if (predicate.isVariable() && !tabledPredicates.isEmpty()) { return true; } else { return tabledPredicates.contains(predicate); } } /** * Compile all the rules in a table. initially just indexed on predicate but want to * add better indexing for the particular cases of wildcard rules and type rules. */ protected void compileAll() { isCompiled = true; predicateToCodeMap = new HashMap<>(); allRuleClauseCodes = new ArrayList<>(); indexPredicateToCodeMap = new HashMap<>(); for ( Rule r : getAllRules() ) { ClauseEntry term = r.getHeadElement( 0 ); if ( term instanceof TriplePattern ) { RuleClauseCode code = new RuleClauseCode( r ); allRuleClauseCodes.add( code ); Node predicate = ( (TriplePattern) term ).getPredicate(); if ( predicate.isVariable() ) { predicate = Node_RuleVariable.WILD; } List<RuleClauseCode> predicateCode = predicateToCodeMap.get( predicate ); if ( predicateCode == null ) { predicateCode = new ArrayList<>(); predicateToCodeMap.put( predicate, predicateCode ); } predicateCode.add( code ); if ( predicateCode.size() > INDEX_THRESHOLD ) { indexPredicateToCodeMap.put( predicate, new HashMap<Node, List<RuleClauseCode>>() ); } } } // Now add the wild card rules into the list for each non-wild predicate) List<RuleClauseCode> wildRules = predicateToCodeMap.get(Node_RuleVariable.WILD); if (wildRules != null) { for ( Map.Entry<Node, List<RuleClauseCode>> entry : predicateToCodeMap.entrySet() ) { Node predicate = entry.getKey(); List<RuleClauseCode> predicateCode = entry.getValue(); if ( predicate != Node_RuleVariable.WILD ) { predicateCode.addAll( wildRules ); } } } indexPredicateToCodeMap.put(Node_RuleVariable.WILD, new HashMap<Node, List<RuleClauseCode>>()); // Now built any required two level indices for ( Map.Entry<Node, Map<Node, List<RuleClauseCode>>> entry : indexPredicateToCodeMap.entrySet() ) { Node predicate = entry.getKey(); Map<Node, List<RuleClauseCode>> predicateMap = entry.getValue(); List<RuleClauseCode> wildRulesForPredicate = new ArrayList<>(); List<RuleClauseCode> allRulesForPredicate = predicate.isVariable() ? allRuleClauseCodes : predicateToCodeMap.get( predicate ); for ( Iterator<RuleClauseCode> j = allRulesForPredicate.iterator(); j.hasNext(); ) { RuleClauseCode code = j.next(); ClauseEntry head = code.getRule().getHeadElement( 0 ); boolean indexed = false; if ( head instanceof TriplePattern ) { Node objectPattern = ( (TriplePattern) head ).getObject(); if ( !objectPattern.isVariable() && !Functor.isFunctor( objectPattern ) ) { // Index against object List<RuleClauseCode> indexedCode = predicateMap.get( objectPattern ); if ( indexedCode == null ) { indexedCode = new ArrayList<>(); predicateMap.put( objectPattern, indexedCode ); } indexedCode.add( code ); indexed = true; } } if ( !indexed ) { wildRulesForPredicate.add( code ); } } // Now fold the rules that apply to any index entry into all the indexed entries for ( Iterator<Map.Entry<Node, List<RuleClauseCode>>> k = predicateMap.entrySet().iterator(); k.hasNext(); ) { Map.Entry<Node, List<RuleClauseCode>> ent = k.next(); List<RuleClauseCode> predicateCode = ent.getValue(); predicateCode.addAll( wildRulesForPredicate ); } } // Now compile all the clauses for ( RuleClauseCode code : allRuleClauseCodes ) { code.compile( this ); } } /** * Add/remove a single rule from the store. * Overridden in order to reset the "isCompiled" flag. * * @param rule the rule, single headed only * @param isAdd true to add, false to remove */ @Override protected void doAddRemoveRule(Rule rule, boolean isAdd) { isCompiled = false; super.doAddRemoveRule(rule, isAdd); } }