package de.unikiel.inf.comsys.neo4j.inference.rules; /* * #%L * neo4j-sparql-extension * %% * Copyright (C) 2014 Niclas Hoyer * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import com.google.common.base.Joiner; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.openrdf.query.algebra.EmptySet; import org.openrdf.query.algebra.Extension; import org.openrdf.query.algebra.ExtensionElem; import org.openrdf.query.algebra.QueryModelNode; import org.openrdf.query.algebra.StatementPattern; import org.openrdf.query.algebra.TupleExpr; import org.openrdf.query.algebra.Union; import org.openrdf.query.algebra.ValueConstant; import org.openrdf.query.algebra.Var; /** * A rule that transforms a statement pattern that contains a variable as * predicate to include inferred triples. */ public class PredicateVariable extends AbstractRule { private final List<String> predicates; /** * Create a new predicate variable rule. * * @param predicates all possible predicates of the TBox */ public PredicateVariable(String... predicates) { this(Arrays.asList(predicates)); } /** * Create a new predicate variable rule. * * @param predicates all possible predicates of the TBox */ public PredicateVariable(List<String> predicates) { this.predicates = predicates; } /** * Returns true if this rule is applicable to a node. * * @param node to a node * @return true if the rule is applicable, false otherwise */ @Override public boolean canApply(StatementPattern node) { Var p = node.getPredicateVar(); // check if predicate is variable return !(predicates.isEmpty() || p.isConstant()); } /** * Returns a list of expressions as a chain of {@link Union} objects, that * will unify all expressions. * * @param unions the expressions to unify * @return an expression that unifies all given expressions */ private TupleExpr listAsUnion(List<TupleExpr> unions) { // nothing to unify if (unions.isEmpty()) { return new EmptySet(); } // no need to add a union if (unions.size() == 1) { return unions.get(0); } Union last; Union tmp; // start with a union of two elements Union first = new Union(); first.setLeftArg(unions.get(0)); first.setRightArg(unions.get(1)); last = first; // for each additional element replace the right side with another // union for (int i = 2; i < unions.size(); i++) { tmp = new Union( last.getRightArg(), unions.get(i) ); last.setRightArg(tmp); last = tmp; } return first; } /** * Creates an expression that extends a statement pattern to include * inferred predicates. * * @param assign a list of predicates that could be inferred * @param source the source statement pattern * @param next a list of expressions that should be visited next * @return a union of statement patterns that include inferred triples * for a predicate variable */ private TupleExpr assignPredicates( List<String> assign, StatementPattern source, List<QueryModelNode> next) { if (assign.isEmpty()) { return source; } Var s = source.getSubjectVar(); Var p = source.getPredicateVar(); Var o = source.getObjectVar(); Var c = source.getContextVar(); Var p2; StatementPattern sp; ArrayList<TupleExpr> union = new ArrayList<>(); // for each possible predicate create an extension element that will // bind the predicate to the variable, if the predicate can be inferred. for (String a : assign) { p2 = new ConstVar(vf.createURI(a)); sp = new StatementPattern(s, p2, o, c); next.add(sp); union.add(new Extension( sp, new ExtensionElem( new ValueConstant(vf.createURI(a)), p.getName()))); } return listAsUnion(union); } /** * Transform a statement pattern to infer triples for a predicate variable. * * @param node the node to transform * @return list of nodes to visit next */ @Override public List<QueryModelNode> apply(StatementPattern node) { List<QueryModelNode> next = newNextList(); StatementPattern left = node.clone(); next.add(left); TupleExpr right = assignPredicates(predicates, node.clone(), next); node.replaceWith(new Union(left, right)); return next; } @Override public String toString() { String str = ""; if (!predicates.isEmpty()) { str += "<"; str += Joiner.on("> <").join(predicates) + ">"; } return "PredicateVariable("+ str + ")"; } }