com.bigdata.rdf.sail.sparql.ValueExprBuilder Maven / Gradle / Ivy
/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
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
*/
/* Portions of this code are:
*
* Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2007.
*
* Licensed under the Aduna BSD-style license.
*/
/*
* Created on Aug 21, 2011
*/
package com.bigdata.rdf.sail.sparql;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.openrdf.model.URI;
import org.openrdf.model.vocabulary.FN;
import com.bigdata.bop.aggregate.AggregateBase;
import com.bigdata.bop.rdf.aggregate.GROUP_CONCAT;
import com.bigdata.rdf.internal.constraints.IriBOp;
import com.bigdata.rdf.model.BigdataURI;
import com.bigdata.rdf.sail.sparql.ast.ASTAbs;
import com.bigdata.rdf.sail.sparql.ast.ASTAggregate;
import com.bigdata.rdf.sail.sparql.ast.ASTAnd;
import com.bigdata.rdf.sail.sparql.ast.ASTAvg;
import com.bigdata.rdf.sail.sparql.ast.ASTBNodeFunc;
import com.bigdata.rdf.sail.sparql.ast.ASTBound;
import com.bigdata.rdf.sail.sparql.ast.ASTCeil;
import com.bigdata.rdf.sail.sparql.ast.ASTCoalesce;
import com.bigdata.rdf.sail.sparql.ast.ASTCompare;
import com.bigdata.rdf.sail.sparql.ast.ASTConcat;
import com.bigdata.rdf.sail.sparql.ast.ASTContains;
import com.bigdata.rdf.sail.sparql.ast.ASTCount;
import com.bigdata.rdf.sail.sparql.ast.ASTDatatype;
import com.bigdata.rdf.sail.sparql.ast.ASTDay;
import com.bigdata.rdf.sail.sparql.ast.ASTEncodeForURI;
import com.bigdata.rdf.sail.sparql.ast.ASTExistsFunc;
import com.bigdata.rdf.sail.sparql.ast.ASTFloor;
import com.bigdata.rdf.sail.sparql.ast.ASTFunctionCall;
import com.bigdata.rdf.sail.sparql.ast.ASTGroupConcat;
import com.bigdata.rdf.sail.sparql.ast.ASTGroupCondition;
import com.bigdata.rdf.sail.sparql.ast.ASTHours;
import com.bigdata.rdf.sail.sparql.ast.ASTIRIFunc;
import com.bigdata.rdf.sail.sparql.ast.ASTIf;
import com.bigdata.rdf.sail.sparql.ast.ASTIn;
import com.bigdata.rdf.sail.sparql.ast.ASTInfix;
import com.bigdata.rdf.sail.sparql.ast.ASTIsBlank;
import com.bigdata.rdf.sail.sparql.ast.ASTIsIRI;
import com.bigdata.rdf.sail.sparql.ast.ASTIsLiteral;
import com.bigdata.rdf.sail.sparql.ast.ASTIsNumeric;
import com.bigdata.rdf.sail.sparql.ast.ASTLang;
import com.bigdata.rdf.sail.sparql.ast.ASTLangMatches;
import com.bigdata.rdf.sail.sparql.ast.ASTLowerCase;
import com.bigdata.rdf.sail.sparql.ast.ASTMD5;
import com.bigdata.rdf.sail.sparql.ast.ASTMath;
import com.bigdata.rdf.sail.sparql.ast.ASTMax;
import com.bigdata.rdf.sail.sparql.ast.ASTMin;
import com.bigdata.rdf.sail.sparql.ast.ASTMinutes;
import com.bigdata.rdf.sail.sparql.ast.ASTMonth;
import com.bigdata.rdf.sail.sparql.ast.ASTNot;
import com.bigdata.rdf.sail.sparql.ast.ASTNotExistsFunc;
import com.bigdata.rdf.sail.sparql.ast.ASTNotIn;
import com.bigdata.rdf.sail.sparql.ast.ASTNow;
import com.bigdata.rdf.sail.sparql.ast.ASTOr;
import com.bigdata.rdf.sail.sparql.ast.ASTRand;
import com.bigdata.rdf.sail.sparql.ast.ASTRegexExpression;
import com.bigdata.rdf.sail.sparql.ast.ASTReplace;
import com.bigdata.rdf.sail.sparql.ast.ASTRound;
import com.bigdata.rdf.sail.sparql.ast.ASTSHA1;
import com.bigdata.rdf.sail.sparql.ast.ASTSHA224;
import com.bigdata.rdf.sail.sparql.ast.ASTSHA256;
import com.bigdata.rdf.sail.sparql.ast.ASTSHA384;
import com.bigdata.rdf.sail.sparql.ast.ASTSHA512;
import com.bigdata.rdf.sail.sparql.ast.ASTSTRUUID;
import com.bigdata.rdf.sail.sparql.ast.ASTSameTerm;
import com.bigdata.rdf.sail.sparql.ast.ASTSample;
import com.bigdata.rdf.sail.sparql.ast.ASTSeconds;
import com.bigdata.rdf.sail.sparql.ast.ASTStr;
import com.bigdata.rdf.sail.sparql.ast.ASTStrAfter;
import com.bigdata.rdf.sail.sparql.ast.ASTStrBefore;
import com.bigdata.rdf.sail.sparql.ast.ASTStrDt;
import com.bigdata.rdf.sail.sparql.ast.ASTStrEnds;
import com.bigdata.rdf.sail.sparql.ast.ASTStrLang;
import com.bigdata.rdf.sail.sparql.ast.ASTStrLen;
import com.bigdata.rdf.sail.sparql.ast.ASTStrStarts;
import com.bigdata.rdf.sail.sparql.ast.ASTSubstr;
import com.bigdata.rdf.sail.sparql.ast.ASTSum;
import com.bigdata.rdf.sail.sparql.ast.ASTTimezone;
import com.bigdata.rdf.sail.sparql.ast.ASTTz;
import com.bigdata.rdf.sail.sparql.ast.ASTUUID;
import com.bigdata.rdf.sail.sparql.ast.ASTUpperCase;
import com.bigdata.rdf.sail.sparql.ast.ASTYear;
import com.bigdata.rdf.sail.sparql.ast.Node;
import com.bigdata.rdf.sail.sparql.ast.SimpleNode;
import com.bigdata.rdf.sail.sparql.ast.VisitorException;
import com.bigdata.rdf.sparql.ast.AssignmentNode;
import com.bigdata.rdf.sparql.ast.ConstantNode;
import com.bigdata.rdf.sparql.ast.ExistsNode;
import com.bigdata.rdf.sparql.ast.FunctionNode;
import com.bigdata.rdf.sparql.ast.FunctionRegistry;
import com.bigdata.rdf.sparql.ast.GraphPatternGroup;
import com.bigdata.rdf.sparql.ast.IGroupMemberNode;
import com.bigdata.rdf.sparql.ast.IValueExpressionNode;
import com.bigdata.rdf.sparql.ast.NotExistsNode;
import com.bigdata.rdf.sparql.ast.ValueExpressionNode;
import com.bigdata.rdf.sparql.ast.VarNode;
/**
* Visitor pattern builds {@link IValueExpressionNode}s.
*
* @author Bryan Thompson
* @version $Id$
*
* @see FunctionRegistry
* @openrdf
*/
public class ValueExprBuilder extends BigdataASTVisitorBase {
// private static final Logger log = Logger.getLogger(ValueExprBuilder.class);
/**
* Used to manage collection and nesting of graph patterns. This is mostly
* handled by {@link GroupGraphPatternBuilder}.
*
* Note: Both {@link ASTExistsFunc} and {@link ASTNotExistsFunc} have an
* inner graph pattern by they appear within value expressions.
*/
protected GroupGraphPattern graphPattern;
public ValueExprBuilder(final BigdataASTContext context) {
super(context);
}
protected ValueExpressionNode left(final SimpleNode node)
throws VisitorException {
return (ValueExpressionNode) node.jjtGetChild(0).jjtAccept(this, null);
}
protected ValueExpressionNode right(final SimpleNode node)
throws VisitorException {
return (ValueExpressionNode) node.jjtGetChild(1).jjtAccept(this, null);
}
/**
* Handle a simple function without any arguments.
*/
protected FunctionNode noneary(final SimpleNode node, final URI functionURI)
throws VisitorException {
return new FunctionNode(functionURI,
null/* scalarValues */, new ValueExpressionNode[] {});
}
/**
* Handle a simple unary function (the child of the node is the argument to
* the function).
*/
protected FunctionNode unary(final SimpleNode node, final URI functionURI)
throws VisitorException {
return new FunctionNode(functionURI,
null/* scalarValues */,
new ValueExpressionNode[] { left(node) });
}
/**
* Handle a simple binary function (both children of the node are arguments
* to the function).
*/
protected FunctionNode binary(final SimpleNode node, final URI functionURI)
throws VisitorException {
return new FunctionNode(functionURI,
null/* scalarValues */, new ValueExpressionNode[] { left(node),
right(node) });
}
/**
* Handle a simple ternary function (there are three children of the node
* which are the arguments to the function).
*/
protected FunctionNode ternary(final SimpleNode node, final URI functionURI)
throws VisitorException {
return new FunctionNode(functionURI,
null/* scalarValues */, new ValueExpressionNode[] {
left(node),
right(node),
(ValueExpressionNode) node.jjtGetChild(2).jjtAccept(
this, null) });
}
/**
* Handle a function with four arguments (there are four children of the
* node which are the arguments to the function).
*/
protected FunctionNode quadary(final SimpleNode node, final URI functionURI)
throws VisitorException {
return new FunctionNode(functionURI,
null/* scalarValues */, new ValueExpressionNode[] {
left(node),
right(node),
(ValueExpressionNode) node.jjtGetChild(2).jjtAccept(
this, null),
(ValueExpressionNode) node.jjtGetChild(3).jjtAccept(
this, null)});
}
/**
* Handle a simple nary function (all children of the node are arguments to
* the function).
*/
protected FunctionNode nary(final SimpleNode node, final URI functionURI)
throws VisitorException {
final int nargs = node.jjtGetNumChildren();
final ValueExpressionNode[] args = new ValueExpressionNode[nargs];
for (int i = 0; i < nargs; i++) {
final Node argNode = node.jjtGetChild(i);
args[i] = (ValueExpressionNode) argNode.jjtAccept(this, null);
}
if (functionURI.equals(FunctionRegistry.COALESCE)) {
return new FunctionNode(FunctionRegistry.COALESCE,
null/* scalarValues */, args);
} else if (functionURI.equals(FN.SUBSTRING)) {
return new FunctionNode(FunctionRegistry.SUBSTR,
null/* scalarValues */, args);
} else if (functionURI.equals(FN.CONCAT)) {
return new FunctionNode(FunctionRegistry.CONCAT,
null/* scalarValues */, args);
} else {
throw new IllegalArgumentException();
}
}
protected FunctionNode aggregate(final ASTAggregate node,
final URI functionURI) throws VisitorException {
Map scalarValues = null;
if (node.isDistinct()) {
scalarValues = Collections.singletonMap(
AggregateBase.Annotations.DISTINCT, (Object) Boolean.TRUE);
}
if (node instanceof ASTCount && ((ASTCount) node).isWildcard()) {
/*
* Note: The wildcard is dropped by openrdf.
*/
return new FunctionNode(functionURI, scalarValues,
new ValueExpressionNode[] { new VarNode("*") });
}
return new FunctionNode(functionURI, scalarValues,
new ValueExpressionNode[] { left(node) });
}
//
//
//
// @Override
// final public FunctionNode visit(ASTBind node, Object data) throws VisitorException {
// return new AssignmentNode((VarNode) right(node), left(node));
// }
@Override
final public FunctionNode visit(ASTOr node, Object data) throws VisitorException {
return binary(node, FunctionRegistry.OR);
}
@Override
final public FunctionNode visit(ASTAnd node, Object data) throws VisitorException {
return binary(node, FunctionRegistry.AND);
}
@Override
final public FunctionNode visit(ASTNot node, Object data) throws VisitorException {
return unary(node, FunctionRegistry.NOT);
}
@Override
final public FunctionNode visit(ASTCoalesce node, Object data)
throws VisitorException {
return nary(node, FunctionRegistry.COALESCE);
}
@Override
final public FunctionNode visit(ASTCompare node, Object data)
throws VisitorException
{
final URI functionURI;
switch (node.getOperator()) {
case LT:
functionURI = FunctionRegistry.LT;
break;
case GT:
functionURI = FunctionRegistry.GT;
break;
case EQ:
functionURI = FunctionRegistry.EQ;
break;
case LE:
functionURI = FunctionRegistry.LE;
break;
case GE:
functionURI = FunctionRegistry.GE;
break;
case NE:
functionURI = FunctionRegistry.NE;
break;
default:
throw new UnsupportedOperationException(node.getOperator()
.getSymbol());
}
return binary(node, functionURI);
}
@Override
final public FunctionNode visit(ASTSubstr node, Object data)
throws VisitorException {
return nary(node, FN.SUBSTRING);
}
@Override
final public FunctionNode visit(ASTConcat node, Object data)
throws VisitorException {
return nary(node, FN.CONCAT);
}
@Override
final public FunctionNode visit(ASTAbs node, Object data) throws VisitorException {
return unary(node, FN.NUMERIC_ABS);
}
@Override
final public FunctionNode visit(ASTCeil node, Object data)
throws VisitorException {
return unary(node, FN.NUMERIC_CEIL);
}
@Override
final public FunctionNode visit(ASTContains node, Object data)
throws VisitorException {
return binary(node, FN.CONTAINS);
}
@Override
final public FunctionNode visit(ASTFloor node, Object data)
throws VisitorException {
return unary(node, FN.NUMERIC_FLOOR);
}
@Override
final public FunctionNode visit(ASTRound node, Object data)
throws VisitorException {
return unary(node, FN.NUMERIC_ROUND);
}
@Override
final public FunctionNode visit(ASTRand node, Object data)
throws VisitorException {
return noneary(node, FunctionRegistry.RAND);
}
@Override
final public FunctionNode visit(ASTSameTerm node, Object data)
throws VisitorException {
return binary(node, FunctionRegistry.SAME_TERM);
}
@Override
final public FunctionNode visit(ASTMath node, Object data)
throws VisitorException
{
final URI functionURI;
switch (node.getOperator()) {
case PLUS:
functionURI = FunctionRegistry.ADD;
break;
case MINUS:
functionURI = FunctionRegistry.SUBTRACT;
break;
case MULTIPLY:
functionURI = FunctionRegistry.MULTIPLY;
break;
case DIVIDE:
functionURI = FunctionRegistry.DIVIDE;
break;
default:
throw new UnsupportedOperationException(node.getOperator()
.getSymbol());
}
return binary(node, functionURI);
}
/** ASTFunctionCall (IRIRef, ArgList). */
@Override
final public FunctionNode visit(ASTFunctionCall node, Object data)
throws VisitorException
{
final ConstantNode uriNode = (ConstantNode) node.jjtGetChild(0)
.jjtAccept(this, null);
final BigdataURI functionURI = (BigdataURI) uriNode.getValue();
final int nargs = node.jjtGetNumChildren() - 1;
final ValueExpressionNode[] args = new ValueExpressionNode[nargs];
for (int i = 0; i < nargs; i++) {
final Node argNode = node.jjtGetChild(i + 1);
args[i] = (ValueExpressionNode) argNode.jjtAccept(this, null);
}
return new FunctionNode(functionURI,
null/* scalarValues */, args);
}
@Override
final public FunctionNode visit(ASTEncodeForURI node, Object data)
throws VisitorException
{
return unary(node, FN.ENCODE_FOR_URI);
}
@Override
final public FunctionNode visit(ASTStr node, Object data) throws VisitorException {
return unary(node, FunctionRegistry.STR);
}
@Override
final public FunctionNode visit(ASTStrDt node, Object data)
throws VisitorException {
return binary(node, FunctionRegistry.STR_DT);
}
@Override
final public FunctionNode visit(ASTStrStarts node, Object data)
throws VisitorException {
return binary(node, FN.STARTS_WITH);
}
@Override
final public FunctionNode visit(ASTStrEnds node, Object data)
throws VisitorException {
return binary(node, FN.ENDS_WITH);
}
@Override
final public FunctionNode visit(ASTStrLen node, Object data)
throws VisitorException {
return unary(node, FN.STRING_LENGTH);
}
@Override
final public FunctionNode visit(ASTStrAfter node, Object data)
throws VisitorException
{
return binary(node, FunctionRegistry.STR_AFTER);
// return createFunctionCall(FN.SUBSTRING_AFTER.toString(), node, 2, 2);
}
@Override
final public FunctionNode visit(ASTStrBefore node, Object data)
throws VisitorException
{
return binary(node, FunctionRegistry.STR_BEFORE);
// return createFunctionCall(FN.SUBSTRING_BEFORE.toString(), node, 2, 2);
}
@Override
final public FunctionNode visit(ASTUpperCase node, Object data)
throws VisitorException {
return unary(node, FN.UPPER_CASE);
}
@Override
final public FunctionNode visit(ASTLowerCase node, Object data)
throws VisitorException {
return unary(node, FN.LOWER_CASE);
}
@Override
final public FunctionNode visit(ASTStrLang node, Object data)
throws VisitorException {
return binary(node, FunctionRegistry.STR_LANG);
}
@Override
final public FunctionNode visit(ASTNow node, Object data) throws VisitorException {
return noneary(node, FunctionRegistry.NOW);
}
@Override
final public FunctionNode visit(ASTYear node, Object data)
throws VisitorException {
return unary(node, FN.YEAR_FROM_DATETIME);
}
@Override
final public FunctionNode visit(ASTMonth node, Object data)
throws VisitorException {
return unary(node, FN.MONTH_FROM_DATETIME);
}
@Override
final public FunctionNode visit(ASTDay node, Object data) throws VisitorException {
return unary(node, FN.DAY_FROM_DATETIME);
}
@Override
final public FunctionNode visit(ASTHours node, Object data)
throws VisitorException {
return unary(node, FN.HOURS_FROM_DATETIME);
}
@Override
final public FunctionNode visit(ASTMinutes node, Object data)
throws VisitorException {
return unary(node, FN.MINUTES_FROM_DATETIME);
}
@Override
final public FunctionNode visit(ASTSeconds node, Object data)
throws VisitorException {
return unary(node, FN.SECONDS_FROM_DATETIME);
}
@Override
final public FunctionNode visit(ASTTimezone node, Object data)
throws VisitorException {
return unary(node, FunctionRegistry.TIMEZONE);
}
@Override
final public FunctionNode visit(ASTTz node, Object data) throws VisitorException {
return unary(node, FunctionRegistry.TZ);
}
@Override
final public FunctionNode visit(ASTMD5 node, Object data) throws VisitorException {
return unary(node, FunctionRegistry.MD5);
}
@Override
final public FunctionNode visit(ASTSHA1 node, Object data)
throws VisitorException {
return unary(node, FunctionRegistry.SHA1);
}
@Override
final public FunctionNode visit(ASTSHA224 node, Object data)
throws VisitorException
{
return unary(node, FunctionRegistry.SHA224);
}
@Override
final public FunctionNode visit(ASTSHA256 node, Object data)
throws VisitorException {
return unary(node, FunctionRegistry.SHA256);
}
@Override
final public FunctionNode visit(ASTSHA384 node, Object data)
throws VisitorException {
return unary(node, FunctionRegistry.SHA384);
}
@Override
final public FunctionNode visit(ASTSHA512 node, Object data)
throws VisitorException {
return unary(node, FunctionRegistry.SHA512);
}
@Override
public FunctionNode visit(ASTUUID node, Object data)
throws VisitorException {
return noneary(node, FunctionRegistry.UUID);
}
@Override
public FunctionNode visit(ASTSTRUUID node, Object data)
throws VisitorException {
return noneary(node, FunctionRegistry.STRUUID);
}
@Override
final public FunctionNode visit(ASTIRIFunc node, Object data)
throws VisitorException {
return new FunctionNode(FunctionRegistry.IRI,
// null/* scalarValues */,
Collections.singletonMap(IriBOp.Annotations.BASE_URI, (Object) node.getBaseURI()),
new ValueExpressionNode[] { left(node) });
// return unary(node, FunctionRegistry.IRI);
/*
* FIXME baseURI resolution needs to be handled for IRI functions, most
* likely in unary(), binary(), nary(), and aggregate(). Write AST
* layer unit tests for this.
*/
// ValueExpr expr = (ValueExpr)node.jjtGetChild(0).jjtAccept(this, null);
// IRIFunction fn = new IRIFunction(expr);
// fn.setBaseURI(node.getBaseURI());
// return fn;
}
@Override
final public FunctionNode visit(ASTLang node, Object data) throws VisitorException {
return unary(node, FunctionRegistry.LANG);
}
@Override
final public FunctionNode visit(ASTDatatype node, Object data)
throws VisitorException {
return unary(node, FunctionRegistry.DATATYPE);
}
@Override
final public FunctionNode visit(ASTLangMatches node, Object data)
throws VisitorException {
return binary(node, FunctionRegistry.LANG_MATCHES);
}
@Override
final public FunctionNode visit(ASTBound node, Object data) throws VisitorException {
return unary(node, FunctionRegistry.BOUND);
}
@Override
final public FunctionNode visit(ASTIsIRI node, Object data)
throws VisitorException {
return unary(node, FunctionRegistry.IS_IRI);
}
@Override
final public FunctionNode visit(ASTIsBlank node, Object data)
throws VisitorException {
return unary(node, FunctionRegistry.IS_BLANK);
}
@Override
final public FunctionNode visit(ASTIsLiteral node, Object data)
throws VisitorException {
return unary(node, FunctionRegistry.IS_LITERAL);
}
@Override
final public FunctionNode visit(ASTIsNumeric node, Object data)
throws VisitorException {
return unary(node, FunctionRegistry.IS_NUMERIC);
}
/** TODO Same functionURI for BNode() and BNode(Literal)? */
@Override
final public FunctionNode visit(ASTBNodeFunc node, Object data)
throws VisitorException {
if (node.jjtGetNumChildren() == 0) {
return noneary(node, FunctionRegistry.BNODE);
}
return unary(node, FunctionRegistry.BNODE);
}
@Override
final public FunctionNode visit(ASTRegexExpression node, Object data)
throws VisitorException {
if (node.jjtGetNumChildren() == 2) {
return binary(node, FunctionRegistry.REGEX);
}
return ternary(node, FunctionRegistry.REGEX);
}
@Override
public FunctionNode visit(ASTReplace node, Object data)
throws VisitorException
{
if (node.jjtGetNumChildren() == 3) {
return ternary(node, FunctionRegistry.REPLACE);
}
return quadary(node, FunctionRegistry.REPLACE);
// return createFunctionCall(FN.REPLACE.toString(), node, 3, 4);
}
/**
* Note: EXISTS is basically an ASK subquery.
*
* @see ExistsNode
*/
@Override
final public ExistsNode visit(final ASTExistsFunc node, Object data)
throws VisitorException {
final VarNode anonvar = context.createAnonVar("-exists-");
/*
* Use a new (empty) graph pattern to prevent the accept of the child
* from being attached into the parent's graph pattern context.
*/
final GroupGraphPattern parentGP = graphPattern;
graphPattern = scopedGroupGraphPattern(node);
@SuppressWarnings("unchecked")
final GraphPatternGroup innerGraphPattern = (GraphPatternGroup) node
.jjtGetChild(0).jjtAccept(this/* visitor */, null);
final ExistsNode fn = new ExistsNode(anonvar, innerGraphPattern);
// Restore the parent's context.
graphPattern = parentGP;
return fn;
}
/**
* See EXISTS above.
*
* @see NotExistsNode
*/
@Override
final public NotExistsNode visit(final ASTNotExistsFunc node, Object data)
throws VisitorException {
final VarNode anonvar = context.createAnonVar("-exists-");
/*
* Use a new (empty) graph pattern to prevent the accept of the child
* from being attached into the parent's graph pattern context.
*/
final GroupGraphPattern parentGP = graphPattern;
graphPattern = scopedGroupGraphPattern(node);
@SuppressWarnings("unchecked")
final GraphPatternGroup innerGraphPattern = (GraphPatternGroup) node
.jjtGetChild(0).jjtAccept(this/* visitor */, null);
final NotExistsNode fn = new NotExistsNode(anonvar, innerGraphPattern);
// Restore the parent's context.
graphPattern = parentGP;
return fn;
}
@Override
final public FunctionNode visit(ASTIf node, Object data) throws VisitorException {
return ternary(node, FunctionRegistry.IF);
}
/**
* Unwrap an {@link ASTInfix} node, returning the inner {@link FunctionNode}
* constructed for it.
*
* Note: Sesame has picked up the notion of an ASTInfix node, but they are
* handling it slightly differently.
*
* @see http://www.openrdf.org/issues/browse/SES-818
*/
@Override
final public FunctionNode visit(ASTInfix node, Object data) throws VisitorException {
final Node left = node.jjtGetChild(0);
final Node op = node.jjtGetChild(1);
op.jjtInsertChild(left, 0);
return (FunctionNode) op.jjtAccept(this, data);
}
/**
* "IN" and "NOT IN" are infix notation operators. The syntax for "IN" is
* [NumericExpression IN ArgList]. However, the IN operators declared by the
* {@link FunctionRegistry} require that the outer NumericExpression is
* their first argument. The function registry optimizes several different
* cases, including where the set is empty, where it has one member, and
* where the members of the set are constants and the outer expression is a
* variable.
*
* @see FunctionRegistry#IN
* @see FunctionRegistry#NOT_IN
*
* @see http://www.openrdf.org/issues/browse/SES-818
*/
@Override
final public FunctionNode visit(ASTIn node, Object data)
throws VisitorException
{
// final int nargs = node.jjtGetNumChildren();
//
// final ValueExpressionNode[] args = new ValueExpressionNode[nargs + 1];
//
// /*
// * Reach up to the parent's 1st child for the left argument to the infix
// * IN operator.
// */
// final ValueExpressionNode leftArg = (ValueExpressionNode) node
// .jjtGetParent().jjtGetChild(0).jjtAccept(this, null);
//
// args[0] = leftArg;
//
// /*
// * Handle the ArgList for IN.
// */
//
// for (int i = 0; i < nargs; i++) {
//
// final Node argNode = node.jjtGetChild(i);
//
// args[i + 1] = (ValueExpressionNode) argNode.jjtAccept(this, null);
//
// }
/*
* Handle the ArgList for IN.
*
* Note: This assumes that the left argument of IN has been rotated into
* place as its first child.
*
* @see InfixProcessor
*/
final int nargs = node.jjtGetNumChildren();
final ValueExpressionNode[] args = new ValueExpressionNode[nargs];
for (int i = 0; i < nargs; i++) {
final Node argNode = node.jjtGetChild(i);
args[i] = (ValueExpressionNode) argNode.jjtAccept(this, null);
}
return new FunctionNode(FunctionRegistry.IN,
null/* scalarValues */, args);
}
/**
* See IN above.
*/
@Override
final public FunctionNode visit(ASTNotIn node, Object data)
throws VisitorException {
//
// final int nargs = node.jjtGetNumChildren();
//
// final ValueExpressionNode[] args = new ValueExpressionNode[nargs + 1];
//
// /*
// * Reach up to the parent's 1st child for the left argument to the infix
// * NOT IN operator.
// */
// final ValueExpressionNode leftArg = (ValueExpressionNode) node
// .jjtGetParent().jjtGetChild(0).jjtAccept(this, null);
//
// args[0] = leftArg;
//
// /*
// * Handle the ArgList for NOT IN.
// */
//
// for (int i = 0; i < nargs; i++) {
//
// final Node argNode = node.jjtGetChild(i);
//
// args[i + 1] = (ValueExpressionNode) argNode.jjtAccept(this, null);
//
// }
/*
* Handle the ArgList for NOT_IN.
*
* Note: This assumes that the left argument of the NOT_IN has been
* rotated into place as its first child.
*/
final int nargs = node.jjtGetNumChildren();
final ValueExpressionNode[] args = new ValueExpressionNode[nargs];
for (int i = 0; i < nargs; i++) {
final Node argNode = node.jjtGetChild(i);
args[i] = (ValueExpressionNode) argNode.jjtAccept(this, null);
}
return new FunctionNode(FunctionRegistry.NOT_IN,
null/* scalarValues */, args);
}
/**
* Aggregate value expressions in GROUP BY clause. The content of this
* production can be a {@link VarNode}, {@link AssignmentNode}, or
* {@link FunctionNode} (which handles both built-in functions and extension
* functions). However, we always wrap it as an {@link AssignmentNode} and
* return that. A {@link VarNode} will just bind itself. A bare
* {@link FunctionNode} will bind an anonymous variable.
*/
@Override
final public AssignmentNode visit(final ASTGroupCondition node, Object data)
throws VisitorException {
// The first value-expression.
final IValueExpressionNode ve = (IValueExpressionNode) node.jjtGetChild(0)
.jjtAccept(this, data);
if (node.jjtGetNumChildren() == 2) {
/*
* If there are two value expressions, then BIND(ve,ve2).
*/
final IValueExpressionNode asVar = (IValueExpressionNode) node
.jjtGetChild(1).jjtAccept(this, data);
return new AssignmentNode((VarNode) asVar, ve);
}
if (ve instanceof VarNode) {
// Assign to self.
return new AssignmentNode((VarNode) ve, (VarNode) ve);
}
// Wrap with assignment to an anonymous variable.
return new AssignmentNode(context.createAnonVar("-groupBy-"), ve);
}
/*
* Aggregate functions. Each accepts a scalar boolean "distinct" argument.
* In addition, COUNT may be used with "*" as the inner expression.
*/
@Override
final public FunctionNode visit(ASTCount node, Object data)
throws VisitorException {
return aggregate(node, FunctionRegistry.COUNT);
}
@Override
final public FunctionNode visit(ASTMax node, Object data) throws VisitorException {
return aggregate(node, FunctionRegistry.MAX);
}
@Override
final public FunctionNode visit(ASTMin node, Object data) throws VisitorException {
return aggregate(node, FunctionRegistry.MIN);
}
@Override
final public FunctionNode visit(ASTSum node, Object data) throws VisitorException {
return aggregate(node, FunctionRegistry.SUM);
}
@Override
final public FunctionNode visit(ASTAvg node, Object data) throws VisitorException {
return aggregate(node, FunctionRegistry.AVERAGE);
}
@Override
final public FunctionNode visit(ASTSample node, Object data) throws VisitorException {
return aggregate(node, FunctionRegistry.SAMPLE);
}
/**
* TODO additional scalar values (sparql.jjt specifies "separator EQ" as a
* constant in the grammar, but we support additional scalar values for
* {@link GROUP_CONCAT}. Also, the grammar is permissive and allows
* Expression for separator rather than a quoted string (per the W3C draft).
*/
@Override
final public FunctionNode visit(ASTGroupConcat node, Object data)
throws VisitorException
{
Map scalarValues = new LinkedHashMap();
if (node.isDistinct()) {
scalarValues.put(AggregateBase.Annotations.DISTINCT, Boolean.TRUE);
}
if (node.jjtGetNumChildren() > 1) {
final ConstantNode separator = (ConstantNode) node.jjtGetChild(1)
.jjtAccept(this, data);
scalarValues.put(GROUP_CONCAT.Annotations.SEPARATOR, separator
.getValue().stringValue());
}
return new FunctionNode(FunctionRegistry.GROUP_CONCAT,
scalarValues, new ValueExpressionNode[] { left(node) });
}
}