/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
* (C) 2009, Geomatys
*
* 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.geotoolkit.filter.binarycomparison;
import java.util.Calendar;
import org.apache.sis.util.ObjectConverters;
import org.geotoolkit.util.StringUtilities;
import org.opengis.filter.MatchAction;
import org.opengis.filter.expression.Expression;
import org.apache.sis.util.UnconvertibleObjectException;
import org.apache.sis.util.logging.Logging;
/**
* Abstract "property equal" filter, used by isEqual and isNotEqual subclass to avoid
* duplicaton the same test in both classes.
*
* @author Johann Sorel (Geomatys)
* @module
*/
public abstract class AbstractPropertyEqual extends AbstractBinaryComparisonOperator<Expression,Expression> {
private static final double EPS = 1E-12;
public AbstractPropertyEqual(final Expression left, final Expression right, final boolean match, final MatchAction matchAction) {
super(left,right,match,matchAction);
}
/**
* {@inheritDoc }
*/
@Override
public boolean evaluateOne(Object value1, Object value2) {
if (value1 == value2) {
// Includes the (value1 == null && value2 == null) case.
return true;
}
if (value1 == null || value2 == null) {
// No need to check for (value2 != null) or (value1 != null).
// If they were null, the previous check would have caugh them.
return false;
}
//quick resolving to avoid using converters-----------------------------
if(value1.getClass() == value2.getClass()){
//same class, return the equal value directly.
if(!match && value1 instanceof String ){
//special case if we are in case insensitive
return ((String)value1).equalsIgnoreCase((String)value2);
}else{
return value1.equals(value2);
}
}else if(value1 instanceof Number && value2 instanceof Number){
//test number case
return numberEqual((Number)value1, (Number)value2);
}else if(value1.equals(value2)){
//test standard equal
//but classes are not the same, so will have to use the converters
//to ensure a proper compare
return true;
}
//we rely on converters to ensure proper compare oparations
try {
Object converted1 = ObjectConverters.convert(value1, value2.getClass());
if(converted1 != null){
if(equalOrNumberEqual(value2, converted1)){
return true;
}
}
} catch (UnconvertibleObjectException | UnsupportedOperationException e) {
// TODO: temporary fix, catch UnsupportedOperationException in the case of
// a converter found which does not override the inverse() method
// To fix in apache-sis
Logging.recoverableException(null, AbstractPropertyEqual.class, "evaluate", e);
// TODO - do we really want to ignore?
}
try {
Object converted2 = ObjectConverters.convert(value2, value1.getClass());
if (value1 instanceof java.sql.Date && converted2 != null) {
Calendar cal1 = Calendar.getInstance();
cal1.setTime((java.sql.Date)value1);
int expYear = cal1.get(Calendar.YEAR);
int expMonth = cal1.get(Calendar.MONTH);
int expDay = cal1.get(Calendar.DAY_OF_MONTH);
cal1.setTime((java.sql.Date)converted2);
return cal1.get(Calendar.YEAR) == expYear &&
cal1.get(Calendar.MONTH) == expMonth &&
cal1.get(Calendar.DAY_OF_MONTH) == expDay;
}
if(converted2 != null){
if(equalOrNumberEqual(value1, converted2)){
return true;
}
}
} catch (UnconvertibleObjectException e) {
Logging.recoverableException(null, AbstractPropertyEqual.class, "evaluate", e);
// TODO - do we really want to ignore?
}
//no comparison matches
return false;
}
private boolean equalOrNumberEqual(final Object value1, final Object value2){
//test general equal case
if(!match && value1 instanceof String && value2 instanceof String){
if( ((String)value1).equalsIgnoreCase((String)value2)){
return true;
}
}
if(value1.equals(value2)){
return true;
}
//test number case
if(value1 instanceof Number && value2 instanceof Number){
return numberEqual((Number)value1, (Number)value2);
}
return false;
}
private static boolean numberEqual(final Number value1, final Number value2){
final Number n1 = (Number) value1;
final Number n2 = (Number) value2;
if( (n1 instanceof Float) || (n1 instanceof Double)
|| (n2 instanceof Float) || (n2 instanceof Double)){
final double d1 = n1.doubleValue();
final double d2 = n2.doubleValue();
if (Double.doubleToLongBits(d1) == Double.doubleToLongBits(d2)) {
return true;
}
if (Math.abs(d1 - d2) < EPS * Math.max(Math.abs(d1), Math.abs(d2))) {
return true;
}
} else {
return n1.longValue() == n2.longValue();
}
return false;
}
/**
* {@inheritDoc }
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("PropertyEqual (matchcase=");
sb.append(match).append(")\n");
sb.append(StringUtilities.toStringTree(left,right));
return sb.toString();
}
}