/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.wps.hz; import static com.hazelcast.query.Predicates.*; import java.util.ArrayList; import java.util.List; import org.apache.commons.collections.functors.FalsePredicate; import org.geotools.filter.LikeToRegexConverter; import org.opengis.filter.And; import org.opengis.filter.BinaryComparisonOperator; 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.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.Expression; import org.opengis.filter.expression.Literal; import org.opengis.filter.expression.PropertyName; import org.opengis.filter.spatial.BBOX; import org.opengis.filter.spatial.Beyond; 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 com.hazelcast.query.Predicate; import com.hazelcast.query.TruePredicate; /** * Converts a OGC Filter to the Hazelcast Criteria API (a {@link Predicate}). * * @author Andrea Aime - GeoSolutions * */ public class FilterToCriteria implements FilterVisitor { private class PropertyComparable { String property; Comparable literal; boolean inverted; public PropertyComparable(BinaryComparisonOperator op) { if (op.getExpression1() instanceof PropertyName && op.getExpression2() instanceof Literal) { property = getPropertyName(op.getExpression1()); literal = (Comparable) ((Literal) op.getExpression2()).getValue(); } else if (op.getExpression2() instanceof PropertyName && op.getExpression1() instanceof Literal) { property = getPropertyName(op.getExpression2()); literal = (Comparable) ((Literal) op.getExpression1()).getValue(); inverted = true; } else { throw new IllegalArgumentException( "Unsupported comparison, only comparison between an attribute and a static value are supported: " + op); } } public PropertyComparable(BinaryTemporalOperator op) { if (op.getExpression1() instanceof PropertyName && op.getExpression2() instanceof Literal) { property = ((PropertyName) op.getExpression1()).getPropertyName(); literal = (Comparable) ((Literal) op.getExpression2()).getValue(); } else if (op.getExpression2() instanceof PropertyName && op.getExpression1() instanceof Literal) { property = ((PropertyName) op.getExpression2()).getPropertyName(); literal = (Comparable) ((Literal) op.getExpression1()).getValue(); inverted = true; } else { throw new IllegalArgumentException( "Unsupported comparison, only comparison between an attribute and a static value are supported: " + op); } } } @Override public Object visitNullFilter(Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(ExcludeFilter filter, Object extraData) { return FalsePredicate.INSTANCE; } @Override public Object visit(IncludeFilter filter, Object extraData) { // we are going to filter against ExecutionStatus, this ensure we match everything return TruePredicate.INSTANCE; } @Override public Object visit(And filter, Object extraData) { List<Predicate> predicates = new ArrayList<>(); for (Filter child : filter.getChildren()) { predicates.add((Predicate) child.accept(this, extraData)); } return and(predicates.toArray(new Predicate[predicates.size()])); } @Override public Object visit(Id filter, Object extraData) { List<String> ids = new ArrayList<>(); for (Object id : filter.getIDs()) { ids.add(id.toString()); } return in("executionId", ids.toArray(new String[ids.size()])); } @Override public Object visit(Not filter, Object extraData) { return not((Predicate) filter.getFilter().accept(this, extraData)); } @Override public Object visit(Or filter, Object extraData) { List<Predicate> predicates = new ArrayList<>(); for (Filter child : filter.getChildren()) { predicates.add((Predicate) child.accept(this, extraData)); } return or(predicates.toArray(new Predicate[predicates.size()])); } @Override public Object visit(PropertyIsBetween filter, Object extraData) { String propertyName = getPropertyName(filter.getExpression()); Comparable low = (Comparable) ((Literal) filter.getLowerBoundary()).getValue(); Comparable high = (Comparable) ((Literal) filter.getLowerBoundary()).getValue(); return between(propertyName, low, high); } private String getPropertyName(Expression expression) { if (!(expression instanceof PropertyName)) { throw new IllegalArgumentException("Was expecting a property name, but found: " + expression); } String propertyName = ((PropertyName) expression).getPropertyName(); if ("processName".equals(propertyName)) { propertyName = "simpleProcessName"; } return propertyName; } @Override public Object visit(PropertyIsEqualTo filter, Object extraData) { PropertyComparable components = new PropertyComparable(filter); return equal(components.property, components.literal); } @Override public Object visit(PropertyIsNotEqualTo filter, Object extraData) { PropertyComparable components = new PropertyComparable(filter); return not(equal(components.property, components.literal)); } @Override public Object visit(PropertyIsGreaterThan filter, Object extraData) { PropertyComparable components = new PropertyComparable(filter); return components.inverted ? lessEqual(components.property, components.literal) : greaterThan(components.property, components.literal); } @Override public Object visit(PropertyIsGreaterThanOrEqualTo filter, Object extraData) { PropertyComparable components = new PropertyComparable(filter); return components.inverted ? lessThan(components.property, components.literal) : greaterEqual(components.property, components.literal); } @Override public Object visit(PropertyIsLessThan filter, Object extraData) { PropertyComparable components = new PropertyComparable(filter); return components.inverted ? greaterEqual(components.property, components.literal) : lessThan(components.property, components.literal); } @Override public Object visit(PropertyIsLessThanOrEqualTo filter, Object extraData) { PropertyComparable components = new PropertyComparable(filter); return components.inverted ? greaterThan(components.property, components.literal) : lessEqual(components.property, components.literal); } @Override public Object visit(PropertyIsLike filter, Object extraData) { String propertyName = getPropertyName(filter.getExpression()); String pattern = new LikeToRegexConverter(filter).getPattern(); if (filter.isMatchingCase()) { return like(propertyName, pattern); } else { return ilike(propertyName, pattern); } } @Override public Object visit(PropertyIsNull filter, Object extraData) { String propertyName = getPropertyName(filter.getExpression()); return equal(propertyName, null); } @Override public Object visit(PropertyIsNil filter, Object extraData) { String propertyName = getPropertyName(filter.getExpression()); return equal(propertyName, null); } @Override public Object visit(BBOX filter, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(Beyond filter, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(Contains filter, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(Crosses filter, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(Disjoint filter, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(DWithin filter, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(Equals filter, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(Intersects filter, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(Overlaps filter, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(Touches filter, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(Within filter, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(After after, Object extraData) { PropertyComparable components = new PropertyComparable(after); return components.inverted ? lessEqual(components.property, components.literal) : greaterThan(components.property, components.literal); } @Override public Object visit(AnyInteracts anyInteracts, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(Before before, Object extraData) { PropertyComparable components = new PropertyComparable(before); return components.inverted ? greaterEqual(components.property, components.literal) : lessThan(components.property, components.literal); } @Override public Object visit(Begins begins, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(BegunBy begunBy, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(During during, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(EndedBy endedBy, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(Ends ends, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(Meets meets, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(MetBy metBy, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(OverlappedBy overlappedBy, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(TContains contains, Object extraData) { throw new UnsupportedOperationException(); } @Override public Object visit(TEquals equals, Object extraData) { PropertyComparable components = new PropertyComparable(equals); return equal(components.property, components.literal); } @Override public Object visit(TOverlaps contains, Object extraData) { throw new UnsupportedOperationException(); } }