/*
* Copyright Aduna (http://www.aduna-software.com/) (c) 2007.
*
* Licensed under the Aduna BSD-style license.
*/
package org.openrdf.query.parser.serql;
import static org.openrdf.query.algebra.Compare.CompareOp.EQ;
import static org.openrdf.query.algebra.Compare.CompareOp.NE;
import java.util.Iterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.algebra.Compare.CompareOp;
import org.openrdf.query.parser.serql.ast.ASTBooleanConstant;
import org.openrdf.query.parser.serql.ast.ASTBooleanExpr;
import org.openrdf.query.parser.serql.ast.ASTBound;
import org.openrdf.query.parser.serql.ast.ASTCompare;
import org.openrdf.query.parser.serql.ast.ASTNot;
import org.openrdf.query.parser.serql.ast.ASTNull;
import org.openrdf.query.parser.serql.ast.ASTProjectionElem;
import org.openrdf.query.parser.serql.ast.ASTQueryContainer;
import org.openrdf.query.parser.serql.ast.ASTSelect;
import org.openrdf.query.parser.serql.ast.ASTValueExpr;
import org.openrdf.query.parser.serql.ast.ASTVar;
import org.openrdf.query.parser.serql.ast.Node;
import org.openrdf.query.parser.serql.ast.VisitorException;
/**
* Processes {@link ASTNull} nodes in query models. Null's that appear in
* projections are simply removed as that doesn't change the semantics. Null's
* that appear in value comparisons are either replaced with {@link ASTBound}
* nodes or constants.
*
* @author Arjohn Kampman
*/
class NullProcessor {
/**
* Processes escape sequences in ASTString objects.
*
* @param qc
* The query that needs to be processed.
* @throws MalformedQueryException
* If an invalid escape sequence was found.
*/
public static void process(ASTQueryContainer qc)
throws MalformedQueryException
{
NullVisitor visitor = new NullVisitor();
try {
qc.jjtAccept(visitor, null);
}
catch (VisitorException e) {
throw new MalformedQueryException(e.getMessage(), e);
}
}
private static class NullVisitor extends ASTVisitorBase {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
public NullVisitor() {
}
@Override
public Object visit(ASTSelect selectNode, Object data)
throws VisitorException
{
Iterator<Node> iter = selectNode.jjtGetChildren().iterator();
while (iter.hasNext()) {
ASTProjectionElem pe = (ASTProjectionElem)iter.next();
if (pe.getValueExpr() instanceof ASTNull) {
logger.warn("Use of NULL values in SeRQL queries has been deprecated");
iter.remove();
}
}
return null;
}
@Override
public Object visit(ASTCompare compareNode, Object data)
throws VisitorException
{
boolean leftIsNull = compareNode.getLeftOperand() instanceof ASTNull;
boolean rightIsNull = compareNode.getRightOperand() instanceof ASTNull;
CompareOp operator = compareNode.getOperator().getValue();
if (leftIsNull && rightIsNull) {
switch (operator) {
case EQ:
logger.warn("Use of NULL values in SeRQL queries has been deprecated, use BOUND(...) instead");
compareNode.jjtReplaceWith(new ASTBooleanConstant(true));
break;
case NE:
logger.warn("Use of NULL values in SeRQL queries has been deprecated, use BOUND(...) instead");
compareNode.jjtReplaceWith(new ASTBooleanConstant(false));
break;
default:
throw new VisitorException(
"Use of NULL values in SeRQL queries has been deprecated, use BOUND(...) instead");
}
}
else if (leftIsNull || rightIsNull) {
ASTValueExpr valueOperand;
if (leftIsNull) {
valueOperand = compareNode.getRightOperand();
}
else {
valueOperand = compareNode.getLeftOperand();
}
if (valueOperand instanceof ASTVar && operator == EQ || operator == NE) {
ASTBooleanExpr replacementNode = new ASTBound(valueOperand);
if (operator == EQ) {
replacementNode = new ASTNot(replacementNode);
}
compareNode.jjtReplaceWith(replacementNode);
return null;
}
throw new VisitorException(
"Use of NULL values in SeRQL queries has been deprecated, use BOUND(...) instead");
}
return null;
}
@Override
public Object visit(ASTNull nullNode, Object data)
throws VisitorException
{
throw new VisitorException(
"Use of NULL values in SeRQL queries has been deprecated, use BOUND(...) instead");
}
}
}