/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2006-2008, Open Source Geospatial Foundation (OSGeo) * * This library 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; * version 2.1 of the License. * * This library 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. */ package org.geotools.filter.text.ecql; import java.util.Collections; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.geotools.filter.text.commons.AbstractFilterBuilder; import org.geotools.filter.text.commons.IToken; import org.geotools.filter.text.commons.Result; import org.geotools.filter.text.cql2.CQLException; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory; import org.opengis.filter.Id; import org.opengis.filter.Not; import org.opengis.filter.Or; import org.opengis.filter.PropertyIsEqualTo; import org.opengis.filter.expression.Expression; import org.opengis.filter.expression.Function; import org.opengis.filter.expression.Literal; import org.opengis.filter.identity.FeatureId; import org.opengis.filter.spatial.BBOX; import org.opengis.filter.spatial.BinarySpatialOperator; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.MultiLineString; import com.vividsolutions.jts.geom.MultiPoint; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; /** * Builds the filters required by the {@link ECQLCompiler}. * * @author Mauricio Pazos (Axios Engineering) * @since 2.6 */ final class ECQLFilterBuilder extends AbstractFilterBuilder { public ECQLFilterBuilder(String ecqlSource, FilterFactory filterFactory) { super(ecqlSource, filterFactory); } /** * builds the filter id * * @param token * <character> * @return String without the quotes */ public FeatureId buildFeatureID(IToken token) { String strId = removeQuotes(token.toString()); FeatureId id = getFilterFactory().featureId(strId); return id; } /** * builds the filter id * * @param jjtfeature_id_separator_node * @return Id * @throws CQLException */ public Id buildFilterId(final int nodeFeatureId) throws CQLException { // retrieves the id from stack List<FeatureId> idList = new LinkedList<FeatureId>(); while (!getResultStack().empty()) { Result result = getResultStack().peek(); int node = result.getNodeType(); if (node != nodeFeatureId) { break; } FeatureId id = (FeatureId) result.getBuilt(); idList.add(id); getResultStack().popResult(); } assert idList.size() >= 1 : "must have one or more FeatureIds"; // shorts the id list and builds the filter Id Collections.reverse(idList); Set<FeatureId> idSet = new LinkedHashSet<FeatureId>(idList); Id filter = getFilterFactory().id(idSet); return filter; } /** * Builds a negative Number * * @return Negative number * @throws CQLException */ public Literal bulidNegativeNumber() throws CQLException { // retrieves the number value from stack and adds the (-) minus Literal literal = getResultStack().popLiteral(); String strNumber = "-" + literal.getValue(); Object value = literal.getValue(); // builds the negative number @SuppressWarnings("unused") Number number = null; if (value instanceof Double) { number = Double.parseDouble(strNumber); } else if (value instanceof Float) { number = Float.parseFloat(strNumber); } else if (value instanceof Integer) { number = Integer.parseInt(strNumber); } else if (value instanceof Long) { number = Long.parseLong(strNumber); } else { assert false : "Number instnce is expected"; } Literal signedNumber = getFilterFactory().literal(strNumber); return signedNumber; } /** * builds the or filter for the in predicate. The method retrieves the list * of expressions and the property name from stack to make the Or filter. * * <pre> * Thus if the stack have the following predicate * propName in (expr1, expr2) * this method will produce: * (propName = expr1) or (propName = expr2) * </pre> * * @param nodeExpression * @return * @throws CQLException */ public Or buildInPredicate(final int nodeExpression) throws CQLException { // retrieves the expressions from stack List<Expression> exprList = new LinkedList<Expression>(); while (!getResultStack().empty()) { Result result = getResultStack().peek(); int node = result.getNodeType(); if (node != nodeExpression) { break; } getResultStack().popResult(); Expression expr = (Expression) getResultStack().popExpression(); exprList.add(expr); } assert exprList.size() >= 1 : "must have one or more expressions"; // retrieve the left hand expression from the stack final Expression leftHandExpr = getResultStack().popExpression(); // makes one comparison for each expression in the expression list, // associated by the Or filter. List<Filter> filterList = new LinkedList<Filter>(); for (Expression expression : exprList) { PropertyIsEqualTo eq = getFilterFactory().equals(leftHandExpr, expression); filterList.add(eq); } Or orFilter = getFilterFactory().or(filterList); return orFilter; } public Coordinate buildCoordinate() throws CQLException { double y = getResultStack().popDoubleValue(); double x = getResultStack().popDoubleValue(); Coordinate coordinate = new Coordinate(x, y); return coordinate; } public Point buildPointText() throws CQLException { PointBuilder builder = new PointBuilder(getStatement(), getResultStack()); Point point = (Point) builder.build(); return point; } public LineString buildLineString(final int pointNode) throws CQLException { LineStringBuilder builder = new LineStringBuilder(getStatement(), getResultStack()); LineString line = (LineString) builder.build(pointNode); return line; } public Polygon buildPolygon(final int linestringNode) throws CQLException { PolygonBuilder builder = new PolygonBuilder(getStatement(), getResultStack()); Polygon polygon = (Polygon) builder.build(linestringNode); return polygon; } /** * Retrieves all points built in previous parsing process from stack and * creates the multipoint geometry. * * @param pointNode * @return a MultiPoint * @throws CQLException */ public MultiPoint buildMultiPoint(int pointNode) throws CQLException { MultiPointBuilder builder = new MultiPointBuilder(getStatement(), getResultStack()); MultiPoint mp = (MultiPoint) builder.build(pointNode); return mp; } /** * Retrieves all linestring built from stack and creates the multilinestring * geometry * * @param pointNode * @return a MultiLineString * * @throws CQLException * ยก */ public MultiLineString buildMultiLineString(final int linestringtextNode) throws CQLException { MultiLineStringBuilder builder = new MultiLineStringBuilder( getStatement(), getResultStack()); MultiLineString ml = (MultiLineString) builder .build(linestringtextNode); return ml; } /** * Builds a {@link MuliPolygon} using the {@link Polygon} staked in the * parsing process * * @param polygontextNode * . * * @return MultiPolygon * @throws CQLException */ public MultiPolygon buildMultiPolygon(final int polygontextNode) throws CQLException { MultiPolygonBuilder builder = new MultiPolygonBuilder(getStatement(), getResultStack()); MultiPolygon mp = (MultiPolygon) builder.build(polygontextNode); return mp; } /** * Builds a {@link GeometryCollection} * * @param jjtgeometryliteral * @return GeometryCollection * @throws CQLException */ public GeometryCollection buildGeometryCollection( final int jjtgeometryliteral) throws CQLException { GeometryCollectionBuilder builder = new GeometryCollectionBuilder( getStatement(), getResultStack()); GeometryCollection gc = (GeometryCollection) builder .build(jjtgeometryliteral); return gc; } /** * Builds literal geometry * * @param geometry * @return a Literal Geometry * @throws CQLException */ public Literal buildGeometry() throws CQLException { Geometry geometry = getResultStack().popGeometry(); Literal literal = getFilterFactory().literal(geometry); return literal; } public Literal buildGeometryLiteral() throws CQLException { return getResultStack().popLiteral(); } public BinarySpatialOperator buildSpatialEqualFilter() throws CQLException { SpatialOperationBuilder builder = new SpatialOperationBuilder( getResultStack(), getFilterFactory()); BinarySpatialOperator filter = builder.buildEquals(); return filter; } public BinarySpatialOperator buildSpatialDisjointFilter() throws CQLException { SpatialOperationBuilder builder = new SpatialOperationBuilder( getResultStack(), getFilterFactory()); BinarySpatialOperator filter = builder.buildDisjoint(); return filter; } public BinarySpatialOperator buildSpatialIntersectsFilter() throws CQLException { SpatialOperationBuilder builder = new SpatialOperationBuilder( getResultStack(), getFilterFactory()); BinarySpatialOperator filter = builder.buildIntersects(); return filter; } public BinarySpatialOperator buildSpatialTouchesFilter() throws CQLException { SpatialOperationBuilder builder = new SpatialOperationBuilder( getResultStack(), getFilterFactory()); BinarySpatialOperator filter = builder.buildTouches(); return filter; } public BinarySpatialOperator buildSpatialCrossesFilter() throws CQLException { SpatialOperationBuilder builder = new SpatialOperationBuilder( getResultStack(), getFilterFactory()); BinarySpatialOperator filter = builder.buildCrosses(); return filter; } /** * Makes an equals to true filter with the relatePattern function * * @return relatePattern is equal to true * @throws CQLException */ public PropertyIsEqualTo buildRelatePattern() throws CQLException { RelatePatternBuilder builder = new RelatePatternBuilder(getResultStack(), getFilterFactory()); Function relatePattern = builder.build(); PropertyIsEqualTo eq = getFilterFactory().equals(relatePattern, getFilterFactory().literal(true)); return eq; } /** * Builds a not equal filter with that evaluate the relate pattern function * @return Not filter * @throws CQLException */ public Not buildNotRelatePattern() throws CQLException { PropertyIsEqualTo eq = buildRelatePattern(); Not notFilter = getFilterFactory().not(eq); return notFilter; } /** * Checks the correctness of pattern and makes a literal with this pattern; * * @return a Literal with the pattern * @throws CQLException if the pattern has not one of the following characters:T,F,*,0,1,2 */ public Literal buildPattern9IM() throws CQLException { // retrieves the pattern from stack Result resut = getResultStack().popResult(); IToken token = resut.getToken(); Literal built = (Literal)resut.getBuilt(); final String pattern = (String)built.getValue(); // validates the length if(pattern.length() != 9){ throw new CQLException("the pattern DE-9IM must have nine (9) characters", token, getStatement() ); } // validates that the pattern has only the characters T,F,*,0,1,2 String patternUC = pattern.toUpperCase(); char[] validFlags = new char[]{'T', 'F', '*', '0', '1', '2'}; for (int i = 0; i < validFlags.length; i++) { char character = patternUC.charAt(i); boolean found = false; for (int j = 0; j < validFlags.length; j++) { if(validFlags[j] == character){ found = true; break; } } if(!found){ throw new CQLException("the pattern DE-9IM must have only the following characters: T, F, *, 0, 1, 2", token, getStatement() ); } } Literal patternExpr = getFilterFactory().literal(pattern); return patternExpr; } public BinarySpatialOperator buildSpatialWithinFilter() throws CQLException { SpatialOperationBuilder builder = new SpatialOperationBuilder( getResultStack(), getFilterFactory()); BinarySpatialOperator filter = builder.buildWithin(); return filter; } public BinarySpatialOperator buildSpatialContainsFilter() throws CQLException { SpatialOperationBuilder builder = new SpatialOperationBuilder( getResultStack(), getFilterFactory()); BinarySpatialOperator filter = builder.buildContains(); return filter; } public BinarySpatialOperator buildSpatialOverlapsFilter() throws CQLException { SpatialOperationBuilder builder = new SpatialOperationBuilder( getResultStack(), getFilterFactory()); BinarySpatialOperator filter = builder.buildOverlaps(); return filter; } /** * An equals filter with to test the relate function * * @return Relate equals true * @throws CQLException */ public PropertyIsEqualTo buildRelate() throws CQLException { RelateBuilder builder = new RelateBuilder(getResultStack(), getFilterFactory()); Function f = builder.build(); PropertyIsEqualTo eq = getFilterFactory().equals(f, getFilterFactory().literal(true)); return eq; } public org.opengis.filter.spatial.BBOX buildBBox() throws CQLException { SpatialOperationBuilder builder = new SpatialOperationBuilder(getResultStack(), getFilterFactory()); BBOX filter = builder.buildBBox(); return filter; } public org.opengis.filter.spatial.BBOX buildBBoxWithCRS() throws CQLException { SpatialOperationBuilder builder = new SpatialOperationBuilder(getResultStack(), getFilterFactory()); BBOX filter = builder.buildBBoxWithCRS(); return filter; } }