/* * This file is part of the GeoLatte project. * * GeoLatte is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GeoLatte 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with GeoLatte. If not, see <http://www.gnu.org/licenses/>. * * Copyright (C) 2010 - 2010 and Ownership of code is shared by: * Qmino bvba - Romeinsestraat 18 - 3001 Heverlee (http://www.qmino.com) * Geovise bvba - Generaal Eisenhowerlei 9 - 2140 Antwerpen (http://www.geovise.com) */ package org.geolatte.common.cql; import org.geolatte.common.cql.node.*; import org.geolatte.common.expressions.*; import org.geolatte.common.reflection.EntityClassReader; import org.geolatte.geom.Geometry; import java.util.Date; import java.util.HashMap; /** * <p> * Treewalker that builds an executable {@link org.geolatte.common.expressions.Expression} based on a given CQL AST. Use as follows: * <pre> * {@code * FilterExpressionBuilder builder = new FilterExpressionBuilder(clazz); // builder for the given class * tree.apply(builder); // with tree, the root element of the AST as returned by the parser. * Expression result = builder.getExp(); * } * </pre> * </p> * <p> * <i>Creation-Date</i>: 26-May-2010<br> * <i>Creation-Time</i>: 11:52:36<br> * </p> * * @author Bert Vanhooff * @author <a href="http://www.qmino.com">Qmino bvba</a> * @since SDK1.5 */ class FilterExpressionBuilder extends AbstractBuilder { private Expression<Boolean> exp = null; private EntityClassReader reader; // A map of all translated nodes as they are visited depth first. private HashMap<Node, Expression> translatedExpressions = new HashMap<Node, Expression>(); public FilterExpressionBuilder(Class clazz) { reader = EntityClassReader.getClassReaderFor(clazz); } public FilterExpressionBuilder() { } public Expression<Boolean> getExp() { return exp; } @Override public void outStart(Start node) { node.getPExpr().apply(this); exp = (Expression<Boolean>)translatedExpressions.get(node.getPExpr()); } @Override public void outAAndExpr(AAndExpr node) { translatedExpressions.put(node, Expressions.and(translatedExpressions.get(node.getLeft()), translatedExpressions.get(node.getRight()))); } @Override public void outAOrExpr(AOrExpr node) { translatedExpressions.put(node, Expressions.or(translatedExpressions.get(node.getLeft()), translatedExpressions.get(node.getRight()))); } @Override public void outANotExpr(ANotExpr node) { translatedExpressions.put(node, Expressions.not(translatedExpressions.get(node.getExpr()))); } @Override public void outAGtExpr(final AGtExpr node) { translatedExpressions.get(node.getLeft()).switchOn(new EmptyBasicTypeSwitch() { @Override public void caseNumber(Expression<Number> expr) { translatedExpressions.put(node, Expressions.isGreaterThan((NumberExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(Double.parseDouble(getLiteral(node.getRight()).toString())))); } @Override public void caseBoolean(Expression<Boolean> expr) { translatedExpressions.put(node, Expressions.isGreaterThan((BooleanExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(Boolean.parseBoolean(getLiteral(node.getRight()).toString())))); } @Override public void caseString(Expression<String> expr) { translatedExpressions.put(node, Expressions.isGreaterThan((StringExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(getLiteral(node.getRight()).toString()))); } }); } @Override public void outALtExpr(final ALtExpr node) { translatedExpressions.get(node.getLeft()).switchOn(new EmptyBasicTypeSwitch() { @Override public void caseNumber(Expression<Number> expr) { translatedExpressions.put(node, Expressions.isLessThan((NumberExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(Double.parseDouble(getLiteral(node.getRight()).toString())))); } @Override public void caseBoolean(Expression<Boolean> expr) { translatedExpressions.put(node, Expressions.isLessThan((BooleanExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(Boolean.parseBoolean(getLiteral(node.getRight()).toString())))); } @Override public void caseString(Expression<String> expr) { translatedExpressions.put(node, Expressions.isLessThan((StringExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(getLiteral(node.getRight()).toString()))); } }); } @Override public void outAGteExpr(final AGteExpr node) { translatedExpressions.get(node.getLeft()).switchOn(new EmptyBasicTypeSwitch() { @Override public void caseNumber(Expression<Number> expr) { translatedExpressions.put(node, Expressions.isGreaterThanOrEqual((NumberExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(Double.parseDouble(getLiteral(node.getRight()).toString())))); } @Override public void caseBoolean(Expression<Boolean> expr) { translatedExpressions.put(node, Expressions.isGreaterThanOrEqual((BooleanExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(Boolean.parseBoolean(getLiteral(node.getRight()).toString())))); } @Override public void caseString(Expression<String> expr) { translatedExpressions.put(node, Expressions.isGreaterThanOrEqual((StringExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(getLiteral(node.getRight()).toString()))); } }); } @Override public void outALteExpr(final ALteExpr node) { translatedExpressions.get(node.getLeft()).switchOn(new EmptyBasicTypeSwitch() { @Override public void caseNumber(Expression<Number> expr) { translatedExpressions.put(node, Expressions.isLessThanOrEqual((NumberExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(Double.parseDouble(getLiteral(node.getRight()).toString())))); } @Override public void caseBoolean(Expression<Boolean> expr) { translatedExpressions.put(node, Expressions.isLessThanOrEqual((BooleanExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(Boolean.parseBoolean(getLiteral(node.getRight()).toString())))); } @Override public void caseString(Expression<String> expr) { translatedExpressions.put(node, Expressions.isLessThanOrEqual((StringExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(getLiteral(node.getRight()).toString()))); } }); } @Override public void outAEqExpr(final AEqExpr node) { translatedExpressions.get(node.getLeft()).switchOn(new EmptyBasicTypeSwitch() { @Override public void caseNumber(Expression<Number> expr) { translatedExpressions.put(node, Expressions.isEqual((NumberExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(Double.parseDouble(getLiteral(node.getRight()).toString())))); } @Override public void caseBoolean(Expression<Boolean> expr) { translatedExpressions.put(node, Expressions.isEqual((BooleanExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(Boolean.parseBoolean(getLiteral(node.getRight()).toString())))); } @Override public void caseString(Expression<String> expr) { translatedExpressions.put(node, Expressions.isEqual((StringExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(getLiteral(node.getRight()).toString()))); } }); } @Override public void outANeqExpr(final ANeqExpr node) { translatedExpressions.get(node.getLeft()).switchOn(new EmptyBasicTypeSwitch() { @Override public void caseNumber(Expression<Number> expr) { translatedExpressions.put(node, Expressions.isNotEqual((NumberExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(Double.parseDouble(getLiteral(node.getRight()).toString())))); } @Override public void caseBoolean(Expression<Boolean> expr) { translatedExpressions.put(node, Expressions.isNotEqual((BooleanExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(Boolean.parseBoolean(getLiteral(node.getRight()).toString())))); } @Override public void caseString(Expression<String> expr) { translatedExpressions.put(node, Expressions.isNotEqual((StringExpression)translatedExpressions.get(node.getLeft()), Expressions.constant(getLiteral(node.getRight()).toString()))); } }); } @Override public void outALikeExpr(ALikeExpr node) { translatedExpressions.put(node, Expressions.like(translatedExpressions.get(node.getLeft()), Expressions.constant(escapeStringLiteral(getLiteral(node.getRight()).toString())))); } @Override public void outANotLikeExpr (ANotLikeExpr node) { translatedExpressions.put(node, Expressions.notLike(translatedExpressions.get(node.getLeft()), Expressions.constant(escapeStringLiteral(getLiteral(node.getRight()).toString())))); } @Override public void outAIlikeExpr(AIlikeExpr node) { translatedExpressions.put(node, Expressions.like(translatedExpressions.get(node.getLeft()), Expressions.constant(escapeStringLiteral(getLiteral(node.getRight()).toString())), true)); } @Override public void outANotIlikeExpr (ANotIlikeExpr node) { translatedExpressions.put(node, Expressions.notLike(translatedExpressions.get(node.getLeft()), Expressions.constant(escapeStringLiteral(getLiteral(node.getRight()).toString())), true)); } @Override public void outABeforeExpr(ABeforeExpr node) { translatedExpressions.put(node, Expressions.isBefore(translatedExpressions.get(node.getAttr()), Expressions.constant(parseDate(getLiteral(node.getDateTime()).toString())))); } @Override public void outAAfterExpr(AAfterExpr node) { translatedExpressions.put(node, Expressions.isAfter(translatedExpressions.get(node.getAttr()), Expressions.constant(parseDate(getLiteral(node.getDateTime()).toString())))); } @Override public void outADuringExpr(ADuringExpr node) { PTimespanLiteral timespan = node.getTimeSpan(); Date lowDate; Date highDate; if (timespan instanceof AFromToTimespanLiteral ) { AFromToTimespanLiteral fromToTimespan = (AFromToTimespanLiteral)timespan; lowDate = parseDate(fromToTimespan.getFrom().getText().trim()); highDate = parseDate(fromToTimespan.getTo().getText().trim()); translatedExpressions.put(node, Expressions.isBetween((ComparableExpression<Date>)translatedExpressions.get(node.getAttr()), Expressions.constant(lowDate), Expressions.constant(highDate))); } } @Override public void outAExistsExpr(AExistsExpr node) { PAttr attribute = node.getAttr(); translatedExpressions.put(node, Expressions.exists(getPropertyPath(attribute))); } @Override public void outADoesNotExistExpr(ADoesNotExistExpr node) { PAttr attribute = node.getAttr(); translatedExpressions.put(node, Expressions.doesNotExist(getPropertyPath(attribute))); } @Override public void outAGeoEqualsExpr(AGeoEqualsExpr node) { translatedExpressions.put(node, Expressions.geoEquals(translatedExpressions.get(node.getLeft()), Expressions.constant((Geometry) getLiteral(node.getRight())))); } @Override public void outACompoundIdAttr(ACompoundIdAttr node) { outPAttr(node); } @Override public void caseAIdAttr(AIdAttr node) { outPAttr(node); } private void outPAttr(PAttr node) { // if this is part of a compound attribute.. skip.. the top compound attribute is the final attribute. Without this test, we treat every part as a separate attribute while the parts on themselves have no meaning. if (!((node.parent() != null) && !(node.parent() instanceof ACompoundIdAttr))) return; String attributeName = getPropertyPath(node); Class attributeType = reader.getPropertyType(attributeName); if (Number.class.isAssignableFrom(attributeType) || int.class.isAssignableFrom(attributeType) || long.class.isAssignableFrom(attributeType) || short.class.isAssignableFrom(attributeType) || float.class.isAssignableFrom(attributeType) || double.class.isAssignableFrom(attributeType) || byte.class.isAssignableFrom(attributeType)) translatedExpressions.put(node, Expressions.numberProperty(attributeName)); else if (String.class.isAssignableFrom(attributeType)) translatedExpressions.put(node, Expressions.stringProperty(attributeName)); else if (Boolean.class.isAssignableFrom(attributeType)) translatedExpressions.put(node, Expressions.booleanProperty(attributeName)); else if (Date.class.isAssignableFrom(attributeType)) translatedExpressions.put(node, Expressions.dateProperty(attributeName)); else if (Geometry.class.isAssignableFrom(attributeType)) translatedExpressions.put(node, Expressions.geometryProperty(attributeName)); } /** * Escapes the given string or remove cql escape sequences if necessary. * @param string The sting to escape. * @return The escaped string. */ private String escapeStringLiteral(String string) { return string.replace("''", "'"); // '' -> ' } }