/**
* Copyright (c) Codice Foundation
* <p>
* This 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 any later version.
* <p>
* This program 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. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*/
package ddf.catalog.filter.proxy.adapter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.geotools.filter.FilterFactoryImpl;
import org.geotools.geometry.jts.spatialschema.geometry.GeometryImpl;
import org.geotools.styling.UomOgcMapping;
import org.geotools.temporal.object.DefaultPeriodDuration;
import org.opengis.filter.And;
import org.opengis.filter.BinaryComparisonOperator;
import org.opengis.filter.ExcludeFilter;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.FilterVisitor;
import org.opengis.filter.Id;
import org.opengis.filter.IncludeFilter;
import org.opengis.filter.Not;
import org.opengis.filter.Or;
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.PropertyIsNil;
import org.opengis.filter.PropertyIsNotEqualTo;
import org.opengis.filter.PropertyIsNull;
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.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.BinaryTemporalOperator;
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;
import org.opengis.temporal.Instant;
import org.opengis.temporal.Period;
import ddf.catalog.filter.FilterAdapter;
import ddf.catalog.filter.FilterDelegate;
import ddf.catalog.impl.filter.FuzzyFunction;
import ddf.catalog.impl.filter.ProximityFunction;
import ddf.catalog.source.UnsupportedQueryException;
import ddf.measure.Distance;
import ddf.measure.Distance.LinearUnit;
public class GeotoolsFilterAdapterImpl implements FilterAdapter, FilterVisitor, ExpressionVisitor {
public static final String CQL_FEET = "feet";
public static final String CQL_METERS = "meters";
public static final String CQL_STATUTE_MILES = "statute miles";
public static final String CQL_NAUTICAL_MILES = "nautical miles";
public static final String CQL_KILOMETERS = "kilometers";
private static final FilterFactory FF = new FilterFactoryImpl();
public <T> T adapt(Filter filter, FilterDelegate<T> filterDelegate)
throws UnsupportedQueryException {
if (filter == null) {
throw new IllegalArgumentException("Cannot adapt a null Filter.");
}
try {
@SuppressWarnings("unchecked")
T result = (T) filter.accept(this, filterDelegate);
return result;
} catch (UnsupportedOperationException e) {
throw new UnsupportedQueryException(e.getMessage(), e);
}
}
public Object visit(NilExpression expression, Object delegate) {
throw new UnsupportedOperationException(NilExpression.class.getSimpleName()
+ " expression not supported by Filter Adapter.");
}
public Object visit(Add expression, Object delegate) {
throw new UnsupportedOperationException(Add.NAME + " expression not supported.");
}
public Object visit(Divide expression, Object delegate) {
throw new UnsupportedOperationException(Divide.NAME + " expression not supported.");
}
public Object visit(Function expression, Object delegate) {
if (expression == null) {
throw new UnsupportedOperationException(
Function.class.getSimpleName() + " must not be null.");
}
if (expression.getName()
.equals(ProximityFunction.NAME.getName())) {
List<Expression> parameters = new ArrayList<>(expression.getParameters());
if (parameters.size() == ProximityFunction.NUM_PARAMETERS) {
String propertyName = parameters.remove(0)
.toString();
int distance;
try {
distance = Integer.parseInt(parameters.remove(0)
.toString());
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Distance parameter was not valid");
}
String searchTerm = parameters.remove(0)
.toString();
return new ProximityArguments(propertyName, distance, searchTerm);
} else {
throw new IllegalArgumentException("Not enough arguments for ProximityFunction");
}
}
return expression;
}
public Object visit(Literal expression, Object delegate) {
if (expression.getValue() == null) {
throw new UnsupportedOperationException(
Literal.class.getSimpleName() + " value must not be null.");
}
return expression.getValue();
}
public Object visit(Multiply expression, Object delegate) {
throw new UnsupportedOperationException(Multiply.NAME + " expression not supported.");
}
public Object visit(PropertyName expression, Object delegate) {
if (expression.getPropertyName() == null) {
throw new UnsupportedOperationException("Property name must not be null.");
}
return expression.getPropertyName();
}
public Object visit(Subtract expression, Object delegate) {
throw new UnsupportedOperationException(Subtract.NAME + " expresssion not supported.");
}
public Object visitNullFilter(Object delegate) {
throw new UnsupportedOperationException("Null filter not supported by Filter Adapter.");
}
public Object visit(ExcludeFilter filter, Object delegate) {
return ((FilterDelegate<?>) delegate).exclude();
}
public Object visit(IncludeFilter filter, Object delegate) {
return ((FilterDelegate<?>) delegate).include();
}
public Object visit(Id filter, Object delegate) {
throw new UnsupportedOperationException(
Id.class.getSimpleName() + " filter not supported by Filter Adapter.");
}
@SuppressWarnings("unchecked")
public Object visit(And filter, Object delegate) {
List<Object> results = new ArrayList<>();
List<Filter> childList = filter.getChildren();
if (childList != null) {
for (Filter child : childList) {
results.add(child.accept(this, delegate));
}
if (results.size() == 1) {
// removing unused and
return results.get(0);
} else if (results.size() > 0) {
return ((FilterDelegate<Object>) delegate).and(results);
}
}
// No children or the children returned 0 results
throw new UnsupportedOperationException("No valid operands for And filter.");
}
@SuppressWarnings("unchecked")
public Object visit(Not filter, Object delegate) {
Filter child = filter.getFilter();
return ((FilterDelegate<Object>) delegate).not(child.accept(this, delegate));
}
@SuppressWarnings("unchecked")
public Object visit(Or filter, Object delegate) {
List<Object> results = new ArrayList<>();
List<Filter> childList = filter.getChildren();
if (childList != null) {
for (Filter child : childList) {
results.add(child.accept(this, delegate));
}
if (results.size() > 0) {
return ((FilterDelegate<Object>) delegate).or(results);
}
}
// No children or the children returned 0 results
throw new UnsupportedOperationException("No valid operands for And filter.");
}
public Object visit(PropertyIsBetween filter, Object delegate) {
String propertyName;
Object lower;
Object upper;
if (filter.getExpression() instanceof PropertyName
&& filter.getLowerBoundary() instanceof Literal
&& filter.getUpperBoundary() instanceof Literal) {
propertyName = (String) filter.getExpression()
.accept(this, delegate);
lower = filter.getLowerBoundary()
.accept(this, delegate);
upper = filter.getUpperBoundary()
.accept(this, delegate);
} else {
throw new UnsupportedOperationException(
"Only support PropertyName for expression and Literal for upper and lower boundaries with PropertyIsBetween filter.");
}
if (lower instanceof String && upper instanceof String) {
return ((FilterDelegate<?>) delegate).propertyIsBetween(propertyName,
(String) lower,
(String) upper);
} else if (lower instanceof Date && upper instanceof Date) {
return ((FilterDelegate<?>) delegate).propertyIsBetween(propertyName,
(Date) lower,
(Date) upper);
} else if (lower instanceof Instant && upper instanceof Instant) {
return ((FilterDelegate<?>) delegate).propertyIsBetween(propertyName,
((Instant) lower).getPosition()
.getDate(),
((Instant) upper).getPosition()
.getDate());
} else if (lower instanceof Integer && upper instanceof Integer) {
return ((FilterDelegate<?>) delegate).propertyIsBetween(propertyName,
((Integer) lower).intValue(),
((Integer) upper).intValue());
} else if (lower instanceof Short && upper instanceof Short) {
return ((FilterDelegate<?>) delegate).propertyIsBetween(propertyName,
((Short) lower).shortValue(),
((Short) upper).shortValue());
} else if (lower instanceof Long && upper instanceof Long) {
return ((FilterDelegate<?>) delegate).propertyIsBetween(propertyName,
((Long) lower).longValue(),
((Long) upper).longValue());
} else if (lower instanceof Float && upper instanceof Float) {
return ((FilterDelegate<?>) delegate).propertyIsBetween(propertyName,
((Float) lower).floatValue(),
((Float) upper).floatValue());
} else if (lower instanceof Double && upper instanceof Double) {
return ((FilterDelegate<?>) delegate).propertyIsBetween(propertyName,
((Double) lower).doubleValue(),
((Double) upper).doubleValue());
} else {
return ((FilterDelegate<?>) delegate).propertyIsBetween(propertyName, lower, upper);
}
}
public Object visit(PropertyIsEqualTo filter, Object delegate) {
ExpressionValues filterValues = getExpressions(filter, delegate);
String propertyName = filterValues.propertyName;
Object literal = filterValues.literal;
if (filterValues.proximityArguments != null) {
if (literal.equals(Boolean.TRUE)) {
return ((FilterDelegate<?>) delegate).propertyIsInProximityTo(filterValues.proximityArguments.propertyName,
filterValues.proximityArguments.distance,
filterValues.proximityArguments.searchTerm);
} else {
return ((FilterDelegate<?>) delegate).propertyIsNotInProximityTo(filterValues.proximityArguments.propertyName,
filterValues.proximityArguments.distance,
filterValues.proximityArguments.searchTerm);
}
} else if (literal instanceof String) {
return ((FilterDelegate<?>) delegate).propertyIsEqualTo(propertyName,
(String) literal,
filter.isMatchingCase());
} else if (literal instanceof Date) {
return ((FilterDelegate<?>) delegate).propertyIsEqualTo(propertyName, (Date) literal);
} else if (literal instanceof Instant) {
return ((FilterDelegate<?>) delegate).propertyIsEqualTo(propertyName,
((Instant) literal).getPosition()
.getDate());
} else if (literal instanceof Period) {
return ((FilterDelegate<?>) delegate).propertyIsEqualTo(propertyName,
((Period) literal).getBeginning()
.getPosition()
.getDate(),
((Period) literal).getEnding()
.getPosition()
.getDate());
} else if (literal instanceof Integer) {
return ((FilterDelegate<?>) delegate).propertyIsEqualTo(propertyName,
((Integer) literal).intValue());
} else if (literal instanceof Short) {
return ((FilterDelegate<?>) delegate).propertyIsEqualTo(propertyName,
((Short) literal).shortValue());
} else if (literal instanceof Long) {
return ((FilterDelegate<?>) delegate).propertyIsEqualTo(propertyName,
((Long) literal).longValue());
} else if (literal instanceof Float) {
return ((FilterDelegate<?>) delegate).propertyIsEqualTo(propertyName,
((Float) literal).floatValue());
} else if (literal instanceof Double) {
return ((FilterDelegate<?>) delegate).propertyIsEqualTo(propertyName,
((Double) literal).doubleValue());
} else if (literal instanceof Boolean) {
return ((FilterDelegate<?>) delegate).propertyIsEqualTo(propertyName,
((Boolean) literal).booleanValue());
} else if (literal instanceof byte[]) {
return ((FilterDelegate<?>) delegate).propertyIsEqualTo(propertyName, (byte[]) literal);
} else {
return ((FilterDelegate<?>) delegate).propertyIsEqualTo(propertyName, literal);
}
}
public Object visit(PropertyIsNotEqualTo filter, Object delegate) {
ExpressionValues filterValues = getExpressions(filter, delegate);
String propertyName = filterValues.propertyName;
Object literal = filterValues.literal;
if (literal instanceof String) {
return ((FilterDelegate<?>) delegate).propertyIsNotEqualTo(propertyName,
(String) literal,
filter.isMatchingCase());
} else if (literal instanceof Date) {
return ((FilterDelegate<?>) delegate).propertyIsNotEqualTo(propertyName,
(Date) literal);
} else if (literal instanceof Instant) {
return ((FilterDelegate<?>) delegate).propertyIsNotEqualTo(propertyName,
((Instant) literal).getPosition()
.getDate());
} else if (literal instanceof Period) {
return ((FilterDelegate<?>) delegate).propertyIsNotEqualTo(propertyName,
((Period) literal).getBeginning()
.getPosition()
.getDate(),
((Period) literal).getEnding()
.getPosition()
.getDate());
} else if (literal instanceof Integer) {
return ((FilterDelegate<?>) delegate).propertyIsNotEqualTo(propertyName,
((Integer) literal).intValue());
} else if (literal instanceof Short) {
return ((FilterDelegate<?>) delegate).propertyIsNotEqualTo(propertyName,
((Short) literal).shortValue());
} else if (literal instanceof Long) {
return ((FilterDelegate<?>) delegate).propertyIsNotEqualTo(propertyName,
((Long) literal).longValue());
} else if (literal instanceof Float) {
return ((FilterDelegate<?>) delegate).propertyIsNotEqualTo(propertyName,
((Float) literal).floatValue());
} else if (literal instanceof Double) {
return ((FilterDelegate<?>) delegate).propertyIsNotEqualTo(propertyName,
((Double) literal).doubleValue());
} else if (literal instanceof Boolean) {
return ((FilterDelegate<?>) delegate).propertyIsNotEqualTo(propertyName,
((Boolean) literal).booleanValue());
} else if (literal instanceof byte[]) {
return ((FilterDelegate<?>) delegate).propertyIsNotEqualTo(propertyName,
(byte[]) literal);
} else {
return ((FilterDelegate<?>) delegate).propertyIsNotEqualTo(propertyName, literal);
}
}
public Object visit(PropertyIsGreaterThan filter, Object delegate) {
ExpressionValues filterValues = getExpressions(filter, delegate);
String propertyName = filterValues.propertyName;
Object literal = filterValues.literal;
// Are property name and literal reversed?
if (filter.getExpression1() instanceof Literal) {
// convert literal > property to property < literal
Filter lessThan = FF.less(FF.property(propertyName), FF.literal(literal));
return lessThan.accept(this, delegate);
}
if (literal instanceof String) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThan(propertyName,
(String) literal);
} else if (literal instanceof Date) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThan(propertyName,
(Date) literal);
} else if (literal instanceof Integer) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThan(propertyName,
((Integer) literal).intValue());
} else if (literal instanceof Short) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThan(propertyName,
((Short) literal).shortValue());
} else if (literal instanceof Long) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThan(propertyName,
((Long) literal).longValue());
} else if (literal instanceof Float) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThan(propertyName,
((Float) literal).floatValue());
} else if (literal instanceof Double) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThan(propertyName,
((Double) literal).doubleValue());
} else if (literal instanceof Boolean) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThan(propertyName, literal);
} else if (literal instanceof byte[]) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThan(propertyName, literal);
} else {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThan(propertyName, literal);
}
}
public Object visit(PropertyIsGreaterThanOrEqualTo filter, Object delegate) {
ExpressionValues filterValues = getExpressions(filter, delegate);
String propertyName = filterValues.propertyName;
Object literal = filterValues.literal;
// Are property name and literal reversed?
if (filter.getExpression1() instanceof Literal) {
// convert literal >= property to property <= literal
Filter lessThanOrEqual = FF.lessOrEqual(FF.property(propertyName), FF.literal(literal));
return lessThanOrEqual.accept(this, delegate);
}
if (literal instanceof String) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThanOrEqualTo(propertyName,
(String) literal);
} else if (literal instanceof Date) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThanOrEqualTo(propertyName,
(Date) literal);
} else if (literal instanceof Integer) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThanOrEqualTo(propertyName,
((Integer) literal).intValue());
} else if (literal instanceof Short) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThanOrEqualTo(propertyName,
((Short) literal).shortValue());
} else if (literal instanceof Long) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThanOrEqualTo(propertyName,
((Long) literal).longValue());
} else if (literal instanceof Float) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThanOrEqualTo(propertyName,
((Float) literal).floatValue());
} else if (literal instanceof Double) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThanOrEqualTo(propertyName,
((Double) literal).doubleValue());
} else if (literal instanceof Boolean) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThanOrEqualTo(propertyName,
literal);
} else if (literal instanceof byte[]) {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThanOrEqualTo(propertyName,
literal);
} else {
return ((FilterDelegate<?>) delegate).propertyIsGreaterThanOrEqualTo(propertyName,
literal);
}
}
public Object visit(PropertyIsLessThan filter, Object delegate) {
ExpressionValues filterValues = getExpressions(filter, delegate);
String propertyName = filterValues.propertyName;
Object literal = filterValues.literal;
// Are property name and literal reversed?
if (filter.getExpression1() instanceof Literal) {
// convert literal < property to property > literal
Filter greaterThan = FF.greater(FF.property(propertyName), FF.literal(literal));
return greaterThan.accept(this, delegate);
}
if (literal instanceof String) {
return ((FilterDelegate<?>) delegate).propertyIsLessThan(propertyName,
(String) literal);
} else if (literal instanceof Date) {
return ((FilterDelegate<?>) delegate).propertyIsLessThan(propertyName, (Date) literal);
} else if (literal instanceof Integer) {
return ((FilterDelegate<?>) delegate).propertyIsLessThan(propertyName,
((Integer) literal).intValue());
} else if (literal instanceof Short) {
return ((FilterDelegate<?>) delegate).propertyIsLessThan(propertyName,
((Short) literal).shortValue());
} else if (literal instanceof Long) {
return ((FilterDelegate<?>) delegate).propertyIsLessThan(propertyName,
((Long) literal).longValue());
} else if (literal instanceof Float) {
return ((FilterDelegate<?>) delegate).propertyIsLessThan(propertyName,
((Float) literal).floatValue());
} else if (literal instanceof Double) {
return ((FilterDelegate<?>) delegate).propertyIsLessThan(propertyName,
((Double) literal).doubleValue());
} else if (literal instanceof Boolean) {
return ((FilterDelegate<?>) delegate).propertyIsLessThan(propertyName, literal);
} else if (literal instanceof byte[]) {
return ((FilterDelegate<?>) delegate).propertyIsLessThan(propertyName, literal);
} else {
return ((FilterDelegate<?>) delegate).propertyIsLessThan(propertyName, literal);
}
}
public Object visit(PropertyIsLessThanOrEqualTo filter, Object delegate) {
ExpressionValues filterValues = getExpressions(filter, delegate);
String propertyName = filterValues.propertyName;
Object literal = filterValues.literal;
// Are property name and literal reversed?
if (filter.getExpression1() instanceof Literal) {
// convert literal <= property to property >= literal
Filter greaterThanOrEqual = FF.greaterOrEqual(FF.property(propertyName), FF.literal(
literal));
return greaterThanOrEqual.accept(this, delegate);
}
if (literal instanceof String) {
return ((FilterDelegate<?>) delegate).propertyIsLessThanOrEqualTo(propertyName,
(String) literal);
} else if (literal instanceof Date) {
return ((FilterDelegate<?>) delegate).propertyIsLessThanOrEqualTo(propertyName,
(Date) literal);
} else if (literal instanceof Integer) {
return ((FilterDelegate<?>) delegate).propertyIsLessThanOrEqualTo(propertyName,
((Integer) literal).intValue());
} else if (literal instanceof Short) {
return ((FilterDelegate<?>) delegate).propertyIsLessThanOrEqualTo(propertyName,
((Short) literal).shortValue());
} else if (literal instanceof Long) {
return ((FilterDelegate<?>) delegate).propertyIsLessThanOrEqualTo(propertyName,
((Long) literal).longValue());
} else if (literal instanceof Float) {
return ((FilterDelegate<?>) delegate).propertyIsLessThanOrEqualTo(propertyName,
((Float) literal).floatValue());
} else if (literal instanceof Double) {
return ((FilterDelegate<?>) delegate).propertyIsLessThanOrEqualTo(propertyName,
((Double) literal).doubleValue());
} else if (literal instanceof Boolean) {
return ((FilterDelegate<?>) delegate).propertyIsLessThanOrEqualTo(propertyName,
literal);
} else if (literal instanceof byte[]) {
return ((FilterDelegate<?>) delegate).propertyIsLessThanOrEqualTo(propertyName,
literal);
} else {
return ((FilterDelegate<?>) delegate).propertyIsLessThanOrEqualTo(propertyName,
literal);
}
}
public Object visit(PropertyIsLike filter, Object delegate) {
String propertyName;
String wildcard = filter.getWildCard();
String singleChar = filter.getSingleChar();
String escapeChar = filter.getEscape();
if (filter.getExpression() == null || filter.getLiteral() == null) {
throw new UnsupportedOperationException(
"Expression and Literal must not be null for PropertyIsLike.");
}
if (wildcard.length() > 1 || singleChar.length() > 1 || escapeChar.length() > 1) {
throw new UnsupportedOperationException(
"Wildcard, single, and escape characters must be a single character for PropertyIsLike.");
}
if (wildcard.equals(singleChar) || wildcard.equals(escapeChar) || singleChar.equals(
escapeChar)) {
throw new UnsupportedOperationException(
"Wildcard, single, and escape characters must be different for PropertyIsLike.");
}
String pattern = normalizePattern(filter.getLiteral(), wildcard, singleChar, escapeChar);
boolean matchCase = filter.isMatchingCase();
boolean isFuzzy = false;
if (filter.getExpression() instanceof FuzzyFunction) {
FuzzyFunction fuzzy = (FuzzyFunction) filter.getExpression();
propertyName = ((PropertyName) (fuzzy.getParameters()
.get(0))).getPropertyName();
isFuzzy = true;
} else if (filter.getExpression() instanceof PropertyName) {
PropertyName expression = (PropertyName) filter.getExpression();
propertyName = expression.getPropertyName();
} else {
throw new UnsupportedOperationException(
"Only support PropertyName expression for PropertyIsLike filter.");
}
boolean isXpathSearch =
(propertyName.indexOf('/') != -1 || propertyName.indexOf('@') != -1);
if (!isFuzzy && !isXpathSearch) {
return ((FilterDelegate<?>) delegate).propertyIsLike(propertyName, pattern, matchCase);
} else if (isFuzzy && !isXpathSearch) {
// TODO check if wildcards are escaped
return ((FilterDelegate<?>) delegate).propertyIsFuzzy(propertyName, pattern);
} else if (!isFuzzy && isXpathSearch) {
if (pattern.trim()
.isEmpty() || pattern.trim()
.equals(FilterDelegate.WILDCARD_CHAR)) {
return ((FilterDelegate<?>) delegate).xpathExists(propertyName);
} else {
return ((FilterDelegate<?>) delegate).xpathIsLike(propertyName, pattern, matchCase);
}
} else if (isFuzzy && isXpathSearch) {
// TODO check if wildcards are escaped
return ((FilterDelegate<?>) delegate).xpathIsFuzzy(propertyName, pattern);
} else {
throw new UnsupportedOperationException("Unsupported operands for PropertyIsLike.");
}
}
private String normalizePattern(String pattern, String wildcard, String singleChar,
String escapeChar) {
StringBuilder sb = new StringBuilder(pattern.length());
for (int i = 0; i < pattern.length(); i++) {
char c = pattern.charAt(i);
if (c == escapeChar.charAt(0)) {
if (i + 1 < pattern.length()) {
i++;
String next = Character.toString(pattern.charAt(i));
if (next.equals(FilterDelegate.WILDCARD_CHAR)
|| next.equals(FilterDelegate.SINGLE_CHAR)
|| next.equals(FilterDelegate.ESCAPE_CHAR)) {
// target normalized character needs to be escaped
sb.append(FilterDelegate.ESCAPE_CHAR);
sb.append(next);
} else {
// escaped character is not a normalized character
// and does not need to be escaped anymore
sb.append(next);
}
}
} else if (c == singleChar.charAt(0)) {
sb.append(FilterDelegate.SINGLE_CHAR);
} else if (c == wildcard.charAt(0)) {
sb.append(FilterDelegate.WILDCARD_CHAR);
} else {
sb.append(c);
}
}
return sb.toString();
}
public Object visit(PropertyIsNull filter, Object delegate) {
if (filter.getExpression() instanceof PropertyName) {
String propertyName = (String) filter.getExpression()
.accept(this, delegate);
return ((FilterDelegate<?>) delegate).propertyIsNull(propertyName);
} else {
throw new UnsupportedOperationException(
"Only support PropertyName expressions for PropertyIsNull.");
}
}
@Override
public Object visit(PropertyIsNil arg0, Object arg1) {
throw new UnsupportedOperationException(
PropertyIsNil.NAME + " filter is not supported by Filter Adapter.");
}
public Object visit(BBOX filter, Object delegate) {
throw new UnsupportedOperationException(
BBOX.NAME + " filter is not supported by Filter Adapter.");
}
public Object visit(Beyond filter, Object delegate) {
double distance = normalizeDistance(filter.getDistance(), filter.getDistanceUnits());
ExpressionValues filterValues = getExpressions(filter, delegate);
String wkt = geometryToWkt(filterValues.literal);
if (distance != 0) {
return ((FilterDelegate<?>) delegate).beyond(filterValues.propertyName, wkt, distance);
} else {
return ((FilterDelegate<?>) delegate).nearestNeighbor(filterValues.propertyName, wkt);
}
}
public Object visit(Contains filter, Object delegate) {
ExpressionValues filterValues = getExpressions(filter, delegate);
String wkt = geometryToWkt(filterValues.literal);
return ((FilterDelegate<?>) delegate).contains(filterValues.propertyName, wkt);
}
public Object visit(DWithin filter, Object delegate) {
double distance = normalizeDistance(filter.getDistance(), filter.getDistanceUnits());
ExpressionValues filterValues = getExpressions(filter, delegate);
String wkt = geometryToWkt(filterValues.literal);
return ((FilterDelegate<?>) delegate).dwithin(filterValues.propertyName, wkt, distance);
}
public Object visit(Intersects filter, Object delegate) {
ExpressionValues filterValues = getExpressions(filter, delegate);
String wkt = geometryToWkt(filterValues.literal);
return ((FilterDelegate<?>) delegate).intersects(filterValues.propertyName, wkt);
}
public Object visit(Within filter, Object delegate) {
ExpressionValues filterValues = getExpressions(filter, delegate);
String wkt = geometryToWkt(filterValues.literal);
return ((FilterDelegate<?>) delegate).within(filterValues.propertyName, wkt);
}
public Object visit(Crosses filter, Object delegate) {
ExpressionValues filterValues = getExpressions(filter, delegate);
String wkt = geometryToWkt(filterValues.literal);
return ((FilterDelegate<?>) delegate).crosses(filterValues.propertyName, wkt);
}
public Object visit(Disjoint filter, Object delegate) {
ExpressionValues filterValues = getExpressions(filter, delegate);
String wkt = geometryToWkt(filterValues.literal);
return ((FilterDelegate<?>) delegate).disjoint(filterValues.propertyName, wkt);
}
public Object visit(Overlaps filter, Object delegate) {
ExpressionValues filterValues = getExpressions(filter, delegate);
String wkt = geometryToWkt(filterValues.literal);
return ((FilterDelegate<?>) delegate).overlaps(filterValues.propertyName, wkt);
}
public Object visit(Touches filter, Object delegate) {
ExpressionValues filterValues = getExpressions(filter, delegate);
String wkt = geometryToWkt(filterValues.literal);
return ((FilterDelegate<?>) delegate).touches(filterValues.propertyName, wkt);
}
private double normalizeDistance(double distance, String distanceUnits) {
if (UomOgcMapping.FOOT.name()
.equals(distanceUnits) || CQL_FEET.equals(distanceUnits)) {
return new Distance(distance, LinearUnit.FOOT_U_S).getAs(LinearUnit.METER);
} else if (UomOgcMapping.METRE.name()
.equals(distanceUnits) || CQL_METERS.equals(distanceUnits)) {
return distance;
} else if (CQL_STATUTE_MILES.equals(distanceUnits)) {
return new Distance(distance, LinearUnit.MILE).getAs(LinearUnit.METER);
} else if (CQL_NAUTICAL_MILES.equals(distanceUnits)) {
return new Distance(distance, LinearUnit.NAUTICAL_MILE).getAs(LinearUnit.METER);
} else if (CQL_KILOMETERS.equals(distanceUnits)) {
return new Distance(distance, LinearUnit.KILOMETER).getAs(LinearUnit.METER);
} else {
throw new UnsupportedOperationException("Unknown units used in spatial filter");
}
}
private String geometryToWkt(Object literal) {
String wkt;
// TODO should support OpenGIS Geometry interface and reconstruct the
// WKT from the getBoundary method
if (literal instanceof GeometryImpl) {
GeometryImpl surface = (GeometryImpl) literal;
com.vividsolutions.jts.geom.Geometry jtsGeometry = surface.getJTSGeometry();
wkt = jtsGeometry.toText();
} else if (literal instanceof com.vividsolutions.jts.geom.Geometry) {
com.vividsolutions.jts.geom.Geometry jtsGeometry =
(com.vividsolutions.jts.geom.Geometry) literal;
wkt = jtsGeometry.toText();
} else {
throw new UnsupportedOperationException(
"Unsupported implementation of Geometry for spatial filters.");
}
return wkt;
}
public Object visit(Equals filter, Object delegate) {
throw new UnsupportedOperationException(
"Spatial Equals filter not supported by Filter Adapter.");
}
public Object visit(After after, Object delegate) {
ExpressionValues filterValues = getExpressions(after, delegate);
String propertyName = filterValues.propertyName;
Object literal = filterValues.literal;
if (literal instanceof Date) {
return ((FilterDelegate<?>) delegate).after(propertyName, (Date) literal);
} else if (literal instanceof Instant) {
return ((FilterDelegate<?>) delegate).after(propertyName,
((Instant) literal).getPosition()
.getDate());
} else if (literal instanceof Period) {
return ((FilterDelegate<?>) delegate).after(propertyName,
((Period) literal).getEnding()
.getPosition()
.getDate());
} else {
throw new UnsupportedOperationException(
"Unsupported implementation of date/time for After filter.");
}
}
public Object visit(Before before, Object delegate) {
ExpressionValues filterValues = getExpressions(before, delegate);
String propertyName = filterValues.propertyName;
Object literal = filterValues.literal;
if (literal instanceof Date) {
return ((FilterDelegate<?>) delegate).before(propertyName, (Date) literal);
} else if (literal instanceof Instant) {
return ((FilterDelegate<?>) delegate).before(propertyName,
((Instant) literal).getPosition()
.getDate());
} else if (literal instanceof Period) {
return ((FilterDelegate<?>) delegate).before(propertyName,
((Period) literal).getBeginning()
.getPosition()
.getDate());
} else {
throw new UnsupportedOperationException(
"Unsupported implementation of date/time for Before filter.");
}
}
public Object visit(During during, Object delegate) {
ExpressionValues filterValues = getExpressions(during, delegate);
// Absolute
if (filterValues.literal instanceof Period) {
Period period = (Period) filterValues.literal;
Date start = period.getBeginning()
.getPosition()
.getDate();
Date end = period.getEnding()
.getPosition()
.getDate();
return ((FilterDelegate<?>) delegate).during(filterValues.propertyName, start, end);
// Relative
} else if (filterValues.literal instanceof DefaultPeriodDuration) {
// TODO should support PeriodDuration and reconstruct the duration
// instead of using an implementation to get the milliseconds
DefaultPeriodDuration duration = (DefaultPeriodDuration) filterValues.literal;
return ((FilterDelegate<?>) delegate).relative(filterValues.propertyName,
duration.getTimeInMillis());
} else {
throw new UnsupportedOperationException(
"Unsupported implementation of Period or PeriodDuration for During filter.");
}
}
public Object visit(AnyInteracts anyInteracts, Object delegate) {
throw new UnsupportedOperationException(
AnyInteracts.NAME + " filter not supported by Filter Adapter.");
}
public Object visit(Begins begins, Object delegate) {
ExpressionValues filterValues = getExpressions(begins, delegate);
if (filterValues.literal instanceof Period) {
Period period = (Period) filterValues.literal;
Date start = period.getBeginning()
.getPosition()
.getDate();
Date end = period.getEnding()
.getPosition()
.getDate();
return ((FilterDelegate<?>) delegate).begins(filterValues.propertyName, start, end);
} else {
throw new UnsupportedOperationException(
Begins.NAME + "filter not supported by Filter Adapter.");
}
}
public Object visit(BegunBy begunBy, Object delegate) {
throw new UnsupportedOperationException(
BegunBy.NAME + " filter not supported by Filter Adapter.");
}
public Object visit(EndedBy endedBy, Object delegate) {
throw new UnsupportedOperationException(
EndedBy.NAME + " filter not supported by Filter Adapter.");
}
public Object visit(Ends ends, Object delegate) {
throw new UnsupportedOperationException(
Ends.NAME + " filter not supported by Filter Adapter.");
}
public Object visit(Meets meets, Object delegate) {
throw new UnsupportedOperationException(
Meets.NAME + " filter not supported by Filter Adapter.");
}
public Object visit(MetBy metBy, Object delegate) {
throw new UnsupportedOperationException(
MetBy.NAME + " filter not supported by Filter Adapter.");
}
public Object visit(OverlappedBy overlappedBy, Object delegate) {
throw new UnsupportedOperationException(
OverlappedBy.NAME + " filter not supported by Filter Adapter.");
}
public Object visit(TContains contains, Object delegate) {
throw new UnsupportedOperationException(
TContains.NAME + " filter not supported by Filter Adapter.");
}
public Object visit(TEquals equals, Object delegate) {
throw new UnsupportedOperationException(
TEquals.NAME + " filter not supported by Filter Adapter.");
}
public Object visit(TOverlaps contains, Object delegate) {
throw new UnsupportedOperationException(
TOverlaps.NAME + " filter not supported by Filter Adapter.");
}
private ExpressionValues getExpressions(BinarySpatialOperator filter, Object delegate) {
return getExpressions(filter.getExpression1(), filter.getExpression2(), delegate);
}
private ExpressionValues getExpressions(BinaryComparisonOperator filter, Object delegate) {
return getExpressions(filter.getExpression1(), filter.getExpression2(), delegate);
}
private ExpressionValues getExpressions(BinaryTemporalOperator filter, Object delegate) {
return getExpressions(filter.getExpression1(), filter.getExpression2(), delegate);
}
private ExpressionValues getExpressions(Expression expression1, Expression expression2,
Object delegate) {
String propertyName;
Object literal;
if (expression1 instanceof Function && ProximityFunction.NAME.getName()
.equals(((Function) expression1).getFunctionName()
.getName()) && expression2 instanceof Literal) {
return new ExpressionValues((ProximityArguments) expression1.accept(this, delegate),
expression2.accept(this, delegate));
} else if (expression1 instanceof PropertyName && expression2 instanceof Literal) {
propertyName = (String) expression1.accept(this, delegate);
literal = expression2.accept(this, delegate);
} else if (expression1 instanceof Literal && expression2 instanceof PropertyName) {
literal = expression1.accept(this, delegate);
propertyName = (String) expression2.accept(this, delegate);
} else {
throw new UnsupportedOperationException(
"Only support PropertyName and Literal expressions for binary filters.");
}
return new ExpressionValues(propertyName, literal);
}
private static class ExpressionValues {
public String propertyName;
public Object literal;
public ProximityArguments proximityArguments;
public ExpressionValues(ProximityArguments proximityArguments, Object literal) {
this.proximityArguments = proximityArguments;
this.literal = literal;
}
public ExpressionValues(String propertyName, Object literal) {
this.propertyName = propertyName;
this.literal = literal;
}
}
private static class ProximityArguments {
public String propertyName;
public Integer distance;
public String searchTerm;
public ProximityArguments(String propertyName, Integer distance, String searchTerm) {
this.propertyName = propertyName;
this.distance = distance;
this.searchTerm = searchTerm;
}
}
}