/** Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com 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; version 2 of the License. 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Created on Aug 27, 2011 */ package com.bigdata.rdf.sail.sparql; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.openrdf.model.Literal; import org.openrdf.model.Resource; import org.openrdf.model.URI; import org.openrdf.model.Value; import org.openrdf.model.vocabulary.RDF; import com.bigdata.bop.BOp; import com.bigdata.bop.BOpUtility; import com.bigdata.rdf.internal.impl.bnode.SidIV; import com.bigdata.rdf.model.BigdataBNode; import com.bigdata.rdf.model.BigdataStatement; import com.bigdata.rdf.model.BigdataValue; import com.bigdata.rdf.sail.sparql.ast.ASTBind; import com.bigdata.rdf.sail.sparql.ast.ASTBlankNodePropertyList; import com.bigdata.rdf.sail.sparql.ast.ASTCollection; import com.bigdata.rdf.sail.sparql.ast.ASTConstruct; import com.bigdata.rdf.sail.sparql.ast.ASTObjectList; import com.bigdata.rdf.sail.sparql.ast.ASTPathAlternative; import com.bigdata.rdf.sail.sparql.ast.ASTPathElt; import com.bigdata.rdf.sail.sparql.ast.ASTPathMod; import com.bigdata.rdf.sail.sparql.ast.ASTPathOneInPropertySet; import com.bigdata.rdf.sail.sparql.ast.ASTPathSequence; import com.bigdata.rdf.sail.sparql.ast.ASTPropertyList; import com.bigdata.rdf.sail.sparql.ast.ASTPropertyListPath; import com.bigdata.rdf.sail.sparql.ast.ASTTRefPattern; import com.bigdata.rdf.sail.sparql.ast.ASTVar; import com.bigdata.rdf.sail.sparql.ast.Node; import com.bigdata.rdf.sail.sparql.ast.VisitorException; import com.bigdata.rdf.sparql.ast.ConstantNode; import com.bigdata.rdf.sparql.ast.PathNode; import com.bigdata.rdf.sparql.ast.PathNode.PathAlternative; import com.bigdata.rdf.sparql.ast.PathNode.PathElt; import com.bigdata.rdf.sparql.ast.PathNode.PathMod; import com.bigdata.rdf.sparql.ast.PathNode.PathNegatedPropertySet; import com.bigdata.rdf.sparql.ast.PathNode.PathOneInPropertySet; import com.bigdata.rdf.sparql.ast.PathNode.PathSequence; import com.bigdata.rdf.sparql.ast.StatementPatternNode; import com.bigdata.rdf.sparql.ast.TermNode; import com.bigdata.rdf.sparql.ast.VarNode; import com.bigdata.rdf.sparql.ast.optimizers.ASTPropertyPathOptimizer; /** * Class handles triple patterns and property paths. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ * @openrdf */ public class TriplePatternExprBuilder extends ValueExprBuilder { private static final transient Logger log = Logger.getLogger(TriplePatternExprBuilder.class); /** * @param context */ public TriplePatternExprBuilder(final BigdataASTContext context) { super(context); } @Override final public Object visit(final ASTPropertyList propListNode, final Object data) throws VisitorException { final TermNode subject = (TermNode) data; final TermNode predicate = (TermNode) propListNode.getVerb().jjtAccept( this, null); @SuppressWarnings("unchecked") final List<TermNode> objectList = (List<TermNode>) propListNode .getObjectList().jjtAccept(this, null); for (TermNode object : objectList) { graphPattern.addSP(subject, predicate, object); } final ASTPropertyList nextPropList = propListNode.getNextPropertyList(); if (nextPropList != null) { nextPropList.jjtAccept(this, subject); } return null; } @Override final public Object visit(final ASTPropertyListPath propListNode, final Object data) throws VisitorException { final TermNode subject = (TermNode) data; final BOp verb = (BOp) propListNode.getVerb().jjtAccept( this, data); @SuppressWarnings("unchecked") final List<TermNode> objectList = (List<TermNode>) propListNode .getObjectList().jjtAccept(this, null); if (verb instanceof TermNode) { final TermNode predicate = (TermNode) verb; for (TermNode object : objectList) { graphPattern.addSP(subject, predicate, object); } } else { final PathAlternative pathAlt = (PathAlternative) verb; /* * Even simple triple patterns are going through the property path * code logic. Easier to just recognize those here, although * we could just as easily do it in the optimizer. But this would * break a lot of the test cases. */ if (ASTPropertyPathOptimizer.isSimpleIRI(pathAlt)) { final ConstantNode predicate = ASTPropertyPathOptimizer.getSimpleIRI(pathAlt); for (TermNode object : objectList) { graphPattern.addSP(subject, predicate, object); } } else { if (log.isDebugEnabled()) log.debug(BOpUtility.toString(pathAlt)); final PathNode path = new PathNode(pathAlt); for (TermNode object : objectList) { graphPattern.addPP(subject, path, object); } } } // if (verbPath instanceof VarNode) { // // @SuppressWarnings("unchecked") // final List<TermNode> objectList = (List<TermNode>) propListNode // .getObjectList().jjtAccept(this, null); // // for (TermNode object : objectList) { // // graphPattern.addSP(subject, verbPath, object); // // } // // } else { // // /* // * PROPERTY PATHS // * // * Note: This code path is a single IRI (degenerative case) or a // * more complex path. This is handled by the aspect of the visitor // * which deals with property paths, even in the case where the // * triple pattern is as simple as (var const var). // * // * FIXME This code block should be empty when we incorporate support // * for property paths from the Sesame visitor methods. It currently // * duplicates the code block above and provides handling for the // * case when the predicate is a constant. // * // * Just need to make this block empty and let the visitor do the work. // */ // //// @SuppressWarnings("unchecked") //// final List<TermNode> objectList = (List<TermNode>) propListNode //// .getObjectList().jjtAccept(this, null); //// //// for (TermNode object : objectList) { //// //// graphPattern.addSP(subject, verbPath, object); //// //// } // // } final ASTPropertyListPath nextPropList = propListNode .getNextPropertyList(); if (nextPropList != null) { nextPropList.jjtAccept(this, subject); } return null; } @Override public PathAlternative visit(final ASTPathAlternative pathAltNode, Object data) throws VisitorException { final int numChildren = pathAltNode.jjtGetNumChildren(); final PathSequence[] args = new PathSequence[numChildren]; for (int i = 0; i < numChildren; i++) { args[i] = (PathSequence) pathAltNode.jjtGetChild(i).jjtAccept(this, data); } return new PathAlternative(args); } @Override public PathSequence visit(ASTPathSequence pathSeqNode, Object data) throws VisitorException { final int numChildren = pathSeqNode.jjtGetNumChildren(); final PathElt[] args = new PathElt[numChildren]; for (int i = 0; i < numChildren; i++) { args[i] = (PathElt) pathSeqNode.jjtGetChild(i).jjtAccept(this, data); } return new PathSequence(args); } @Override public Object visit(ASTPathElt pathEltNode, Object data) throws VisitorException { final BOp arg; if (pathEltNode.isNegatedPropertySet()) { final int numChildren = pathEltNode.jjtGetNumChildren(); final PathOneInPropertySet[] args = new PathOneInPropertySet[numChildren]; for (int i = 0; i < numChildren; i++) { args[i] = (PathOneInPropertySet) pathEltNode.jjtGetChild(i).jjtAccept(this, data); } arg = new PathNegatedPropertySet(args); } else { // handle the iri and nested path cases arg = (BOp) pathEltNode.jjtGetChild(0).jjtAccept(this, data); } final boolean inverse = pathEltNode.isInverse(); final PathMod mod = toPathMod(pathEltNode.getPathMod()); return new PathElt(arg, inverse, mod); } private PathMod toPathMod(ASTPathMod pathModNode) { if (pathModNode == null) return null; final long lower = pathModNode.getLowerBound(); final long upper = pathModNode.getUpperBound(); if (upper == 1) { return PathMod.ZERO_OR_ONE; } else if (lower == 1) { return PathMod.ONE_OR_MORE; } else { return PathMod.ZERO_OR_MORE; } } @Override public Object visit(final ASTPathOneInPropertySet node, Object data) throws VisitorException { final ConstantNode arg = (ConstantNode) node.jjtGetChild(0).jjtAccept(this, data); final boolean inverse = node.isInverse(); return new PathOneInPropertySet(arg, inverse); } @Override final public List<TermNode> visit(final ASTObjectList node, final Object data) throws VisitorException { final int childCount = node.jjtGetNumChildren(); final List<TermNode> result = new ArrayList<TermNode>(childCount); for (int i = 0; i < childCount; i++) { result.add((TermNode) node.jjtGetChild(i).jjtAccept(this, null)); } return result; } @Override final public VarNode visit(final ASTBlankNodePropertyList node, final Object data) throws VisitorException { final VarNode bnodeVar = context.createAnonVar(node.getVarName()); super.visit(node, bnodeVar); return bnodeVar; } /** * Handle the RDF Collection syntax. */ @Override public VarNode visit(final ASTCollection node, final Object data) throws VisitorException { final String listVarName = node.getVarName(); final VarNode rootListVar = context.createAnonVar(listVarName); TermNode listVar = rootListVar; final int childCount = node.jjtGetNumChildren(); for (int i = 0; i < childCount; i++) { final TermNode childValue = (TermNode) node.jjtGetChild(i) .jjtAccept(this, null); graphPattern.addSP(listVar, context.createConstVar(RDF.FIRST), childValue); TermNode nextListVar; if (i == childCount - 1) { nextListVar = context.createConstVar(RDF.NIL); } else { nextListVar = context.createAnonVar(listVarName + "-" + (i + 1)); } graphPattern.addSP(listVar, context.createConstVar(RDF.REST), nextListVar); listVar = nextListVar; } return rootListVar; } // // FIXME Property paths (Implement). // // @Override // public Object visit(ASTPathAlternative pathAltNode, Object data) // throws VisitorException // { // // int altCount = pathAltNode.jjtGetNumChildren(); // // if (altCount > 1) { // GraphPattern parentGP = graphPattern; // Union union = new Union(); // Union currentUnion = union; // for (int i = 0; i < altCount - 1; i++) { // graphPattern = new GraphPattern(parentGP); // pathAltNode.jjtGetChild(i).jjtAccept(this, data); // TupleExpr arg = graphPattern.buildTupleExpr(); // currentUnion.setLeftArg(arg); // if (i == altCount - 2) { // second-to-last item // graphPattern = new GraphPattern(parentGP); // pathAltNode.jjtGetChild(i + 1).jjtAccept(this, data); // arg = graphPattern.buildTupleExpr(); // currentUnion.setRightArg(arg); // } // else { // Union newUnion = new Union(); // currentUnion.setRightArg(newUnion); // currentUnion = newUnion; // } // } // // parentGP.addRequiredTE(union); // graphPattern = parentGP; // } // else { // pathAltNode.jjtGetChild(0).jjtAccept(this, data); // } // // return null; // } // // @Override // public PathOneInPropertySet visit(ASTPathOneInPropertySet node, Object data) // throws VisitorException // { // // PropertySetElem result = new PropertySetElem(); // result.setInverse(node.isInverse()); // ValueConstant predicate = (ValueConstant)node.jjtGetChild(0).jjtAccept(this, data); // result.setPredicate(predicate); // // return result; // } // // private ASTObjectList getObjectList(Node node) { // if (node == null) { // return null; // } // if (node instanceof ASTPropertyListPath) { // return ((ASTPropertyListPath)node).getObjectList(); // } // else { // return getObjectList(node.jjtGetParent()); // } // } // // private boolean checkInverse(Node node) { // if (node instanceof ASTPathElt) { // return ((ASTPathElt)node).isInverse(); // } // else { // Node parent = node.jjtGetParent(); // if (parent != null) { // return checkInverse(parent); // } // else { // return false; // } // } // } // // @Override // public Object visit(ASTPathSequence pathSeqNode, Object data) // throws VisitorException // { // // ValueExpr subject = (ValueExpr)data; // Var subjVar = valueExpr2Var(subject); // // // check if we should invert subject and object. // boolean invertSequence = checkInverse(pathSeqNode); // // @SuppressWarnings("unchecked") // List<ValueExpr> objectList = (List<ValueExpr>)getObjectList(pathSeqNode).jjtAccept(this, null); // // List<ASTPathElt> pathElements = pathSeqNode.getPathElements(); // // int pathLength = pathElements.size(); // // GraphPattern pathSequencePattern = new GraphPattern(graphPattern); // // Scope scope = pathSequencePattern.getStatementPatternScope(); // Var contextVar = pathSequencePattern.getContextVar(); // // Var startVar = subjVar; // // for (int i = 0; i < pathLength; i++) { // ASTPathElt pathElement = pathElements.get(i); // // ASTPathMod pathMod = pathElement.getPathMod(); // // long lowerBound = Long.MIN_VALUE; // long upperBound = Long.MIN_VALUE; // // if (pathMod != null) { // lowerBound = pathMod.getLowerBound(); // upperBound = pathMod.getUpperBound(); // // if (upperBound == Long.MIN_VALUE) { // upperBound = lowerBound; // } // else if (lowerBound == Long.MIN_VALUE) { // lowerBound = upperBound; // } // } // // if (pathElement.isNegatedPropertySet()) { // // // create a temporary negated property set object and set the // // correct subject and object vars to continue // // the path sequence. // // NegatedPropertySet nps = new NegatedPropertySet(); // nps.setScope(scope); // nps.setSubjectVar(startVar); // nps.setContextVar(contextVar); // // for (Node child : pathElement.jjtGetChildren()) { // nps.addPropertySetElem((PropertySetElem)child.jjtAccept(this, data)); // } // // if (i == pathLength - 1) { // nps.setObjectList(objectList); // } // else { // // not last element in path. // Var nextVar = createAnonVar(startVar.getName() + "-property-set-" + i); // // List<ValueExpr> nextVarList = new ArrayList<ValueExpr>(); // nextVarList.add(nextVar); // nps.setObjectList(nextVarList); // // startVar = nextVar; // } // // // convert the NegatedPropertySet to a proper TupleExpr // pathSequencePattern.addRequiredTE(createTupleExprForNegatedPropertySet(nps, i)); // // } // else if (pathElement.isNestedPath()) { // GraphPattern parentGP = graphPattern; // // graphPattern = new GraphPattern(parentGP); // // if (i == pathLength - 1) { // // last element in the path // pathElement.jjtGetChild(0).jjtAccept(this, startVar); // // TupleExpr te = graphPattern.buildTupleExpr(); // // for (ValueExpr object : objectList) { // Var objVar = valueExpr2Var(object); // te = handlePathModifiers(scope, startVar, te, objVar, contextVar, lowerBound, upperBound); // pathSequencePattern.addRequiredTE(te); // } // } // else { // // not the last element in the path, introduce an anonymous var // // to connect. // Var nextVar = createAnonVar(subjVar.getName() + "-nested-" + i); // // pathElement.jjtGetChild(0).jjtAccept(this, startVar); // // TupleExpr te = graphPattern.buildTupleExpr(); // // // replace all object list occurrences with the intermediate var. // // te = replaceVarOccurrence(te, objectList, nextVar); // te = handlePathModifiers(scope, startVar, te, nextVar, contextVar, lowerBound, upperBound); // pathSequencePattern.addRequiredTE(te); // // startVar = nextVar; // } // // graphPattern = parentGP; // } // else { // // ValueExpr pred = (ValueExpr)pathElement.jjtAccept(this, data); // Var predVar = valueExpr2Var(pred); // // TupleExpr te; // // if (i == pathLength - 1) { // // last element in the path, connect to list of defined objects // for (ValueExpr object : objectList) { // Var objVar = valueExpr2Var(object); // Var endVar = objVar; // // if (invertSequence) { // endVar = subjVar; // } // // if (pathElement.isInverse()) { // te = new StatementPattern(scope, endVar, predVar, startVar, contextVar); // te = handlePathModifiers(scope, endVar, te, startVar, contextVar, lowerBound, upperBound); // } // else { // te = new StatementPattern(scope, startVar, predVar, endVar, contextVar); // te = handlePathModifiers(scope, startVar, te, endVar, contextVar, lowerBound, upperBound); // // } // // pathSequencePattern.addRequiredTE(te); // } // } // else { // // not the last element in the path, introduce an anonymous var // // to connect. // Var nextVar = createAnonVar(predVar.getName() + "-" + i); // // if (invertSequence && startVar.equals(subjVar)) { // first // // element in // // inverted // // sequence // for (ValueExpr object : objectList) { // Var objVar = valueExpr2Var(object); // startVar = objVar; // // if (pathElement.isInverse()) { // Var temp = startVar; // startVar = nextVar; // nextVar = temp; // } // // te = new StatementPattern(scope, startVar, predVar, nextVar, contextVar); // te = handlePathModifiers(scope, startVar, te, nextVar, contextVar, lowerBound, // upperBound); // // pathSequencePattern.addRequiredTE(te); // } // } // else { // // if (pathElement.isInverse()) { // startVar = nextVar; // nextVar = subjVar; // } // // te = new StatementPattern(scope, startVar, predVar, nextVar, contextVar); // te = handlePathModifiers(scope, startVar, te, nextVar, contextVar, lowerBound, upperBound); // // pathSequencePattern.addRequiredTE(te); // } // // // set the subject for the next element in the path. // startVar = (pathElement.isInverse() ? startVar : nextVar); // } // } // } // // // add the created path sequence to the graph pattern. // for (TupleExpr te : pathSequencePattern.getRequiredTEs()) { // graphPattern.addRequiredTE(te); // } // // return null; // } // // private TupleExpr createTupleExprForNegatedPropertySet(NegatedPropertySet nps, int index) { // Var subjVar = nps.getSubjectVar(); // // Var predVar = createAnonVar("nps-" + subjVar.getName() + "-" + index); // // Var predVarInverse = createAnonVar("nps-inverse-" + subjVar.getName() + // // "-" + index); // // ValueExpr filterCondition = null; // ValueExpr filterConditionInverse = null; // // // build (inverted) filter conditions for each negated path element. // for (PropertySetElem elem : nps.getPropertySetElems()) { // ValueConstant predicate = elem.getPredicate(); // // if (elem.isInverse()) { // Compare compare = new Compare(predVar, predicate, CompareOp.NE); // if (filterConditionInverse == null) { // filterConditionInverse = compare; // } // else { // filterConditionInverse = new And(compare, filterConditionInverse); // } // } // else { // Compare compare = new Compare(predVar, predicate, CompareOp.NE); // if (filterCondition == null) { // filterCondition = compare; // } // else { // filterCondition = new And(compare, filterCondition); // } // } // } // // TupleExpr patternMatch = null; // // // build a regular statement pattern (or a join of several patterns if the // // object list has more than // // one item) // if (filterCondition != null) { // for (ValueExpr objVar : nps.getObjectList()) { // if (patternMatch == null) { // patternMatch = new StatementPattern(nps.getScope(), subjVar, predVar, (Var)objVar, // nps.getContextVar()); // } // else { // patternMatch = new Join(new StatementPattern(nps.getScope(), subjVar, predVar, (Var)objVar, // nps.getContextVar()), patternMatch); // } // } // } // // TupleExpr patternMatchInverse = null; // // // build a inverse statement pattern (or a join of several patterns if the // // object list has more than // // one item): // if (filterConditionInverse != null) { // for (ValueExpr objVar : nps.getObjectList()) { // if (patternMatchInverse == null) { // patternMatchInverse = new StatementPattern(nps.getScope(), (Var)objVar, predVar, subjVar, // nps.getContextVar()); // } // else { // patternMatchInverse = new Join(new StatementPattern(nps.getScope(), (Var)objVar, predVar, // subjVar, nps.getContextVar()), patternMatchInverse); // } // } // } // // TupleExpr completeMatch = null; // // if (patternMatch != null) { // completeMatch = new Filter(patternMatch, filterCondition); // } // // if (patternMatchInverse != null) { // if (completeMatch == null) { // completeMatch = new Filter(patternMatchInverse, filterConditionInverse); // } // else { // completeMatch = new Union(new Filter(patternMatchInverse, filterConditionInverse), completeMatch); // } // } // // return completeMatch; // } // // private TupleExpr replaceVarOccurrence(TupleExpr te, List<ValueExpr> objectList, Var replacementVar) // throws VisitorException // { // for (ValueExpr objExpr : objectList) { // Var objVar = valueExpr2Var(objExpr); // VarReplacer replacer = new VarReplacer(objVar, replacementVar); // te.visit(replacer); // } // return te; // } // // private TupleExpr handlePathModifiers(Scope scope, Var subjVar, TupleExpr te, Var endVar, Var contextVar, // long lowerBound, long upperBound) // throws VisitorException // { // // TupleExpr result = te; // // if (lowerBound >= 0L) { // // if (lowerBound < upperBound) { // // if (upperBound < Long.MAX_VALUE) { // // upperbound is fixed-length // // // create set of unions for all path lengths between lower // // and upper bound. // Union union = new Union(); // Union currentUnion = union; // // for (long length = lowerBound; length < upperBound; length++) { // // TupleExpr path = createPath(scope, subjVar, te, endVar, contextVar, length); // // currentUnion.setLeftArg(path); // if (length == upperBound - 1) { // path = createPath(scope, subjVar, te, endVar, contextVar, length + 1); // currentUnion.setRightArg(path); // } // else { // Union nextUnion = new Union(); // currentUnion.setRightArg(nextUnion); // currentUnion = nextUnion; // } // } // // result = union; // } // else { // // upperbound is abitrary-length // // result = new ArbitraryLengthPath(scope, subjVar, te, endVar, contextVar, lowerBound); // } // } // else { // // create single path of fixed length. // TupleExpr path = createPath(scope, subjVar, te, endVar, contextVar, lowerBound); // result = path; // } // } // // return result; // } // // private TupleExpr createPath(Scope scope, Var subjVar, TupleExpr pathExpression, Var endVar, // Var contextVar, long length) // throws VisitorException // { // if (pathExpression instanceof StatementPattern) { // Var predVar = ((StatementPattern)pathExpression).getPredicateVar(); // // if (length == 0L) { // return new ZeroLengthPath(scope, subjVar, endVar, contextVar); // } // else { // GraphPattern gp = new GraphPattern(); // gp.setContextVar(contextVar); // gp.setStatementPatternScope(scope); // // Var nextVar = null; // // for (long i = 0L; i < length; i++) { // if (i < length - 1) { // nextVar = createAnonVar(predVar.getName() + "-path-" + length + "-" + i); // } // else { // nextVar = endVar; // } // gp.addRequiredSP(subjVar, predVar, nextVar); // subjVar = nextVar; // } // return gp.buildTupleExpr(); // } // } // else { // if (length == 0L) { // return new ZeroLengthPath(scope, subjVar, endVar, contextVar); // } // else { // GraphPattern gp = new GraphPattern(); // gp.setContextVar(contextVar); // gp.setStatementPatternScope(scope); // // Var nextVar = null; // for (long i = 0L; i < length; i++) { // if (i < length - 1L) { // nextVar = createAnonVar(subjVar.getName() + "-expression-path-" + length + "-" + i); // } // else { // nextVar = endVar; // } // // // create a clone of the path expression. // TupleExpr clone = pathExpression.clone(); // // VarReplacer replacer = new VarReplacer(endVar, nextVar); // clone.visit(replacer); // // gp.addRequiredTE(clone); // // subjVar = nextVar; // } // return gp.buildTupleExpr(); // } // } // } // // protected class VarCollector extends QueryModelVisitorBase<VisitorException> { // // private final Set<Var> collectedVars = new HashSet<Var>(); // // @Override // public void meet(Var var) { // collectedVars.add(var); // } // // /** // * @return Returns the collectedVars. // */ // public Set<Var> getCollectedVars() { // return collectedVars; // } // // } // // private class VarReplacer extends QueryModelVisitorBase<VisitorException> { // // private Var toBeReplaced; // // private Var replacement; // // public VarReplacer(Var toBeReplaced, Var replacement) { // this.toBeReplaced = toBeReplaced; // this.replacement = replacement; // } // // @Override // public void meet(Var var) { // if (toBeReplaced.equals(var)) { // QueryModelNode parent = var.getParentNode(); // parent.replaceChildNode(var, replacement); // replacement.setParentNode(parent); // } // } // } // // private class SubSelectAlphaConvertor extends QueryModelVisitorBase<VisitorException> { // // private Var toBeReplaced; // // private Var replacement; // // private Projection projection; // // public SubSelectAlphaConvertor(Projection projection, Var toBeReplaced, Var replacement) { // this.projection = projection; // this.toBeReplaced = toBeReplaced; // this.replacement = replacement; // } // // public void meet(Projection projection) // throws VisitorException // { // if (projection.getBindingNames().contains(toBeReplaced.getName())) { // super.meet(projection); // } // } // // public void meet(ProjectionElem elem) // throws VisitorException // { // if (elem.getSourceName().equals(toBeReplaced.getName())) { // elem.setSourceName(replacement.getName()); // elem.setTargetName(replacement.getName()); // } // super.meet(elem); // } // // public void meet(ExtensionElem elem) // throws VisitorException // { // if (elem.getName().equals(toBeReplaced.getName())) { // elem.setName(replacement.getName()); // } // super.meet(elem); // } // // @Override // public void meet(Group group) // throws VisitorException // { // super.meet(group); // // String name = toBeReplaced.getName(); // // Set<String> groupBindings = group.getGroupBindingNames(); // Set<String> newGroupBindings = new HashSet<String>(groupBindings); // // if (groupBindings.contains(name)) { // newGroupBindings.remove(name); // newGroupBindings.add(replacement.getName()); // // group.setGroupBindingNames(newGroupBindings); // } // } // // @Override // public void meet(StatementPattern sp) // throws VisitorException // { // if (toBeReplaced.equals(sp.getContextVar())) { // if (projection.getProjectionContext() != null // && projection.getProjectionContext().equals(sp.getContextVar())) // { // Var newSpContextVar = createAnonVar(sp.getContextVar().getName() // + UUID.randomUUID().toString()); // sp.setContextVar(newSpContextVar); // } // } // super.meet(sp); // } // // @Override // public void meet(Var var) { // if (toBeReplaced.equals(var)) { // QueryModelNode parent = var.getParentNode(); // // parent.replaceChildNode(var, replacement); // replacement.setParentNode(parent); // } // } // } /* * Reification done right. * * @see https://sourceforge.net/apps/trac/bigdata/ticket/526 (Reification * done right) */ /** * This is invoked in two different contexts. One for a bare triple * reference pattern: * * <pre> * <<a b c>> d e * </pre> * * and the other for a BIND() of a triple reference pattern onto a statement * identifier variable: * * <pre> * BIND(<<a b c>> as ?sidVar) * </pre> * * In both cases we translate the triple reference pattern into a new * {@link StatementPatternNode}. We reach back to the parent to decide which * of the two invocation contexts applies and either generate a new SID * variable or use the one from the BIND(). Then we set the SID variable on * the {@link StatementPatternNode} and add it to the current graph pattern * group. * * <p> * If either the subject or object position in the triple reference pattern * is a triple reference pattern, then it will have been turned into a * {@link StatementPatternNode} by recursion through this method and we * replace it with the SID variable which was assigned to that * {@link StatementPatternNode}. * * @return The SID variable. This allows the existing code paths to * handle nested triple reference patterns without change. */ public TermNode visit(final ASTTRefPattern node, final Object data) throws VisitorException { /* * Accept and convert. */ final TermNode s = (TermNode) node.jjtGetChild(0).jjtAccept(this, data); final TermNode p = (TermNode) node.jjtGetChild(1).jjtAccept(this, data); final TermNode o = (TermNode) node.jjtGetChild(2).jjtAccept(this, data); /* * Check constraints. */ Resource cs = null; URI cp = null; Value co = null; if(s instanceof ConstantNode) { final BigdataValue v = ((ConstantNode)s).getValue(); if (v instanceof Literal) { throw new VisitorException( "Subject in triple reference pattern may not be literal."); } else { cs = (Resource) v; } } else { if (((VarNode) s).isAnonymous()) { /* * Blank nodes have already been translated into variables. * * TODO We could run into trouble here if there anonymous * variables are introduced for anything other than a blank node * (and there are). We need to explicitly mark anonymous * variables which correspond to blank nodes in the original * query and then test for isBlankNode() here. */ throw new VisitorException( "Subject in triple reference pattern may not be blank node."); } } if (p instanceof ConstantNode) { final BigdataValue v = ((ConstantNode) p).getValue(); if (!(v instanceof URI)) { throw new VisitorException( "Predicate in triple reference pattern must be IRI."); } else { cp = (URI) v; } } if (o instanceof ConstantNode) { final BigdataValue v = ((ConstantNode) o).getValue(); // See https://jira.blazegraph.com/browse/BLZG-1229 // To support SPARQL* syntax in CONSTRUCT clauses with nested TRef values, // BNode should be allowed as an object value of the nesting node // if (v instanceof BNode) { // // throw new VisitorException( // "Object in triple reference pattern may not be blank node."); // // } co = v; } else { if (((VarNode) o).isAnonymous()) { /* * Blank nodes have already been translated into variables. */ throw new VisitorException( "Object in triple reference pattern may not be blank node."); } } /* * Note: The caller will associate the SID variable with this * StatementPatternNode if it appears in a BIND(<<...>> AS ?sid) * construction. That will overwrite the variable which we assign here. * * @see GroupGraphPatternBuilder#visit(ASTBind,Object) */ final StatementPatternNode sp = new StatementPatternNode(s, p, o, graphPattern.getContext(), graphPattern.getStatementPatternScope()); final Node parent = node.jjtGetParent(); final VarNode sidVar; if(parent instanceof ASTBind) { final ASTBind bind = (ASTBind) parent; final Node aliasNode = bind.jjtGetChild(1); final String alias = ((ASTVar) aliasNode).getName(); // Use the specified variable. sidVar = new VarNode(alias); // sidVar.setSID(true); } else { // Create a new variable. sidVar = context.createSidVar(); } // Set the SID variable on the SP. sp.setSid(sidVar); if (isInConstructClause(parent)) { // @see https://jira.blazegraph.com/browse/BLZG-1229 // in construct clause we should keep TRef as is if (cs == null || cp == null || co == null) { throw new VisitorException( "Invalid TRef: (" + cs + "," + cp + "," + co + ")"); } BigdataStatement stmt = context.valueFactory.createStatement(cs, cp, co); BigdataBNode val = context.valueFactory.createBNode(stmt); SidIV<BigdataBNode> iv = new SidIV<BigdataBNode>(stmt); val.setIV(iv); iv.setValue(val); return new ConstantNode(iv); } // add to the current join group. graphPattern.addSP(sp); // log.error("node:=" + node + " => " + sp); // Return the SID variable. return sidVar; } /** * @param x the node to be tested * @return true if provide node is ASTConstruct or has ASTConstruct in any of transitive parents. */ private boolean isInConstructClause(Node x) { if (x == null) { return false; } else if (x instanceof ASTConstruct) { return true; } else { return isInConstructClause(x.jjtGetParent()); } } }