/* Copyright 2013 The jeo project. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.jeo.filter; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.Iterator; import java.util.LinkedList; import java.util.List; /** * Builder for filters that can compose complex filter / expression trees. * <p> * Example usage: * <pre> * Filter f = new FilterBuilder() * .property("foo").literal(10).gt() * .property("bar").literal("blah").eq() * .or(); * </pre> * Would result in the filter <tt>[foo] > 10 OR [bar] = "blah"</tt> * </p> * @author Justin Deoliveira, OpenGeo */ public class FilterBuilder { protected Deque<Object> stack = new ArrayDeque<Object>(); public FilterBuilder() { } public FilterBuilder literal(Object o) { stack.push(new Literal(o)); return this; } public FilterBuilder property(String prop) { stack.push(new Property(prop)); return this; } public FilterBuilder eq() { return cmp(Comparison.Type.EQUAL); } public FilterBuilder neq() { return cmp(Comparison.Type.NOT_EQUAL); } public FilterBuilder lt() { return cmp(Comparison.Type.LESS); } public FilterBuilder lte() { return cmp(Comparison.Type.LESS_OR_EQUAL); } public FilterBuilder gt() { return cmp(Comparison.Type.GREATER); } public FilterBuilder gte() { return cmp(Comparison.Type.GREATER_OR_EQUAL); } public FilterBuilder between() { Expression high = (Expression) stack.pop(); Expression low = (Expression) stack.pop(); Expression e = (Expression) stack.pop(); stack.push(new Comparison<Object>(Comparison.Type.GREATER_OR_EQUAL, e, low). and(new Comparison<Object>(Comparison.Type.LESS_OR_EQUAL, e, high))); return this; } public FilterBuilder id() { List<Expression> ids = new ArrayList<Expression>(); while(!stack.isEmpty() && stack.peek() instanceof Expression) { ids.add((Expression) stack.pop()); } stack.push(new Id<Object>(ids)); return this; } public FilterBuilder and() { return log(Logic.Type.AND); } public FilterBuilder or() { return log(Logic.Type.OR); } public FilterBuilder not() { return log(Logic.Type.NOT); } void likeFilter(boolean not) { Expression match = (Expression) stack.pop(); Property prop = (Property) stack.pop(); stack.push(new Like(prop, match, not)); } public FilterBuilder like() { likeFilter(false); return this; } public FilterBuilder notLike() { likeFilter(true); return this; } public FilterBuilder equals() { return spatial(Spatial.Type.EQUALS); } public FilterBuilder intersects() { return spatial(Spatial.Type.INTERSECTS); } public FilterBuilder touches() { return spatial(Spatial.Type.TOUCHES); } public FilterBuilder disjoint() { return spatial(Spatial.Type.DISJOINT); } public FilterBuilder overlaps() { return spatial(Spatial.Type.OVERLAPS); } public FilterBuilder crosses() { return spatial(Spatial.Type.CROSSES); } public FilterBuilder covers() { return spatial(Spatial.Type.COVERS); } public FilterBuilder within() { return spatial(Spatial.Type.WITHIN); } public FilterBuilder contains() { return spatial(Spatial.Type.CONTAINS); } public FilterBuilder bbox() { return spatial(Spatial.Type.BBOX); } public FilterBuilder dwithin() { return spatial(Spatial.Type.DWITHIN); } public FilterBuilder beyond() { return spatial(Spatial.Type.BEYOND); } public Object pop() { return stack.pop(); } public Filter filter() { return (Filter) stack.pop(); } public FilterBuilder type(Class<?> type) { if (stack.peek() instanceof Expression) { stack.push(new TypeOf<Object>((Expression)stack.pop(), type)); } else { stack.push(new TypeOf<Object>(new Self(), type)); } return this; } public FilterBuilder cmp(Comparison.Type type) { Expression e2 = (Expression) stack.pop(); Expression e1 = (Expression) stack.pop(); stack.push(new Comparison<Object>(type, e1, e2)); return this; } void inFilter(boolean not) { List<Expression> values = new ArrayList<Expression>(stack.size() - 1); Iterator i = stack.descendingIterator(); i.next(); while (i.hasNext()) { values.add( (Expression) i.next()); i.remove(); } Property prop = (Property) stack.pop(); stack.push(new In(prop, values, not)); } public FilterBuilder in() { inFilter(false); return this; } public FilterBuilder notIn() { inFilter(true); return this; } FilterBuilder log(Logic.Type type) { LinkedList<Filter<Object>> parts = new LinkedList<Filter<Object>>(); while(stack.peek() instanceof Filter) { parts.addFirst((Filter<Object>)stack.pop()); if (type == Logic.Type.NOT) { break; } } stack.push(new Logic<Object>(type, parts)); return this; } FilterBuilder spatial(Spatial.Type type) { Expression top = (Expression) stack.peek(); Expression d = null; if ( top.evaluate(null) instanceof Number) { d = top; stack.pop(); } Expression e2 = (Expression) stack.pop(); Expression e1 = (Expression) stack.pop(); stack.push(new Spatial<Object>(type, e1, e2, d)); return this; } void math(char type) { Expression e2 = (Expression) stack.pop(); Expression e1 = (Expression) stack.pop(); stack.push(new Math(type, e1, e2)); } public FilterBuilder add() { math(Math.ADD); return this; } public FilterBuilder subtract() { math(Math.SUBTRACT); return this; } public FilterBuilder multiply() { math(Math.MULTIPLY); return this; } public FilterBuilder divide() { math(Math.DIVIDE); return this; } public FilterBuilder isNull() { stack.push(new Null((Property) stack.pop(), false)); return this; } public FilterBuilder isNotNull() { stack.push(new Null((Property) stack.pop(), true)); return this; } }