package org.geotools.filter.visitor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.opengis.filter.BinaryComparisonOperator; import org.opengis.filter.Filter; import org.opengis.filter.MultiValuedFilter; import org.opengis.filter.MultiValuedFilter.MatchAction; 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.PropertyIsNotEqualTo; import org.opengis.filter.expression.Expression; import org.opengis.filter.expression.Literal; 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; /** * This visitor gets rid of equations that contain literals with multiple values (collections) * and creates instead multiple singe value equations, * replacing the ANY, ALL, ONE logic by AND, OR, NOT logic * * @author Niels Charlier * */ public class LiteralDemultiplyingFilterVisitor extends DuplicatingFilterVisitor { /** * This interface is in support of a generic function (demultiply) that gets rid of the multi-valued literals, with any type of filter * that takes two expressions. */ protected static interface FilterReplacer<F extends MultiValuedFilter> { public Expression getExpression1(F filter); public Expression getExpression2(F filter); /** * Replace the expressions in a filter */ public Filter replaceExpressions(F filter, Expression expression1, Expression expression2); } /** * An implementation for Binary Comparison Operators * Takes the method name in the FilterFactory to create the filter * */ protected class BinaryComparisonOperatorReplacer implements FilterReplacer<BinaryComparisonOperator> { protected Method method; public BinaryComparisonOperatorReplacer(String methodName){ try { method = ff.getClass().getMethod(methodName, Expression.class, Expression.class, boolean.class, MatchAction.class); } catch (Exception e) { throw new RuntimeException(e); } } @Override public Expression getExpression1(BinaryComparisonOperator filter) { return filter.getExpression1(); } @Override public Expression getExpression2(BinaryComparisonOperator filter) { return filter.getExpression2(); } @Override public Filter replaceExpressions(BinaryComparisonOperator filter, Expression expression1, Expression expression2) { try { return (Filter) method.invoke (ff, expression1, expression2, filter.isMatchingCase(), filter.getMatchAction() ); } catch (Exception e) { throw new RuntimeException(e); } } } /** * An implementation for Binary Spatial Operators * Takes the method name in the FilterFactory to create the filter * */ protected class BinarySpatialOperatorReplacer implements FilterReplacer<BinarySpatialOperator> { protected Method method; public BinarySpatialOperatorReplacer(String methodName){ try { method = ff.getClass().getMethod(methodName, Expression.class, Expression.class, MatchAction.class); } catch (Exception e) { throw new RuntimeException(e); } } @Override public Expression getExpression1(BinarySpatialOperator filter) { return filter.getExpression1(); } @Override public Expression getExpression2(BinarySpatialOperator filter) { return filter.getExpression2(); } @Override public Filter replaceExpressions(BinarySpatialOperator filter, Expression expression1, Expression expression2) { try { return (Filter) method.invoke (ff, expression1, expression2, filter.getMatchAction() ); } catch (Exception e) { throw new RuntimeException(e); } } } /** * An implementation for Binary Temporal Operators * Takes the method name in the FilterFactory to create the filter * */ protected class BinaryTemporalOperatorReplacer implements FilterReplacer<BinaryTemporalOperator> { protected Method method; public BinaryTemporalOperatorReplacer(String methodName){ try { method = ff.getClass().getMethod(methodName, Expression.class, Expression.class, MatchAction.class); } catch (Exception e) { throw new RuntimeException(e); } } @Override public Expression getExpression1(BinaryTemporalOperator filter) { return filter.getExpression1(); } @Override public Expression getExpression2(BinaryTemporalOperator filter) { return filter.getExpression2(); } @Override public Filter replaceExpressions(BinaryTemporalOperator filter, Expression expression1, Expression expression2) { try { return (Filter) method.invoke (ff, expression1, expression2, filter.getMatchAction() ); } catch (Exception e) { throw new RuntimeException(e); } } } /** * demultiplies the first expression * * @param filter The filter * @param replacer The filter replacer * @return the new filter */ protected<T extends MultiValuedFilter> Filter demultiplyFirst( T filter, FilterReplacer<T> replacer) { Expression one = replacer.getExpression1(filter); Expression two = replacer.getExpression2(filter); if (one instanceof Literal) { Literal l = (Literal) one; Object value = l.getValue(); if (value instanceof Collection) { //demultiplying is necessary List<Filter> filters = new ArrayList<Filter>(); //list of all filters for (Object valueElement : (Collection) value) { //create a single-valued new filter filters.add(replacer.replaceExpressions(filter, ff.literal(valueElement), two)); } //merge the filters depending on match action if (filter.getMatchAction() == MatchAction.ANY) { return ff.or(filters); } else if (filter.getMatchAction() == MatchAction.ALL) { return ff.and(filters); } else if (filter.getMatchAction() == MatchAction.ONE) { List<Filter> filters2 = new ArrayList<Filter>(); for (int i = 0; i < filters.size(); i++) { List<Filter> filters3 = new ArrayList<Filter>(); for (int j = 0; j < filters.size(); j++) { if (i==j) { filters3.add(filters.get(j)); } else { filters3.add(ff.not(filters.get(j))); } } filters2.add(ff.and(filters3)); } return ff.or(filters2); } } } return filter; } /** * Demultiplies first and second expression * * @param filter * @param replacer * @return */ protected<T extends MultiValuedFilter> Filter demultiply( T filter, FilterReplacer<T> replacer) { Expression one = replacer.getExpression1(filter); Expression two = replacer.getExpression2(filter); if (two instanceof Literal) { Literal l = (Literal) two; Object value = l.getValue(); if (value instanceof Collection) { //demultiplying is necessary List<Filter> filters = new ArrayList<Filter>(); //list of all filters for (Object valueElement : (Collection) value) { //create a single-valued new filter filters.add(demultiplyFirst((T) replacer.replaceExpressions(filter, one, ff.literal(valueElement)), replacer)); } //merge the filters depending on match action if (filter.getMatchAction() == MatchAction.ANY) { return ff.or(filters); } else if (filter.getMatchAction() == MatchAction.ALL) { return ff.and(filters); } else if (filter.getMatchAction() == MatchAction.ONE) { List<Filter> filters2 = new ArrayList<Filter>(); for (int i = 0; i < filters.size(); i++) { List<Filter> filters3 = new ArrayList<Filter>(); for (int j = 0; j < filters.size(); j++) { if (i==j) { filters3.add(filters.get(j)); } else { filters3.add(ff.not(filters.get(j))); } } filters2.add(ff.and(filters3)); } return ff.or(filters2); } } } return demultiplyFirst(filter, replacer); } @Override public Object visit(PropertyIsBetween filter, Object extraData) { // TODO: support ProperyIsBetween (there are three expressions here) return super.visit(filter, extraData); } @Override public Object visit(PropertyIsEqualTo filter, Object extraData) { return demultiply(filter, new BinaryComparisonOperatorReplacer("equal")); } @Override public Object visit(PropertyIsNotEqualTo filter, Object extraData) { return demultiply(filter, new BinaryComparisonOperatorReplacer("notEqual")); } @Override public Object visit(PropertyIsGreaterThan filter, Object extraData) { return demultiply(filter, new BinaryComparisonOperatorReplacer("greater")); } @Override public Object visit(PropertyIsGreaterThanOrEqualTo filter, Object extraData) { return demultiply(filter, new BinaryComparisonOperatorReplacer("greaterOrEqual")); } @Override public Object visit(PropertyIsLessThan filter, Object extraData) { return demultiply(filter, new BinaryComparisonOperatorReplacer("less")); } @Override public Object visit(PropertyIsLessThanOrEqualTo filter, Object extraData) { return demultiply(filter, new BinaryComparisonOperatorReplacer("lessOrEqual")); } @Override public Object visit(BBOX filter, Object extraData) { return demultiply(filter, new BinarySpatialOperatorReplacer("bbox")); } @Override public Object visit(Beyond filter, Object extraData) { return demultiply(filter, new FilterReplacer<Beyond>(){ //beyond filter takes extra properties, therefore needs its own filterreplacer @Override public Expression getExpression1(Beyond filter) { return filter.getExpression1(); } @Override public Expression getExpression2(Beyond filter) { return filter.getExpression2(); } @Override public Filter replaceExpressions(Beyond filter, Expression expression1, Expression expression2) { return ff.beyond(expression1, expression2, filter.getDistance(), filter.getDistanceUnits(), filter.getMatchAction()); } }); } @Override public Object visit(Contains filter, Object extraData) { return demultiply(filter, new BinarySpatialOperatorReplacer("contains")); } @Override public Object visit(Crosses filter, Object extraData) { return demultiply(filter, new BinarySpatialOperatorReplacer("crosses")); } @Override public Object visit(Disjoint filter, Object extraData) { return demultiply(filter, new BinarySpatialOperatorReplacer("disjoint")); } @Override public Object visit(DWithin filter, Object extraData) { return demultiply(filter, new FilterReplacer<DWithin>(){ //DWithin filter takes extra properties, therefore needs its own filterreplacer @Override public Expression getExpression1(DWithin filter) { return filter.getExpression1(); } @Override public Expression getExpression2(DWithin filter) { return filter.getExpression2(); } @Override public Filter replaceExpressions(DWithin filter, Expression expression1, Expression expression2) { return ff.dwithin(expression1, expression2, filter.getDistance(), filter.getDistanceUnits(), filter.getMatchAction()); } }); } @Override public Object visit(Equals filter, Object extraData) { return demultiply(filter, new BinarySpatialOperatorReplacer("equal")); } @Override public Object visit(Intersects filter, Object extraData) { return demultiply(filter, new BinarySpatialOperatorReplacer("intersects")); } @Override public Object visit(Overlaps filter, Object extraData) { return demultiply(filter, new BinarySpatialOperatorReplacer("overlaps")); } @Override public Object visit(Touches filter, Object extraData) { return demultiply(filter, new BinarySpatialOperatorReplacer("touches")); } @Override public Object visit(Within filter, Object extraData) { return demultiply(filter, new BinarySpatialOperatorReplacer("within")); } @Override public Object visit(After after, Object extraData) { return demultiply(after, new BinaryTemporalOperatorReplacer("after")); } @Override public Object visit(AnyInteracts anyInteracts, Object extraData) { return demultiply(anyInteracts, new BinaryTemporalOperatorReplacer("anyInteracts")); } @Override public Object visit(Before before, Object extraData) { return demultiply(before, new BinaryTemporalOperatorReplacer("before")); } @Override public Object visit(Begins begins, Object extraData) { return demultiply(begins, new BinaryTemporalOperatorReplacer("begins")); } @Override public Object visit(BegunBy begunBy, Object extraData) { return demultiply(begunBy, new BinaryTemporalOperatorReplacer("begunBy")); } @Override public Object visit(During during, Object extraData) { return demultiply(during, new BinaryTemporalOperatorReplacer("during")); } @Override public Object visit(EndedBy endedBy, Object extraData) { return demultiply(endedBy, new BinaryTemporalOperatorReplacer("endedBy")); } @Override public Object visit(Ends ends, Object extraData) { return demultiply(ends, new BinaryTemporalOperatorReplacer("ends")); } @Override public Object visit(Meets meets, Object extraData) { return demultiply(meets, new BinaryTemporalOperatorReplacer("meets")); } @Override public Object visit(MetBy metBy, Object extraData) { return demultiply(metBy, new BinaryTemporalOperatorReplacer("metBy")); } @Override public Object visit(OverlappedBy overlappedBy, Object extraData) { return demultiply(overlappedBy, new BinaryTemporalOperatorReplacer("overlappedBy")); } @Override public Object visit(TContains contains, Object extraData) { return demultiply(contains, new BinaryTemporalOperatorReplacer("tcontains")); } @Override public Object visit(TEquals equals, Object extraData) { return demultiply(equals, new BinaryTemporalOperatorReplacer("tequals")); } @Override public Object visit(TOverlaps overlaps, Object extraData) { return demultiply(overlaps, new BinaryTemporalOperatorReplacer("toverlaps")); } }