/* * 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; import static org.geotools.filter.Filters.getExpressionType; import org.geotools.util.Converters; import org.opengis.filter.expression.ExpressionVisitor; import org.opengis.filter.expression.Literal; import com.vividsolutions.jts.geom.Geometry; /** * Defines an expression that holds a literal for return. * * @author Rob Hranac, Vision for New York * * * @source $URL$ * @version $Id$ */ public class LiteralExpressionImpl extends DefaultExpression implements Literal { /** Holds a reference to the literal. */ private Object literal = null; /** * Constructor with literal. */ protected LiteralExpressionImpl() { } /** * Constructor with literal. * * @param literal The literal to store inside this expression. * * @throws IllegalFilterException This literal type is not in scope. */ public LiteralExpressionImpl(Object literal) throws IllegalFilterException { this.setValue(literal); } /** * Constructor with literal. This alternative constructor is a convinience * one for integers an Integer object will be constructed, and no * IllegalFilterException can ever be thrown. * * @param value The integer to store inside this expression. */ protected LiteralExpressionImpl(int value) { try { this.setValue(new Integer(value)); } catch (IllegalFilterException ile) { //this is imposible as this is only thrown for //invalid types, and Integer is a valid type throw new AssertionError( "LiteralExpressionImpl is broken, it should accept Integers"); } } protected LiteralExpressionImpl(long value) { try { this.setValue(new Long(value)); } catch (IllegalFilterException ile) { //this is imposible as this is only thrown for //invalid types, and Double is a valid type throw new AssertionError( "LiteralExpressionImpl is broken, it should accept Longs"); } } /** * Constructor with literal. This alternative constructor is a convinience * one for doubles an Double object will be constructed, and no * IllegalFilterException can ever be thrown. * * @param value The double to store inside this expression. */ protected LiteralExpressionImpl(double value) { try { this.setValue(new Double(value)); } catch (IllegalFilterException ile) { //this is imposible as this is only thrown for //invalid types, and Double is a valid type throw new AssertionError( "LiteralExpressionImpl is broken, it should accept Doubles"); } } /** * Constructor with literal. This alternative constructor is a convinience * one for doubles an Double object will be constructed, and no * IllegalFilterException can ever be thrown. * * @param value The double to store inside this expression. */ protected LiteralExpressionImpl(String value) { try { this.setValue(value); } catch (IllegalFilterException ile) { //this is imposible as this is only thrown for //invalid types, and String is a valid type throw new AssertionError( "LiteralExpressionImpl is broken, it should accept Strings"); } } /** * Retrieves the literal of this expression. * * @return the literal held by this expression. * */ public Object getValue() { return literal; } /** * Sets the literal. * * @param literal The literal to store inside this expression. * * @throws IllegalFilterException This literal type is not in scope. */ public final void setValue(Object literal) { this.literal = literal; } public Object evaluate(Object feature) { return literal; } public <T> T evaluate(Object feature, Class<T> context) { return Converters.convert(literal, context); } /** * Return this filter as a string. * * @return String representation of this geometry filter. */ public String toString() { return literal == null ? "NULL" : literal.toString(); } /** * Compares this filter to the specified object. Returns true if the * passed in object is the same as this expression. Checks to make sure * the expression types are the same as well as the literals. * * @param obj - the object to compare this ExpressionLiteral against. * * @return true if specified object is equal to this expression; false * otherwise. * * @task REVISIT: missmatched types now considered not equal. This may be a * problem when comparing Doubles and Integers */ public boolean equals(Object obj) { if (obj instanceof LiteralExpressionImpl) { LiteralExpressionImpl expLit = (LiteralExpressionImpl) obj; // This is a problem. The Expression with type String of "2.0" // should be equals to the Expression with type Integer of "2.0" // Same thing with doubles and integers (as noted in the javadocs) // null handling if (this.literal == null) { return expLit.literal == null; } else if(expLit.literal == null) { return false; } // direct comparison if same type if (getExpressionType(this) == getExpressionType(expLit)) { if(this.literal.equals(expLit.literal)) { return true; } } // do the conversion dance int expressionType = getExpressionType(this); if (expressionType == ExpressionType.LITERAL_GEOMETRY) { return ((Geometry) this.literal).equalsExact( expLit.evaluate(null, Geometry.class)); } else if (expressionType == ExpressionType.LITERAL_INTEGER) { return ((Integer) this.literal).equals( expLit.evaluate(null, Integer.class)); } else if (expressionType == ExpressionType.LITERAL_STRING) { return ((String) this.literal).equals(expLit.evaluate(null, String.class)); } else if (expressionType == ExpressionType.LITERAL_DOUBLE) { return ((Double) this.literal).equals(expLit.evaluate(null, Double.class)); } else if (expressionType == ExpressionType.LITERAL_LONG) { return ((Long) this.literal).equals(expLit.evaluate(null, Long.class)); } else { // try to convert the other to the current type Object other = expLit.evaluate(null, this.literal.getClass()); if (other != null) { return other.equals(this.literal); } // converters might be one way, try the opposite other = expLit.getValue(); Object converted = this.evaluate(null, other.getClass()); if(converted != null) { return converted.equals(other); } // final attemp with a string to string comparison String str1 = (String) this.evaluate(null, String.class); String str2 = (String) expLit.evaluate(null, String.class); return str1 != null && str2 != null && str1.equals(str2); } } else if (obj instanceof Literal) { // some other Literal implementation like ConstantExpression Literal other = (Literal) obj; return equals( new LiteralExpressionImpl( other.getValue() ) ); } else { return false; } } /** * Override of hashCode method. * * @return the hash code for this literal expression */ public int hashCode() { int result = 17; result = (37 * result) + ((literal == null) ? 0 : literal.hashCode()); result = (37 * result) + Filters.getExpressionType( this ); 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 Object accept(ExpressionVisitor visitor, Object extraData) { return visitor.visit(this,extraData); } }