package org.geotools.data.efeature.query;
import static org.geotools.data.efeature.query.SpatialConditionEncoder.convert;
import java.util.Iterator;
import java.util.Set;
import java.util.Stack;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.query.conditions.Condition;
import org.eclipse.emf.query.conditions.Not;
import org.eclipse.emf.query.conditions.eobjects.EObjectCondition;
import org.eclipse.emf.query.conditions.eobjects.EObjectConditionAdapter;
import org.geotools.data.efeature.DataTypes;
import org.geotools.data.efeature.EFeature;
import org.geotools.data.efeature.EFeatureInfo;
import org.geotools.filter.Capabilities;
import org.geotools.filter.function.PropertyExistsFunction;
import org.geotools.filter.visitor.IsFullySupportedFilterVisitor;
import org.geotools.filter.visitor.IsSupportedFilterVisitor;
import org.geotools.util.logging.Logging;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.filter.BinaryComparisonOperator;
import org.opengis.filter.BinaryLogicOperator;
import org.opengis.filter.ExcludeFilter;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterVisitor;
import org.opengis.filter.Id;
import org.opengis.filter.IncludeFilter;
import org.opengis.filter.PropertyIsBetween;
import org.opengis.filter.PropertyIsEqualTo;
import org.opengis.filter.PropertyIsGreaterThan;
import org.opengis.filter.PropertyIsGreaterThanOrEqualTo;
import org.opengis.filter.PropertyIsLessThan;
import org.opengis.filter.PropertyIsLessThanOrEqualTo;
import org.opengis.filter.PropertyIsLike;
import org.opengis.filter.PropertyIsNotEqualTo;
import org.opengis.filter.PropertyIsNull;
import org.opengis.filter.capability.FilterCapabilities;
import org.opengis.filter.expression.Add;
import org.opengis.filter.expression.Divide;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.ExpressionVisitor;
import org.opengis.filter.expression.Function;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.Multiply;
import org.opengis.filter.expression.NilExpression;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.expression.Subtract;
import org.opengis.filter.identity.Identifier;
import org.opengis.filter.spatial.BBOX;
import org.opengis.filter.spatial.Beyond;
import org.opengis.filter.spatial.BinarySpatialOperator;
import org.opengis.filter.spatial.Contains;
import org.opengis.filter.spatial.Crosses;
import org.opengis.filter.spatial.DWithin;
import org.opengis.filter.spatial.Disjoint;
import org.opengis.filter.spatial.Equals;
import org.opengis.filter.spatial.Intersects;
import org.opengis.filter.spatial.Overlaps;
import org.opengis.filter.spatial.Touches;
import org.opengis.filter.spatial.Within;
import org.opengis.filter.temporal.After;
import org.opengis.filter.temporal.AnyInteracts;
import org.opengis.filter.temporal.Before;
import org.opengis.filter.temporal.Begins;
import org.opengis.filter.temporal.BegunBy;
import org.opengis.filter.temporal.During;
import org.opengis.filter.temporal.EndedBy;
import org.opengis.filter.temporal.Ends;
import org.opengis.filter.temporal.Meets;
import org.opengis.filter.temporal.MetBy;
import org.opengis.filter.temporal.OverlappedBy;
import org.opengis.filter.temporal.TContains;
import org.opengis.filter.temporal.TEquals;
import org.opengis.filter.temporal.TOverlaps;
/**
* Encodes a {@link Filter} into an {@link EObjectCondition} statement.
* <p>
* This class is based on the org.geotools.data.jdbc.FilterToSQL source code by Chris Holmes (TOPP)
* and Saul Farber (MassGIS)
*
* @author kengu
*
*
* @source $URL$
*/
public class EObjectConditionEncoder implements FilterVisitor, ExpressionVisitor {
/** {@link Logger} instance for this class */
private Logger LOGGER = Logging.getLogger(EObjectConditionEncoder.class);
/**
* The srid of the schema, so the BBOX conforms.
*/
private String srid;
/**
* Cached {@link SimpleFeatureType} instance
*/
private SimpleFeatureType featureType;
/**
* The geometry attribute to use if none is specified.
*/
// private String defaultGeom;
/**
* Cached {@link EFeatureInfo} instance. The following information is used:
* <ol>
* <li>Name of default geometry attribute, used if none is specified</li>
* </ol>
*/
private EFeatureInfo eFeatureInfo;
/**
* Whether the BBOX filter should be strict (using the exact geom), or loose (using the
* envelopes)
*/
private boolean looseBBox = false;
/**
* Cached {@link FilterCapabilities} supported by {@link EFeatureQuery}
*/
private Capabilities capabilities;
/**
* Cached {@link EObjectCondition} built during encoding
*/
private Condition eCondition;
/**
* Stack of {@link Condition}s waiting to be logically combined
*/
private Stack<Condition> eConditionStack = new Stack<Condition>();
/**
* Stack of {@link Expression} evaluation results waiting to be combined into an
* {@link EFeatureFilter} expression
*/
private Stack<Object> eExpressionStack = new Stack<Object>();
/**
* {@link EObjectConditionEncoder} constructor
*
* @param eFeatureInfo - {@link EFeatureInfo} instance
* @param looseBBox -
*/
public EObjectConditionEncoder(EFeatureInfo eFeatureInfo, boolean looseBBox) {
this.eFeatureInfo = eFeatureInfo;
this.srid = eFeatureInfo.getSRID();
this.featureType = eFeatureInfo.getFeatureType();
this.looseBBox = looseBBox;
}
public Capabilities getCapabilities() {
if (capabilities == null) {
capabilities = createFilterCapabilities();
}
return capabilities;
}
/**
* Determines if specific filter passed in is supported.
*
* @param filter - the Filter to be tested.
* @return true if supported, false otherwise.
* @see IsSupportedFilterVisitor
*/
public boolean supports(Filter filter) {
return getCapabilities().supports(filter);
}
/**
* Determines if the filter and all its sub filters and expressions are supported.
* <p>
*
* @param filter - the filter to be tested.
* @return true if all sub filters are supported, false otherwise.
* @see IsFullySupportedFilterVisitor
*/
public boolean fullySupports(Filter filter) {
return getCapabilities().fullySupports(filter);
}
/**
* Determines if the expression and all its sub expressions is supported.
* <p>
*
* @param filter the filter to be tested.
* @return true if all sub filters are supported, false otherwise.
* @see IsFullySupportedFilterVisitor
*/
public boolean fullySupports(Expression expression) {
return getCapabilities().fullySupports(expression);
}
/**
* Gets whether the Filter.BBOX query will be strict and use an intersects or 'loose' and just
* operate against the geometry envelopes.
*
* @return <tt>true</tt> if this encoder is going to do loose filtering.
*/
public boolean isLooseBBox() {
return looseBBox;
}
/**
* Sets whether the Filter.BBOX query should be 'loose', meaning that it should just do a
* bounding box against the envelope. If set to <tt>false</tt> then the BBOX query will perform
* a full intersects against the geometry, ensuring that it is exactly correct. If <tt>true</tt>
* then the query will likely perform faster, but may not be exactly correct.
*
* @param isLooseBBox - whether the BBOX should be loose or strict.
*/
public void setLooseBBox(boolean isLooseBBox) {
this.looseBBox = isLooseBBox;
}
public String getSRID() {
return this.srid;
}
/**
* Sets a spatial reference system ESPG number, so that the geometry can be properly encoded for
* {@link EFeature}.
*
* @param srid - the code for the spatial reference system.
*/
public void setSRID(String srid) {
this.srid = srid;
}
/**
* Get feature type which EMF Queries are encoded for.
*
* @return a {@link SimpleFeatureType} instance.
*/
public SimpleFeatureType getFeatureType() {
return featureType;
}
/**
* Sets the {@link FeatureType} the encoder is encoding {@link EFeatureFilter} for.
* <p>
* This is used in the context for attribute expressions when encoding to
* {@link EObjectCondition}s.
* </p>
*
* @param featureType - given {@link FeatureType} instance
*/
public void setFeatureType(SimpleFeatureType featureType) {
this.featureType = featureType;
}
/**
* Encode given {@link Filter} into an {@link EObjectCondition}.
*
* @param filter - the {@link Filter} to be encoded.
*
*
*
* @throws EFeatureEncoderException If filter type not supported.
*/
public EObjectCondition encode(Filter filter) throws EFeatureEncoderException {
try {
eCondition = null;
eConditionStack.clear();
Condition condition = (Condition)filter.accept(this, null);
if(condition instanceof EObjectCondition) {
eCondition = condition;
} else {
eCondition = new EObjectConditionAdapter(condition);
}
} catch (Exception ex) {
LOGGER.warning("Unable to export filter" + ex);
throw new EFeatureEncoderException("Problem writing filter: " + filter, ex);
}
return new EObjectConditionAdapter(eCondition);
}
/**
* Encode given {@link Expression} into an {@link Condition}.
*
* @param expression the {@link Expression} to be encoded.
*
* @throws EFeatureEncoderException If filter type not supported.
*/
public Condition encode(Expression expression) throws EFeatureEncoderException {
try {
eCondition = null;
eConditionStack.clear();
expression.accept(this, null);
} catch (Exception ex) {
LOGGER.warning("Unable to export filter" + ex);
throw new EFeatureEncoderException("Problem writing expression: " + expression, ex);
}
return eCondition;
}
// -----------------------------------------------------
// FilterVisitor implementation
// -----------------------------------------------------
/**
* Push {@link EObjectCondition#E_FALSE} to {@link EObjectCondition} stack
* <p>
*
* @param filter - the {@link Filter} instance to be visited
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @see {@link FilterVisitor#visit(ExcludeFilter, Object)}
*/
@Override
public Condition visit(ExcludeFilter filter, Object extraData) {
eConditionStack.add(EObjectCondition.E_FALSE);
return eConditionStack.peek();
}
/**
* Push {@link EObjectCondition#E_TRUE} to {@link EObjectCondition} stack
* <p>
*
* @param filter - the {@link Filter} instance to be visited
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @see {@link FilterVisitor#visit(IncludeFilter, Object)}
*
*/
@Override
public Condition visit(IncludeFilter filter, Object extraData) {
eConditionStack.add(EObjectCondition.E_TRUE);
return eConditionStack.peek();
}
/**
* Create EMF query compatible {@link EAttributeValueIsBetween BETWEEN condition} from given
* {@link PropertyIsBetween BETWEEN filter} and push it to {@link EObjectCondition} stack.
* <p>
* Literal values {@link Number}, {@link java.util.Date} and {@link String} are supported. Any
* other {@link Literal} value will throw an {@link RuntimeException}.
* </p>
*
* @param filter - the {@link Filter} instance to be visited
* @param extraData - not in use
*
* @return a {@link EAttributeValueIsBetween} instance
*
* @throws RuntimeException If one or more expressions are not supported
*/
@Override
public EAttributeValueIsBetween visit(PropertyIsBetween filter, Object extraData)
throws RuntimeException {
//
// Get all expressions
//
Expression expr = filter.getExpression();
Expression lowerbounds = filter.getLowerBoundary();
Expression upperbounds = filter.getUpperBoundary();
//
// Get expression data type
//
Class<?> type;
AttributeDescriptor attType = (AttributeDescriptor) expr.evaluate(featureType);
if (attType != null) {
type = attType.getType().getBinding();
} else {
//
// Assume it's a string
//
type = String.class;
}
//
// Check if type is not supported
//
if (!DataTypes.supports(type)) {
throw new RuntimeException("Type " + type + " not supported.");
}
//
// For each expression in filter:
// 1) Build expression recursively and put onto expression stack
// 2) Cast expression result to expected type
//
expr.accept(this, false);
PropertyName name = toType(eExpressionStack.pop(), PropertyName.class,
"PropertyIsBetween expression is not a PropertyName");
lowerbounds.accept(this, type);
Literal lower = toLiteral(eExpressionStack.pop(),
"PropertyIsBetween lower bound expression is not a Literal");
upperbounds.accept(this, type);
Literal upper = toLiteral(eExpressionStack.pop(),
"PropertyIsBetween upper bound expression is not a Literal");
//
// Get EAttribute instance from definition
//
EAttribute eAttribute = eFeatureInfo.eGetAttribute(name.getPropertyName());
//
// Found attribute?
//
if (eAttribute == null) {
throw new IllegalArgumentException("EAttribute " + name.getPropertyName()
+ " not found");
}
try {
//
// Create EObjectCondition instance and push it to condition stack
//
EAttributeValueIsBetween eCondition = new EAttributeValueIsBetween(eAttribute, lower,
upper);
//
// Push to stack
//
eConditionStack.push(eCondition);
//
// Finished
//
return eCondition;
} catch (EFeatureEncoderException e) {
throw new RuntimeException("Failed to create BETWEEN condition", e);
}
}
/**
* Create EMF query compatible {@link EAttributeValueIsLike LIKE condition} from given
* {@link PropertyIsLike LIKE filter} and push it to {@link EObjectCondition} stack.
* <p>
* Only {@link String} literal values are supported. Any other {@link Literal} value will throw
* a {@link RuntimeException}.
* <p>
* String pattern must comply with {@link Pattern} specification. An invalid pattern will throw
* a {@link RuntimeException}.
* </p>
*
* @param filter - the LIKE Filter to be visited.
*
* @return a {@link EAttributeValueIsLike} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public EAttributeValueIsLike visit(PropertyIsLike filter, Object extraData) {
// Get LIKE clause information
//
// char esc = filter.getEscape().charAt(0);
// char multi = filter.getWildCard().charAt(0);
// char single = filter.getSingleChar().charAt(0);
// boolean matchCase = filter.isMatchingCase();
// TODO: Verify pattern expression
// Get expression and literal
//
String pattern = filter.getLiteral();
Expression expr = filter.getExpression();
// Get expression data type
//
Class<?> type;
AttributeDescriptor attType = (AttributeDescriptor) expr.evaluate(featureType);
if (attType != null) {
type = attType.getType().getBinding();
} else {
// assume it's a string
type = String.class;
}
// Check if type is not supported
//
if (!DataTypes.supports(type)) {
throw new RuntimeException("Type " + type + " not supported.");
}
// Build expression recursively and put onto expression stack
//
expr.accept(this, extraData);
// Cast expression to PropertyName
//
PropertyName name = toType(eExpressionStack.pop(), PropertyName.class,
"PropertyIsLike expression is not a PropertyName");
// Get EAttribute instance from definition
//
EAttribute eAttribute = eFeatureInfo.eGetAttribute(name.getPropertyName());
// Found attribute?
//
if (eAttribute == null) {
throw new IllegalArgumentException("EAttribute " + name.getPropertyName()
+ " not found");
}
try {
// Create EObjectCondition instance and push it
//
EAttributeValueIsLike eCondition = new EAttributeValueIsLike(eAttribute, pattern);
// Push to stack
//
eConditionStack.push(eCondition);
// Finished
//
return eCondition;
} catch (EFeatureEncoderException e) {
throw new RuntimeException("Failed to create LIKE condition", e);
}
}
/**
* Create EMF Query compatible {@link Not <code>NOT</code> condition} from given Geotools
* {@link org.opengis.filter.Not NOT filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(org.opengis.filter.Not filter, Object extraData) {
//
// Build filter recursively and put onto condition stack
//
filter.getFilter().accept(this,extraData);
//
// Invert condition
//
eCondition = new Not(eConditionStack.pop());
//
// Finished
//
return eCondition;
}
/**
* Create EMF Query compatible {@link Condition#AND(Condition) AND condition}Â from given
* Geotools {@link org.opengis.filter.And AND filter} and push it to {@link EObjectCondition}
* stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*/
@Override
public Condition visit(org.opengis.filter.And filter, Object extraData) {
return visit((BinaryLogicOperator) filter, "AND");
}
/**
* Create EMF Query compatible {@link Condition#OR(Condition) OR condition}Â from given Geotools
* {@link org.opengis.filter.Or OR filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(org.opengis.filter.Or filter, Object extraData) {
return visit((BinaryLogicOperator) filter, "OR");
}
/**
* Common implementation for {@link BinaryLogicOperator binary logic operator} filters. This way
* they're all handled centrally.
* <p>
* This method iterate over all filter children, concatenating them into a single EMF Query
* {@link Condition logical operator condition} and push it to {@link EObjectCondition} stack.
* <p>
* Only {@link Not NOT}, {@link Condition#OR(Condition) OR} and {@link Condition#OR(Condition)
* AND} operator filters are supported. Any other {@link BinaryLogicOperator binary logic
* operator} filter instance will throw a {@link RuntimeException}.
* </p>
*
* @param filter - the logic statement.
* @param extraData - extra filter data. Not modified directly by this method.
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
protected Condition visit(BinaryLogicOperator filter, Object extraData) {
// Initialize
//
Condition eCondition = null;
// Get name logic operator
//
String operator = ((String) extraData).toUpperCase();
// Get filter iterator
//
Iterator<Filter> list = filter.getChildren().iterator();
// Is inverse operator?
//
if ("OR".equals(operator)) {
while (list.hasNext()) {
// Build filter recursively and put onto condition stack
//
list.next().accept(this, extraData);
// Initialize?
//
if (eCondition == null) {
eCondition = eConditionStack.pop();
} else {
eCondition = eCondition.OR(eConditionStack.pop());
}
}
} else if ("AND".equals(operator)) {
while (list.hasNext()) {
// Build filter recursively and put onto condition stack
//
list.next().accept(this, extraData);
// Initialize?
//
if (eCondition == null) {
eCondition = eConditionStack.pop();
} else {
eCondition = eCondition.AND(eConditionStack.pop());
}
}
} else {
throw new RuntimeException("Binary logical " + "operator " + operator
+ " filter not supported");
}
// No condition created?
//
if (eCondition == null) {
throw new NullPointerException("Binary logical " + "operator " + operator
+ " filter not supported");
}
// Push concatenated condition to stack
//
eConditionStack.push(eCondition);
// Finished
//
return eCondition;
}
/**
* Create EMF Query compatible {@link EAttributeValueIsEqual EQ (==) condition}Â from given
* Geotools {@link PropertyIsEqualTo EQ filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*/
@Override
public Condition visit(PropertyIsEqualTo filter, Object extraData) {
return visitBinaryComparisonOperator(filter, PropertyIsEqualTo.NAME);
}
/**
* Create EMF Query compatible {@link EAttributeValueIsGreaterEqual GE (>=) condition}Â from
* given Geotools {@link PropertyIsGreaterThanOrEqualTo GE filter} and push it to
* {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(PropertyIsGreaterThanOrEqualTo filter, Object extraData) {
return visitBinaryComparisonOperator(filter, PropertyIsGreaterThanOrEqualTo.NAME);
}
/**
* Create EMF Query compatible {@link EAttributeValueIsGreaterThan GE (>) condition}Â from given
* Geotools {@link PropertyIsGreaterThan GT filter} and push it to {@link EObjectCondition}
* stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(PropertyIsGreaterThan filter, Object extraData) {
return visitBinaryComparisonOperator(filter, PropertyIsGreaterThan.NAME);
}
/**
* Create EMF Query compatible {@link EAttributeValueIsLessThan LE (<) condition}Â from given
* Geotools {@link PropertyIsLessThan LT filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(PropertyIsLessThan filter, Object extraData) {
return visitBinaryComparisonOperator(filter, PropertyIsLessThan.NAME);
}
/**
* Create EMF Query compatible {@link EAttributeValueIsLessEqual LE (<=) condition}Â from given
* Geotools {@link PropertyIsLessThanOrEqualTo LE filter} and push it to
* {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(PropertyIsLessThanOrEqualTo filter, Object extraData) {
return visitBinaryComparisonOperator(filter, PropertyIsLessThanOrEqualTo.NAME);
}
/**
* Create EMF Query compatible {@link EAttributeValueIsNotEqual NE (!=) condition }Â from given
* Geotools {@link PropertyIsNotEqualTo NE filter} and push it to {@link EObjectCondition}
* stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Object visit(PropertyIsNotEqualTo filter, Object extraData) {
visitBinaryComparisonOperator(filter, PropertyIsNotEqualTo.NAME);
return extraData;
}
/**
* Common implementation for {@link BinaryComparisonOperator binary comparison operator}
* filters. This way they're all handled centrally.
* <p>
* This method iterate over all filter children, concatenating them into a single EMF Query
* {@link Condition logical comparison condition} and push it to {@link EObjectCondition} stack.
* <p>
* Only {@link EAttributeValueIsEqual EQ (==)}, {@link EAttributeValueIsNotEqual NE (>)},
* {@link EAttributeValueIsLessThan LT (<)}, {@link EAttributeValueIsLessThan LE (<=)},
* {@link EAttributeValueIsGreaterThan GT (>)} and {@link EAttributeValueIsLessThan GE (>=)}
* logical comparison filters are supported. Any other {@link BinaryLogicOperator binary logic
* operator} filter instance will throw a {@link RuntimeException}.
* </p>
*
* @param filter - the logic statement.
* @param extraData - extra filter data. Not modified directly by this method.
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
protected Condition visitBinaryComparisonOperator(BinaryComparisonOperator filter,
Object extraData) throws RuntimeException {
// LOGGER.finer("exporting SQL ComparisonFilter");
//
// Initialize
//
Literal value = null;
String name = null;
Condition eCondition = null;
//
// Get left and right expression
//
Expression left = filter.getExpression1();
Expression right = filter.getExpression2();
//
// Detect functions
//
if(left instanceof Function || right instanceof Function) {
throw new IllegalArgumentException("Functions are not supported");
}
//
// Detect implicit inner join
//
if (left instanceof PropertyName && right instanceof PropertyName) {
throw new IllegalArgumentException("Implicit inner joint predicates not supported");
}
// Verify left expression
//
if (left instanceof PropertyName) {
name = ((PropertyName) left).getPropertyName();
} else if (left instanceof PropertyExistsFunction) {
name = ((PropertyExistsFunction) left).getParameters().get(0).toString();
} else if (left instanceof Literal) {
value = (Literal) left;
} else {
throw new IllegalArgumentException("Left argument must be a Literal");
}
//
// Verify right expression
//
if (right instanceof PropertyName) {
name = ((PropertyName) right).getPropertyName();
} else if (right instanceof PropertyExistsFunction) {
name = ((PropertyExistsFunction) right).getParameters().get(0).toString();
} else if (right instanceof Literal) {
value = (Literal) right;
} else {
throw new IllegalArgumentException("Right argument must be a Literal");
}
// Get EAttribute instance from definition
//
EAttribute eAttribute = eFeatureInfo.eGetAttribute(name);
// Found attribute?
//
if (eAttribute == null) {
throw new IllegalArgumentException("EAttribute " + name + " not found");
}
// Get comparator
//
String comparator = (String) extraData;
try {
// Create comparator condition
//
if (PropertyIsEqualTo.NAME.equals(comparator)) {
eCondition = new EAttributeValueIsEqual(eAttribute, value);
} else if (PropertyIsLessThan.NAME.equals(comparator)) {
eCondition = new EAttributeValueIsEqual(eAttribute, value);
} else if (PropertyIsLessThanOrEqualTo.NAME.equals(comparator)) {
eCondition = new EAttributeValueIsEqual(eAttribute, value);
} else if (PropertyIsGreaterThan.NAME.equals(comparator)) {
eCondition = new EAttributeValueIsEqual(eAttribute, value);
} else if (PropertyIsGreaterThanOrEqualTo.NAME.equals(comparator)) {
eCondition = new EAttributeValueIsEqual(eAttribute, value);
} else if (PropertyIsNotEqualTo.NAME.equals(comparator)) {
eCondition = new EAttributeValueIsEqual(eAttribute, value);
} else {
throw new IllegalArgumentException("Comparator " + comparator + " not supported");
}
} catch (EFeatureEncoderException e) {
String msg = "Failed to create " + "EObjectCondition (" + comparator + ")";
LOGGER.warning(msg);
throw new IllegalArgumentException(msg, e);
}
// Push concatenated condition to stack
//
eConditionStack.push(eCondition);
// Finished
//
return eCondition;
}
/**
* Create EMF Query compatible {@link EAttributeValueIsNull IS NULL condition}Â from given
* Geotools {@link PropertyIsNull IS NULL filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link EAttributeValueIsNull} instance
*
* @throws RuntimeException If one expression is not supported
*
*/
@Override
public EAttributeValueIsNull visit(PropertyIsNull filter, Object extraData)
throws RuntimeException {
// LOGGER.finer("exporting NullFilter");
// Get filter expression
//
Expression expr = filter.getExpression();
// Build expression recursively and put onto expression stack
//
expr.accept(this, extraData);
// Get PropertyName instance
//
PropertyName name = toType(eExpressionStack.pop(), PropertyName.class,
"PropertyIsNull expression is not a PropertyName");
// Get EAttribute instance from definition
//
EAttribute eAttribute = eFeatureInfo.eGetAttribute(name.getPropertyName());
// Found attribute?
//
if (eAttribute == null) {
throw new IllegalArgumentException("EAttribute " + name.getPropertyName()
+ " not found");
}
// Create EObjectCondition instance and push to stack
//
EAttributeValueIsNull eCondition = new EAttributeValueIsNull(eAttribute);
// Push to stack
//
eConditionStack.push(eCondition);
// Finished
//
return eCondition;
}
/**
* Create EMF Query compatible {@link EAttributeValueIsID ID condition}Â from given Geotools
* {@link Id ID filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @param filter - the {@link Id identifier} filter
*
* @return a {@link EAttributeValueIsID} instance
*
* @throws RuntimeException - if no or more than one {@link Identifier identifier}s are given.
*
*/
@Override
public EAttributeValueIsID visit(Id filter, Object extraData) {
// Get identifiers
//
Set<Identifier> ids = filter.getIdentifiers();
//
// LOGGER.finer("Exporting FID=" + ids);
//
// Verify that only one identifier is given
//
if (ids.isEmpty()) {
throw new RuntimeException("Id filter " + filter + " has no identifier");
} else if (ids.size() > 1) {
throw new RuntimeException("Id filter " + filter + " has more than one identifier");
}
//
// Get ID
//
Object id = ids.iterator().next().getID();
//
// Is ID invalid?
//
if (id == null) {
throw new NullPointerException("Identifier is null");
}
//
// Get ID EAttribute instance from definition
//
EAttribute eAttribute = eFeatureInfo.eIDAttribute();
try {
// Create EObjectCondition instance and push it
//
EAttributeValueIsID eCondition = new EAttributeValueIsID(eAttribute, id.toString());
//
// Push to stack
//
eConditionStack.push(eCondition);
//
// Finished
//
return eCondition;
} catch (EFeatureEncoderException e) {
throw new RuntimeException("Failed to create IS ID condition", e);
}
}
/**
* Create EMF Query compatible {@link EGeometryValueBBox BBOX condition}Â from given Geotools
* {@link BBOX BBOX filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(BBOX filter, Object extraData) {
return visitBinarySpatialOperator(filter, BBOX.NAME);
}
/**
* Create EMF Query compatible {@link EGeometryValueBeyond BEYOND condition}Â from given
* Geotools {@link Beyond BEYOND filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(Beyond filter, Object extraData) {
return visitBinarySpatialOperator(filter, Beyond.NAME);
}
/**
* Create EMF Query compatible {@link EGeometryValueContains CONTAINS condition}Â from given
* Geotools {@link Contains CONTAINS filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(Contains filter, Object extraData) {
return visitBinarySpatialOperator(filter, Contains.NAME);
}
/**
* Create EMF Query compatible {@link EGeometryValueCrosses CROSSES condition}Â from given
* Geotools {@link Crosses CROSSES filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(Crosses filter, Object extraData) {
return visitBinarySpatialOperator(filter, Crosses.NAME);
}
/**
* Create EMF Query compatible {@link EGeometryValueDisjoint DISJOINT condition}Â from given
* Geotools {@link Disjoint DISJOINT filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(Disjoint filter, Object extraData) {
return visitBinarySpatialOperator(filter, Disjoint.NAME);
}
/**
* Create EMF Query compatible {@link EGeometryValueDWithin DWITHIN condition}Â from given
* Geotools {@link DWithin DWITHIN filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(DWithin filter, Object extraData) {
return visitBinarySpatialOperator(filter, DWithin.NAME);
}
/**
* Create EMF Query compatible {@link EGeometryValueEquals EQUALS condition}Â from given
* Geotools {@link Equals EQUALS filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(Equals filter, Object extraData) {
return visitBinarySpatialOperator(filter, Equals.NAME);
}
/**
* Create EMF Query compatible {@link EGeometryValueIntersects INTERSECTS condition}Â from given
* Geotools {@link Intersects INTERSECTS filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(Intersects filter, Object extraData) {
return visitBinarySpatialOperator(filter, Intersects.NAME);
}
/**
* Create EMF Query compatible {@link EGeometryValueOverlaps OVERLAPS condition}Â from given
* Geotools {@link Overlaps OVERLAPS filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(Overlaps filter, Object extraData) {
return visitBinarySpatialOperator(filter, Overlaps.NAME);
}
/**
* Create EMF Query compatible {@link EGeometryValueTouches TOUCHES condition}Â from given
* Geotools {@link Touches TOUCHES filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Object visit(Touches filter, Object extraData) {
return visitBinarySpatialOperator(filter, Touches.NAME);
}
/**
* Create EMF Query compatible {@link EGeometryValueWithin WITHIN condition}Â from given
* Geotools {@link Within WITHIN filter} and push it to {@link EObjectCondition} stack.
*
* @param filter - the filter to visit
* @param extraData - not in use
*
* @return a {@link Condition} instance
*
* @throws RuntimeException If one or more expressions are not supported
*
*/
@Override
public Condition visit(Within filter, Object extraData) {
return visitBinarySpatialOperator(filter, Within.NAME);
}
/**
* Common implementation for {@link BinarySpatialOperator binary spatial operator} filters. This
* way they're all handled centrally.
* <p>
* This method extracts property name, geometry literal and descriptor and evaluates if property
* name and geometry literal arguments are swapped.
* </p>
*
* @param filter - binary spatial operator statement
* @param name - operator name
*
* @return a {@link Condition} instance
*
* @throws RuntimeException - if one or more expressions are not supported
*
*/
protected Condition visitBinarySpatialOperator(BinarySpatialOperator filter, String name) {
// Perform some sanity checks
//
if (filter == null) {
throw new NullPointerException("Filter to be encoded cannot be null");
}
// Extract the property name and the geometry literal
//
Literal geometry;
PropertyName property;
boolean swapped = false;
BinaryComparisonOperator op = (BinaryComparisonOperator) filter;
if (op.getExpression1() instanceof PropertyName && op.getExpression2() instanceof Literal) {
property = (PropertyName) op.getExpression1();
geometry = (Literal) op.getExpression2();
swapped = true;
} else if (op.getExpression2() instanceof PropertyName
&& op.getExpression1() instanceof Literal) {
property = (PropertyName) op.getExpression2();
geometry = (Literal) op.getExpression1();
} else {
throw new IllegalArgumentException("Can only encode spatial filters that do "
+ "compare a property name and a geometry");
}
// Get EAttribute instance
//
EAttribute eAttribute = eFeatureInfo.eGetAttribute(property.getPropertyName());
// Going through evaluate ensures we get the proper result even
// if the name has not been specified (convention -> the default geometry)
AttributeDescriptor descriptor = (AttributeDescriptor) property.evaluate(featureType);
if (descriptor instanceof GeometryDescriptor) {
// Forward
//
return visitBinarySpatialOperator(filter, name, eAttribute,
(GeometryDescriptor) descriptor, geometry, swapped);
} else if(descriptor == null){
throw new IllegalArgumentException("Attribute '" +
property.getPropertyName() + "' not found in '" +
featureType.getTypeName() + "'");
} else {
throw new IllegalArgumentException("Attribute '" +
property.getPropertyName() + "' is not a geometry in " +
featureType.getTypeName() + "'");
}
}
/**
* Common implementation for {@link BinarySpatialOperator binary spatial operator} filters. This
* way they're all handled centrally.
* <p>
* This method extracts property name, geometry literal and descriptor and evaluates if property
* name and geometry literal arguments are swapped.
* </p>
*
* @param filter - the binary spatial operator statement
* @param name - operator name
* @param property - property name expression
* @param geometry - geometry literal expression
* @param swapped - true if geometry literal is left expression (first)
* @param eAttribute - EAttribute instance
* @param descriptor - geometry descriptor
*
* @return a {@link Condition} instance
*
* @throws RuntimeException - if one or more expressions are not supported
*
*/
protected Condition visitBinarySpatialOperator(BinarySpatialOperator filter, String name,
EAttribute eAttribute, GeometryDescriptor descriptor, Literal geometry, boolean swapped) {
try {
if (BBOX.NAME.equals(name)) {
return new EGeometryValueBBox(eAttribute, geometry, swapped);
} else if (Beyond.NAME.equals(name)) {
double distance = convert((Beyond) filter, descriptor);
return new EGeometryValueBeyond(eAttribute, geometry, distance);
} else if (DWithin.NAME.equals(name)) {
double distance = convert((DWithin) filter, descriptor);
return new EGeometryValueDWithin(eAttribute, geometry, distance);
} else if (Contains.NAME.equals(name)) {
return new EGeometryValueContains(eAttribute, geometry, swapped);
} else if (Crosses.NAME.equals(name)) {
return new EGeometryValueCrosses(eAttribute, geometry, swapped);
} else if (Disjoint.NAME.equals(name)) {
return new EGeometryValueDisjoint(eAttribute, geometry, swapped);
} else if (Equals.NAME.equals(name)) {
return new EGeometryValueEquals(eAttribute, geometry, swapped);
} else if (Intersects.NAME.equals(name)) {
return new EGeometryValueIntersects(eAttribute, geometry, swapped);
} else if (Overlaps.NAME.equals(name)) {
return new EGeometryValueOverlaps(eAttribute, geometry, swapped);
} else if (Touches.NAME.equals(name)) {
return new EGeometryValueTouches(eAttribute, geometry, swapped);
} else if (Within.NAME.equals(name)) {
return new EGeometryValueWithin(eAttribute, geometry, swapped);
}
} catch (Exception e) {
throw new RuntimeException("Spatial operation " + name + " is not supported", e);
}
// Not supported, throw exception
//
throw new IllegalArgumentException("Spatial operation " + name + " is not supported");
}
@Override
public Object visitNullFilter(Object extraData) {
return extraData;
}
// -----------------------------------------------------
// ExpressionVisitor
// -----------------------------------------------------
/**
* Validate property against current {@link #getFeatureType() feature} and push it or the
* {@link AttributeDescriptor} to the {@link Expression} stack.
*
* @param expression - the expression to visit
* @param toAttribute - if {@link Boolean#TRUE TRUE}, push and return
* {@link AttributeDescriptor} found in current {@link #getFeatureType() feature type},
* otherwise return {@link PropertyName} instance.
*
* @return a {@link AttributeDescriptor} or {@link PropertyName} instance depending on
* 'toAttribute' flag
*
* @throws RuntimeException If {@link #getFeatureType()} does not define a
* {@link AttributeDescriptor} with given property name.
*/
@Override
public Object visit(PropertyName expression, Object toAttribute) throws RuntimeException {
// LOGGER.finer("exporting PropertyName");
//
// Get parse flag
//
boolean bFlag = (toAttribute instanceof Boolean ? (Boolean) toAttribute : false);
//
// First evaluate expression against feature type
// get the attribute, this handles xpath
//
AttributeDescriptor attribute = null;
try {
attribute = (AttributeDescriptor) expression.evaluate(featureType);
} catch (Exception e) {
// Property name not supported
//
throw new IllegalArgumentException("Error occured mapping " + expression
+ " to feature type", e);
}
// Verify
//
if (attribute == null) {
throw new NullPointerException("Attribute with name " +
"[" + expression.getPropertyName() + "] not found");
}
// Push to stack
//
eExpressionStack.push((bFlag ? attribute : expression));
// Finished
//
return eExpressionStack.peek();
}
/**
* Get literal or it's value.
* <p>
*
* @param expression - the expression to visit
* @param toValue - if {@link Boolean#TRUE TRUE}, push and return {@link Literal#getValue()
* literal value}, otherwise push and return the {@link Literal} itself.
*
* @return a {@link Object value} or the {@link Literal itself} instance depending on 'toValue'
* flag
*
*/
@Override
public Object visit(Literal expression, Object toValue) {
// Initialize
//
Object literal = expression;
// Get parse flag
//
boolean bFlag = (toValue instanceof Boolean ? (Boolean) toValue : false);
// Get literal value?
//
if (bFlag) {
// Get literal value
//
literal = expression.evaluate(null);
// If that failed, grab the value as is
//
if (literal == null) {
literal = expression.getValue();
}
} else if (literal == null) {
literal = Expression.NIL;
}
// Push to stack
//
eExpressionStack.push(literal);
// Finished
//
return literal;
}
/**
* Binary expression ADD is not supported.
*/
@Override
public Object visit(Add expression, Object extraData) {
throw new IllegalArgumentException("Binary expression ADD is not supported");
// return visit((BinaryExpression)expression, "+", extraData);
}
/**
* Binary expression DIVIDE is not supported.
*/
@Override
public Object visit(Divide expression, Object extraData) {
throw new IllegalArgumentException("Binary expression DIVIDE is not supported");
// return visit((BinaryExpression)expression, "/", extraData);
}
/**
* Binary expression MULTIPLY is not supported.
*/
@Override
public Object visit(Multiply expression, Object extraData) {
throw new IllegalArgumentException("Binary expression MULTIPLY is not supported");
// return visit((BinaryExpression)expression, "*", extraData);
}
/**
* Binary expression SUBSTRACT is not supported.
*/
@Override
public Object visit(Subtract expression, Object extraData) {
throw new IllegalArgumentException("Binary expression SUBSTRACT is not supported");
// return visit((BinaryExpression)expression, "-", extraData);
}
// /**
// * Writes the SQL for the Math Expression.
// *
// * @param expression the Math phrase to be written.
// * @param operator The operator of the expression.
// *
// * @throws RuntimeException for io problems
// */
// protected Object visit(BinaryExpression expression, String operator, Object extraData) throws
// RuntimeException {
// LOGGER.finer("exporting Expression Math");
//
// try {
// expression.getExpression1().accept(this, extraData);
// out.write(" " + operator + " ");
// expression.getExpression2().accept(this, extraData);
// } catch (java.io.IOException ioe) {
// throw new RuntimeException("IO problems writing expression", ioe);
// }
// return extraData;
// }
/**
* {@link Function}s are not supported.
*/
@Override
public Object visit(Function function, Object extraData) throws RuntimeException {
throw new IllegalArgumentException("Functions are not not supported");
// try {
// List<Expression> parameters = function.getParameters();
// List contexts = null;
// //check context, if a list which patches parameter size list assume its context
// // to pass along to each Expression for encoding
// if( extraData instanceof List && ((List)extraData).size() == parameters.size() ) {
// contexts = (List) extraData;
// }
//
// //set the encoding function flag to signal we are inside a function
// encodingFunction = true;
//
// //write the name
// out.write( function.getName() );
//
// //write the arguments
// out.write( "(");
// for ( int i = 0; i < parameters.size(); i++ ) {
// Expression e = parameters.get( i );
//
// Object context = contexts != null ? contexts.get( i ) : extraData;
// e.accept(this, context);
//
// if ( i < parameters.size()-1 ) {
// out.write( ",");
// }
//
// }
// out.write( ")");
//
// //reset the encoding function flag
// encodingFunction = false;
// }
// catch (IOException e) {
// throw new RuntimeException( e );
// }
//
// return extraData;
}
@Override
public NilExpression visit(NilExpression expression, Object toValue) {
// Get parse flag
//
boolean bFlag = (toValue instanceof Boolean ? (Boolean) toValue : false);
return bFlag ? null : expression;
}
// private void encodeGeomFilter(BinarySpatialOperator filter, String function, String
// comparison, boolean useIndex) {
//
// // This method blindly assumes that the filter is supported
// //
// Expression left = filter.getExpression1();
// Expression right = filter.getExpression2();
//
// try {
// //should we use the index?
// if (useIndex) {
// encodeExpression(left);
// out.write(" && ");
// encodeExpression(right);
// }
//
// // looseBbox only applies to GEOMETRY_BBOX, so unless this is a
// // BBOX, we will always generate the full SQL.
// if (filter.getFilterType() != AbstractFilter.GEOMETRY_BBOX || !looseBBox) {
// if (useIndex) {
// out.write(" AND ");
// }
// out.write(function + "(");
// encodeExpression(left);
// out.write(", ");
// encodeExpression(right);
// out.write(")" + comparison);
// }
// } catch (IOException ex) {
// EFeaturePlugin.warn("Unable to export filter", ex);
// }
// }
//
// private void encodeExpression(Expression expr) throws IOException {
// if (expr == null) {
// out.write("\"" + defaultGeom + "\"");
// } else {
// expr.accept(this,null);
// }
// }
// /**
// * Turns a geometry filter into the postgis sql bbox statement.
// *
// * @param filter the geometry filter to be encoded.
// *
// * @throws RuntimeException for IO exception (need a better error)
// */
// public void visit(BinarySpatialOperator filter) throws RuntimeException {
//
// //LOGGER.finer("exporting GeometryFilter");
//
// short filterType = filter.getFilterType();
// DefaultExpression left = (DefaultExpression) filter.getLeftGeometry();
// DefaultExpression right = (DefaultExpression) filter.getRightGeometry();
//
//
//
// // Figure out if we need to constrain this query with the && constraint.
// int literalGeometryCount = 0;
//
// if ((left != null)
// && (left.getType() == DefaultExpression.LITERAL_GEOMETRY)) {
// literalGeometryCount++;
// }
//
// if ((right != null)
// && (right.getType() == DefaultExpression.LITERAL_GEOMETRY)) {
// literalGeometryCount++;
// }
//
// boolean constrainBBOX = (literalGeometryCount == 1);
// boolean onlyBbox = filterType == AbstractFilter.GEOMETRY_BBOX
// && looseBBox;
//
// try {
//
// // DJB: disjoint is not correctly handled in the pre-march 22/05
// // version
// // I changed it to not do a "&&" index search for disjoint because
// // Geom1 and Geom2 can have a bbox overlap and be disjoint
// // I also added test case.
// // NOTE: this will not use the index, but its unlikely that using
// // the index
// // for a disjoint query will be the correct thing to do.
//
// // DJB NOTE: need to check for a NOT(A intersects G) filter
// // --> NOT( (A && G) AND intersects(A,G))
// // and check that it does the right thing.
//
// constrainBBOX = constrainBBOX
// && (filterType != AbstractFilter.GEOMETRY_DISJOINT);
//
// if (constrainBBOX) {
// encodeExpression(left);
// out.write(" && ");
// encodeExpression(right);
//
// if (!onlyBbox) {
// out.write(" AND ");
// }
// }
//
// String closingParenthesis = ")";
//
// if (!onlyBbox) {
// if (filterType == AbstractFilter.GEOMETRY_EQUALS) {
// out.write("equals");
// } else if (filterType == AbstractFilter.GEOMETRY_DISJOINT) {
// out.write("NOT (intersects");
// closingParenthesis += ")";
// } else if (filterType == AbstractFilter.GEOMETRY_INTERSECTS) {
// out.write("intersects");
// } else if (filterType == AbstractFilter.GEOMETRY_CROSSES) {
// out.write("crosses");
// } else if (filterType == AbstractFilter.GEOMETRY_WITHIN) {
// out.write("within");
// } else if (filterType == AbstractFilter.GEOMETRY_CONTAINS) {
// out.write("contains");
// } else if (filterType == AbstractFilter.GEOMETRY_OVERLAPS) {
// out.write("overlaps");
// } else if (filterType == AbstractFilter.GEOMETRY_BBOX) {
// out.write("intersects");
// } else if (filterType == AbstractFilter.GEOMETRY_TOUCHES) {
// out.write("touches");
// } else {
// // this will choke on beyond and dwithin
// throw new RuntimeException("does not support filter type "
// + filterType);
// }
// out.write("(");
//
// encodeExpression(left);
// out.write(", ");
// encodeExpression(right);
//
// out.write(closingParenthesis);
// }
// } catch (java.io.IOException ioe) {
// LOGGER.warning("Unable to export filter" + ioe);
// throw new RuntimeException("io error while writing", ioe);
// }
// }
//
// public void visit(PropertyIsLike filter) throws UnsupportedOperationException {
// char esc = filter.getEscape().charAt(0);
// char multi = filter.getWildcardMulti().charAt(0);
// char single = filter.getWildcardSingle().charAt(0);
// String pattern = LikeFilterImpl.convertToSQL92(esc,multi,single,filter.getPattern());
//
// DefaultExpression att = (DefaultExpression) filter.getValue();
//
// try {
// out.write( " ( " );
// att.accept(this);
//
// out.write(" LIKE '");
// out.write(pattern);
// out.write("' ");
//
// //JD: this is an ugly ugly hack!! hopefully when the new feature model is around we can
// // fix this
// //check for context for a date
// if ( att instanceof AttributeExpression && context != null
// && java.util.Date.class.isAssignableFrom( context ) ) {
// //if it is a date, add additional logic for a timestamp, or a timestamp with
// // timezone
// out.write( " OR " );
// att.accept( this );
// out.write( " LIKE '" );
// out.write(pattern + " __:__:__'" ); //timestamp
//
// out.write( " OR " );
// att.accept( this );
// out.write( " LIKE '" );
// out.write(pattern + " __:__:_____'" ); //timestamp with time zone
// }
//
// out.write( " ) " );
//
// } catch (java.io.IOException ioe) {
// throw new RuntimeException(IO_ERROR, ioe);
// }
//
//
// }
//
// /**
// * Writes the SQL for a Compare Filter.
// *
// * DJB: note, postgis overwrites this implementation because of the way
// * null is handled. This is for <PropertyIsNull> filters and <PropertyIsEqual> filters
// * are handled. They will come here with "property = null".
// * NOTE:
// * SELECT * FROM <table> WHERE <column> isnull; -- postgresql
// * SELECT * FROM <table> WHERE isnull(<column>); -- oracle???
// *
// * @param filter - the comparison filter.
// *
// * @throws RuntimeException for io exception with writer
// */
// public void visit(BinaryComparisonOperator filter) throws RuntimeException {
// //LOGGER.finer("exporting SQL ComparisonFilter");
//
// DefaultExpression left = (DefaultExpression) filter.getLeftValue();
// DefaultExpression right = (DefaultExpression) filter.getRightValue();
// LOGGER.finer("Filter type id is " + filter.getFilterType());
// LOGGER.finer("Filter type text is "
// + comparisions.get(new Integer(filter.getFilterType())));
//
// String type = (String) comparisions.get(new Integer(
// filter.getFilterType()));
//
// try {
// // a bit hacky, but what else can we really do?
// if ( (right == null) && (filter.getFilterType()==FilterType.COMPARE_EQUALS ) )
// {
// left.accept(this);
// out.write(" isnull");
// }
// else
// {
// //check for case insentivity (TODO: perhaps move this up to jdbc)
// if ( !filter.isMatchingCase() ) {
// //only for == or !=
// if ( filter.getFilterType() == Filter.COMPARE_EQUALS ||
// filter.getFilterType() == Filter.COMPARE_NOT_EQUALS ) {
//
// //only for strings
// if ( left.getType() == Expression.LITERAL_STRING
// || right.getType() == Expression.LITERAL_STRING ) {
//
// out.write( "lower(" ); left.accept( this ); out.write( ")");
// out.write( " " + type + " " );
// out.write( "lower(" ); right.accept( this ); out.write( ")");
//
// return;
// }
// }
// }
//
// //normal execution
// left.accept(this);
// out.write(" " + type + " ");
// right.accept(this);
// }
// } catch (java.io.IOException ioe) {
// throw new RuntimeException(IO_ERROR, ioe);
// }
// }
// -----------------------------------------------------
// Private helper methods
// -----------------------------------------------------
@Override
public Object visit(After arg0, Object extraData) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(AnyInteracts expression, Object extraData) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(Before expression, Object extraData) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(Begins expression, Object extraData) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(BegunBy expression, Object extraData) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(During expression, Object extraData) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(EndedBy expression, Object extraData) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(Ends expression, Object extraData) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(Meets expression, Object extraData) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(MetBy expression, Object extraData) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(OverlappedBy expression, Object extraData) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(TContains expression, Object extraData) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(TEquals expression, Object extraData) {
// TODO Auto-generated method stub
return null;
}
@Override
public Object visit(TOverlaps expression, Object extraData) {
// TODO Auto-generated method stub
return null;
}
// -----------------------------------------------------
// Private helper methods
// -----------------------------------------------------
private static <T> T toType(Object object, Class<T> type, String message) {
if (!type.isInstance(object)) {
throw new IllegalArgumentException(message);
}
return type.cast(object);
}
private static Literal toLiteral(Object object, String message) {
return toType(object,Literal.class,message);
}
private static Capabilities createFilterCapabilities() {
// Create Capabilities helper class
//
Capabilities capabilities = new Capabilities();
// Add EFeature ID filter capability
//
capabilities.addType(Id.class);
// Add Include All filter capability
//
capabilities.addType(IncludeFilter.class);
// Add Exclude All filter capability
//
capabilities.addType(ExcludeFilter.class);
// Add logical operators (AND, OR, NOT)
//
capabilities.addAll(Capabilities.LOGICAL);
// Add simple comparators
//
capabilities.addAll(Capabilities.SIMPLE_COMPARISONS);
// Add special comparators
//
capabilities.addName(PropertyIsNull.NAME);
// Add spatial operations
//
capabilities.addName(BBOX.NAME);
capabilities.addName(Equals.NAME);
capabilities.addName(Disjoint.NAME);
capabilities.addName(Intersects.NAME);
capabilities.addName(Touches.NAME);
capabilities.addName(Crosses.NAME);
capabilities.addName(Within.NAME);
capabilities.addName(Contains.NAME);
capabilities.addName(Overlaps.NAME);
capabilities.addName(Beyond.NAME);
capabilities.addName(DWithin.NAME);
// Finished
//
return capabilities;
}
}