/*
* Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2007.
* Copyright James Leigh (c) 2006.
*
* Licensed under the Aduna BSD-style license.
*/
package org.openrdf.query.algebra.evaluation.impl;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.openrdf.model.Value;
import org.openrdf.model.impl.BooleanLiteralImpl;
import org.openrdf.query.BindingSet;
import org.openrdf.query.Dataset;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.query.algebra.And;
import org.openrdf.query.algebra.BinaryValueOperator;
import org.openrdf.query.algebra.Bound;
import org.openrdf.query.algebra.FunctionCall;
import org.openrdf.query.algebra.Or;
import org.openrdf.query.algebra.TupleExpr;
import org.openrdf.query.algebra.UnaryValueOperator;
import org.openrdf.query.algebra.ValueConstant;
import org.openrdf.query.algebra.ValueExpr;
import org.openrdf.query.algebra.Var;
import org.openrdf.query.algebra.evaluation.EvaluationStrategy;
import org.openrdf.query.algebra.evaluation.QueryOptimizer;
import org.openrdf.query.algebra.evaluation.ValueExprEvaluationException;
import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
import org.openrdf.query.impl.EmptyBindingSet;
/**
* A query optimizer that optimizes constant value expressions.
*
* @author James Leigh
* @author Arjohn Kampman
*/
public class ConstantOptimizer implements QueryOptimizer {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
protected final EvaluationStrategy strategy;
public ConstantOptimizer(EvaluationStrategy strategy) {
this.strategy = strategy;
}
/**
* Applies generally applicable optimizations to the supplied query: variable
* assignments are inlined.
*
* @param tupleExpr
* @return optimized TupleExpr
* @throws QueryEvaluationException
*/
public void optimize(TupleExpr tupleExpr, Dataset dataset, BindingSet bindings) {
tupleExpr.visit(new ConstantVisitor());
}
protected class ConstantVisitor extends QueryModelVisitorBase<RuntimeException> {
@Override
public void meet(Or or)
{
or.visitChildren(this);
try {
if (isConstant(or.getLeftArg()) && isConstant(or.getRightArg())) {
boolean value = strategy.isTrue(or, EmptyBindingSet.getInstance());
or.replaceWith(new ValueConstant(BooleanLiteralImpl.valueOf(value)));
}
else if (isConstant(or.getLeftArg())) {
boolean leftIsTrue = strategy.isTrue(or.getLeftArg(), EmptyBindingSet.getInstance());
if (leftIsTrue) {
or.replaceWith(new ValueConstant(BooleanLiteralImpl.TRUE));
}
else {
or.replaceWith(or.getRightArg());
}
}
else if (isConstant(or.getRightArg())) {
boolean rightIsTrue = strategy.isTrue(or.getRightArg(), EmptyBindingSet.getInstance());
if (rightIsTrue) {
or.replaceWith(new ValueConstant(BooleanLiteralImpl.TRUE));
}
else {
or.replaceWith(or.getLeftArg());
}
}
}
catch (ValueExprEvaluationException e) {
logger.warn("Failed to evaluate BinaryValueOperator with two constant arguments", e);
}
catch (QueryEvaluationException e) {
logger.error("Query evaluation exception caught", e);
}
}
@Override
public void meet(And and)
{
and.visitChildren(this);
try {
if (isConstant(and.getLeftArg()) && isConstant(and.getRightArg())) {
boolean value = strategy.isTrue(and, EmptyBindingSet.getInstance());
and.replaceWith(new ValueConstant(BooleanLiteralImpl.valueOf(value)));
}
else if (isConstant(and.getLeftArg())) {
boolean leftIsTrue = strategy.isTrue(and.getLeftArg(), EmptyBindingSet.getInstance());
if (leftIsTrue) {
and.replaceWith(and.getRightArg());
}
else {
and.replaceWith(new ValueConstant(BooleanLiteralImpl.FALSE));
}
}
else if (isConstant(and.getRightArg())) {
boolean rightIsTrue = strategy.isTrue(and.getRightArg(), EmptyBindingSet.getInstance());
if (rightIsTrue) {
and.replaceWith(and.getLeftArg());
}
else {
and.replaceWith(new ValueConstant(BooleanLiteralImpl.FALSE));
}
}
}
catch (ValueExprEvaluationException e) {
logger.warn("Failed to evaluate BinaryValueOperator with two constant arguments", e);
}
catch (QueryEvaluationException e) {
logger.error("Query evaluation exception caught", e);
}
}
@Override
protected void meetBinaryValueOperator(BinaryValueOperator binaryValueOp)
{
super.meetBinaryValueOperator(binaryValueOp);
if (isConstant(binaryValueOp.getLeftArg()) && isConstant(binaryValueOp.getRightArg())) {
try {
Value value = strategy.evaluate(binaryValueOp, EmptyBindingSet.getInstance());
binaryValueOp.replaceWith(new ValueConstant(value));
}
catch (ValueExprEvaluationException e) {
logger.warn("Failed to evaluate BinaryValueOperator with two constant arguments", e);
}
catch (QueryEvaluationException e) {
logger.error("Query evaluation exception caught", e);
}
}
}
@Override
protected void meetUnaryValueOperator(UnaryValueOperator unaryValueOp)
{
super.meetUnaryValueOperator(unaryValueOp);
if (isConstant(unaryValueOp.getArg())) {
try {
Value value = strategy.evaluate(unaryValueOp, EmptyBindingSet.getInstance());
unaryValueOp.replaceWith(new ValueConstant(value));
}
catch (ValueExprEvaluationException e) {
logger.warn("Failed to evaluate UnaryValueOperator with a constant argument", e);
}
catch (QueryEvaluationException e) {
logger.error("Query evaluation exception caught", e);
}
}
}
@Override
public void meet(FunctionCall functionCall)
{
super.meet(functionCall);
List<ValueExpr> args = functionCall.getArgs();
for (ValueExpr arg : args) {
if (!isConstant(arg)) {
return;
}
}
// All arguments are constant
try {
Value value = strategy.evaluate(functionCall, EmptyBindingSet.getInstance());
functionCall.replaceWith(new ValueConstant(value));
}
catch (ValueExprEvaluationException e) {
logger.warn("Failed to evaluate BinaryValueOperator with two constant arguments", e);
}
catch (QueryEvaluationException e) {
logger.error("Query evaluation exception caught", e);
}
}
@Override
public void meet(Bound bound)
{
super.meet(bound);
if (bound.getArg().hasValue()) {
// variable is always bound
bound.replaceWith(new ValueConstant(BooleanLiteralImpl.TRUE));
}
}
private boolean isConstant(ValueExpr expr) {
return expr instanceof ValueConstant || expr instanceof Var && ((Var)expr).hasValue();
}
}
}