/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * This library 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; * version 2.1 of the License. * * This library 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. */ package org.geotools.filter; // Geotools dependencies import java.util.logging.Logger; import org.geotools.factory.CommonFactoryFinder; import org.opengis.feature.simple.SimpleFeature; import org.opengis.filter.FilterVisitor; /** * Defines a comparison filter (can be a math comparison or generic equals). * This filter implements a comparison - of some sort - between two * expressions. The comparison may be a math comparison or a generic equals * comparison. If it is a math comparison, only math expressions are allowed; * if it is an equals comparison, any expression types are allowed. Note that * this comparison does not attempt to restrict its expressions to be * meaningful. This means that it considers itself a valid filter as long as * the expression comparison returns a valid result. It does no checking to * see whether or not the expression comparison is meaningful with regard to * checking feature attributes. In other words, this is a valid filter: * <b>52 = 92</b>, even though it will always return the same result and could * be simplified away. It is up the the filter creator, therefore, to attempt * to simplify/make meaningful filter logic. * * @author Rob Hranac, Vision for New York * * @source $URL$ * @version $Id$ */ public abstract class CompareFilterImpl extends BinaryComparisonAbstract implements CompareFilter { /** The logger for the default core module. */ static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.core"); /** * Constructor with filter type. * * @param filterType The type of comparison. * * @throws IllegalFilterException Non-compare type. * @deprecated use {@link #CompareFilterImpl(org.opengis.filter.FilterFactory, org.opengis.filter.expression.Expression, org.opengis.filter.expression.Expression)} */ protected CompareFilterImpl(short filterType) throws IllegalFilterException { super(CommonFactoryFinder.getFilterFactory(null)); if (isCompareFilter(filterType)) { this.filterType = filterType; } else { throw new IllegalFilterException( "Attempted to create compare filter with non-compare type."); } } protected CompareFilterImpl(org.opengis.filter.FilterFactory factory, org.opengis.filter.expression.Expression e1, org.opengis.filter.expression.Expression e2) { this(factory,e1,e2,true); } protected CompareFilterImpl(org.opengis.filter.FilterFactory factory, org.opengis.filter.expression.Expression e1, org.opengis.filter.expression.Expression e2, boolean matchCase ) { super(factory,e1,e2,matchCase); } /** * Adds the 'left' value to this filter. * * @param leftValue Expression for 'left' value. * * @throws IllegalFilterException Filter is not internally consistent. * * @task REVISIT: immutability? */ public final void addLeftValue(Expression leftValue) throws IllegalFilterException { setExpression1(leftValue); } public void setExpression1(org.opengis.filter.expression.Expression leftValue) { //Checks if this is math filter or not and handles appropriately if (isMathFilter(filterType)) { if (DefaultExpression.isMathExpression(leftValue) || permissiveConstruction) { this.expression1 = leftValue; } else { throw new IllegalFilterException( "Attempted to add non-math expression to math filter." ); } } else { this.expression1 = leftValue; } } /** * Adds the 'right' value to this filter. * * @param rightValue Expression for 'right' value. * * @throws IllegalFilterException Filter is not internally consistent. * * @task REVISIT: make immutable. */ public final void addRightValue(Expression rightValue) throws IllegalFilterException { setExpression2(rightValue); } public void setExpression2(org.opengis.filter.expression.Expression rightValue) { // Checks if this is math filter or not and handles appropriately if (isMathFilter(filterType)) { if (DefaultExpression.isMathExpression(rightValue) || permissiveConstruction) { this.expression2 = rightValue; } else { throw new IllegalFilterException( "Attempted to add non-math expression to math filter." ); } } else { this.expression2 = rightValue; } } /** * Gets the left expression. * * @return The expression on the left of the comparison. * * * @deprecated use {@link #getExpression1()} */ public final Expression getLeftValue() { return (Expression)getExpression1(); } /** * Gets the right expression. * * @return The expression on the right of the comparison. * * @deprecated use {@link #getExpression2()} */ public final Expression getRightValue() { return (Expression)getExpression2(); } /** * Determines whether or not a given feature is 'inside' this filter. * * @param feature Specified feature to examine. * * @return Flag confirming whether or not this feature is inside the * filter. */ public boolean evaluate(SimpleFeature feature){ return evaluate((Object)feature); } /** * Subclass convenience method which compares to instances of comparables * in a pretty lax way, converting types among String, Number, Double when * appropriate. * * @return same contract as {@link Comparable#compareTo(java.lang.Object)}. */ protected int compare (Comparable leftObj, Comparable rightObj) { //implements a lax compare, doing some back flips for numbers if (!(leftObj instanceof Number && rightObj instanceof Number)) { //check for case of one number one string if (!(leftObj.getClass() == rightObj.getClass())) { //differnt classes, if numbers lets try and match them up if ( leftObj instanceof Number && (rightObj.getClass() == String.class) ) { try{ rightObj = new Double( Double.parseDouble( (String) rightObj )); leftObj = new Double( ((Number) leftObj).doubleValue() ); } catch(Exception e) { leftObj = leftObj.toString(); rightObj = rightObj.toString(); } } else if ( (leftObj.getClass() == String.class) && rightObj instanceof Number ) { try{ leftObj = new Double( Double.parseDouble( (String) leftObj ) ); rightObj = new Double( ((Number) rightObj).doubleValue() ); } catch(Exception e) { leftObj = leftObj.toString(); rightObj = rightObj.toString(); } } else { leftObj = leftObj.toString(); rightObj = rightObj.toString(); } } return leftObj.compareTo(rightObj); } else { //both numbers, make double double left = ((Number) leftObj).doubleValue(); double right = ((Number) rightObj).doubleValue(); return left > right ? 1 : (left == right ? 0 : -1); } } /** * Returns a string representation of this filter. * * @return String representation of the compare filter. */ public String toString() { if (filterType == NULL) { return "[ " + expression1 + " IS NULL ]"; } String operator = null; if (filterType == COMPARE_EQUALS) { operator = " = "; } if (filterType == COMPARE_LESS_THAN) { operator = " < "; } if (filterType == COMPARE_GREATER_THAN) { operator = " > "; } if (filterType == COMPARE_LESS_THAN_EQUAL) { operator = " <= "; } if (filterType == COMPARE_GREATER_THAN_EQUAL) { operator = " >= "; } if (filterType == COMPARE_NOT_EQUALS) { operator = " != "; } return "[ " + expression1 + operator + expression2 + " ]"; } /** * Compares this filter to the specified object. Returns true if the * passed in object is the same as this filter. Checks to make sure the * filter types are the same as well as both of the values. * * @param obj - the object to compare this CompareFilter against. * * @return true if specified object is equal to this filter; false * otherwise. */ public boolean equals(Object obj) { if (obj instanceof CompareFilter) { CompareFilter cFilter = (CompareFilter) obj; // todo - check for nulls here, or make immutable. // return filterType == cFilter.getFilterType() && ( expression1 == cFilter.getLeftValue() || (expression1 != null && expression1.equals( cFilter.getLeftValue() ) ) ) && ( expression2 == cFilter.getRightValue() || (expression2 != null && expression2.equals( cFilter.getRightValue() ) ) ); } else { return false; } } /** * Override of hashCode method. * * @return a code to hash this object by. */ public int hashCode() { int result = 17; result = (37 * result) + filterType; result = (37 * result) + ((expression1 == null) ? 0 : expression1.hashCode()); result = (37 * result) + ((expression2 == null) ? 0 : expression2.hashCode()); return result; } /** * Used by FilterVisitors to perform some action on this filter instance. * Typicaly used by Filter decoders, but may also be used by any thing * which needs infomration from filter structure. Implementations should * always call: visitor.visit(this); It is importatant that this is not * left to a parent class unless the parents API is identical. * * @param visitor The visitor which requires access to this filter, the * method must call visitor.visit(this); */ public abstract Object accept(FilterVisitor visitor, Object extraData); }