/*
* 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 java.awt.Dialog;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.geotools.util.MapEntry;
import org.opengis.filter.And;
import org.opengis.filter.Not;
import org.opengis.filter.Or;
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.PropertyIsLike;
import org.opengis.filter.PropertyIsNotEqualTo;
import org.opengis.filter.PropertyIsNull;
import org.opengis.filter.expression.Add;
import org.opengis.filter.expression.Divide;
import org.opengis.filter.expression.Function;
import org.opengis.filter.expression.Multiply;
import org.opengis.filter.expression.Subtract;
import org.opengis.filter.spatial.BBOX;
import org.opengis.filter.spatial.Beyond;
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;
/**
* Represents the Filter capabilities that are supported by a SQLEncoder
* <p>
* Each SQLEncoder class should have one static FilterCapabilities, representing
* the filter encoding operations that it can successfully perform.
* <p>
* This class is used as one big mask to detect filters that cannot be performed
*
* @author Chris Holmes, TOPP
* @source $URL$
*
* @deprecated use {@link org.opengis.filter.capability.FilterCapabilities}.
*/
public class FilterCapabilities {
/**
* Mask for no operation
*/
public static final long NO_OP = 0;
/**
* Mask for Filter.INCLUDE
*/
public static final long NONE = 0x01<<30;
/**
* Mask for Filter.EXCLUDE
*/
public static final long ALL = 0x01<<31;
// spatial masks
/**
* Spatial Mask for bbox operation
*/
public static final long SPATIAL_BBOX = 0x01;
/**
* Spatial Mask for equals operation
*/
public static final long SPATIAL_EQUALS = 0x01<<1;
/**
* Spatial Mask for disjoint operation
*/
public static final long SPATIAL_DISJOINT = 0x01<<2;
/**
* Spatial Mask for intersect operation
*/
public static final long SPATIAL_INTERSECT = 0x01<<3;
/**
* Spatial Mask for touches operation
*/
public static final long SPATIAL_TOUCHES = 0x01<<4;
/**
* Spatial Mask for crosses operation
*/
public static final long SPATIAL_CROSSES = 0x01<<5;
/**
* Spatial Mask for within operation
*/
public static final long SPATIAL_WITHIN = 0x01<<6;
/**
* Spatial Mask for contains operation
*/
public static final long SPATIAL_CONTAINS = 0x01<<7;
/**
* Spatial Mask for overlaps operation
*/
public static final long SPATIAL_OVERLAPS = 0x01<<8;
/**
* Spatial Mask for beyond operation
*/
public static final long SPATIAL_BEYOND = 0x01<<9;
/**
* Spatial Mask for dwithin operation
*/
public static final long SPATIAL_DWITHIN = 0x01<<10;
//scalar masks
/**
* Scalar Mask for like operation
*/
public static final long LIKE = 0x01<<11;
/**
* Scalar Mask for between opelongion
*/
public static final long BETWEEN = 0x01<<12;
/**
* Scalar Mask for null check operation
*/
public static final long NULL_CHECK = 0x01<<13;
/**
* Scalar Mask for simple arithmetic operations
*/
public static final long SIMPLE_ARITHMETIC = 0x01<<14;
/**
* Scalar Mask for function operations
*/
public static final long FUNCTIONS = 0x01<<15;
// masks for different comparison filters
public static final long COMPARE_EQUALS = 0x01<<16;
public static final long COMPARE_GREATER_THAN = 0x01<<17;
public static final long COMPARE_GREATER_THAN_EQUAL = 0x01<<18;
public static final long COMPARE_LESS_THAN = 0x01<<19;
public static final long COMPARE_LESS_THAN_EQUAL = 0x01<<20;
public static final long COMPARE_NOT_EQUALS = 0x01<<21;
public static final long FID = 0x01<<22;
// masks for different logic filters
public static final long LOGIC_AND = 0x01<<23;
public static final long LOGIC_NOT = 0x01<<24;
public static final long LOGIC_OR = 0x01<<25;
/**
* Scalar Mask for logical operation
*/
public static final long LOGICAL = (LOGIC_AND|LOGIC_OR|LOGIC_NOT);
/**
* Scalar Mask for simple comparison operations
*/
public static final long SIMPLE_COMPARISONS = COMPARE_EQUALS|COMPARE_GREATER_THAN|COMPARE_GREATER_THAN_EQUAL|COMPARE_LESS_THAN|COMPARE_LESS_THAN_EQUAL|COMPARE_NOT_EQUALS;
public static final FilterCapabilities SIMPLE_COMPARISONS_OPENGIS;
public static final FilterCapabilities LOGICAL_OPENGIS;
static final Map/*<int,Class>*/ intTypeToOpenGisTypeMap = new HashMap();
static {
SIMPLE_COMPARISONS_OPENGIS = new FilterCapabilities();
SIMPLE_COMPARISONS_OPENGIS.addType(PropertyIsEqualTo.class);
SIMPLE_COMPARISONS_OPENGIS.addType(PropertyIsGreaterThan.class);
SIMPLE_COMPARISONS_OPENGIS.addType(PropertyIsGreaterThanOrEqualTo.class);
SIMPLE_COMPARISONS_OPENGIS.addType(PropertyIsLessThanOrEqualTo.class);
SIMPLE_COMPARISONS_OPENGIS.addType(PropertyIsLessThan.class);
SIMPLE_COMPARISONS_OPENGIS.addType(PropertyIsNotEqualTo.class);
LOGICAL_OPENGIS = new FilterCapabilities();
LOGICAL_OPENGIS.addType(And.class);
LOGICAL_OPENGIS.addType(Not.class);
LOGICAL_OPENGIS.addType(Or.class);
intTypeToOpenGisTypeMap.put(new Long(SPATIAL_BBOX), BBOX.class);
intTypeToOpenGisTypeMap.put(new Long(SPATIAL_EQUALS), Equals.class);
intTypeToOpenGisTypeMap.put(new Long(SPATIAL_DISJOINT), Disjoint.class);
intTypeToOpenGisTypeMap.put(new Long(SPATIAL_INTERSECT), Intersects.class);
intTypeToOpenGisTypeMap.put(new Long(SPATIAL_TOUCHES), Touches.class);
intTypeToOpenGisTypeMap.put(new Long(SPATIAL_CROSSES), Crosses.class);
intTypeToOpenGisTypeMap.put(new Long(SPATIAL_WITHIN), Within.class);
intTypeToOpenGisTypeMap.put(new Long(SPATIAL_CONTAINS), Contains.class);
intTypeToOpenGisTypeMap.put(new Long(SPATIAL_OVERLAPS), Overlaps.class);
intTypeToOpenGisTypeMap.put(new Long(SPATIAL_BEYOND), Beyond.class);
intTypeToOpenGisTypeMap.put(new Long(SPATIAL_DWITHIN), DWithin.class);
intTypeToOpenGisTypeMap.put(new Long(SIMPLE_ARITHMETIC), new Class[] {Add.class, Subtract.class, Multiply.class, Divide.class});
intTypeToOpenGisTypeMap.put(new Long(FUNCTIONS), Function.class);
intTypeToOpenGisTypeMap.put(new Long(COMPARE_EQUALS), PropertyIsEqualTo.class);
intTypeToOpenGisTypeMap.put(new Long(COMPARE_NOT_EQUALS), PropertyIsNotEqualTo.class);
intTypeToOpenGisTypeMap.put(new Long(COMPARE_GREATER_THAN), PropertyIsGreaterThan.class);
intTypeToOpenGisTypeMap.put(new Long(COMPARE_GREATER_THAN_EQUAL), PropertyIsGreaterThanOrEqualTo.class);
intTypeToOpenGisTypeMap.put(new Long(COMPARE_LESS_THAN), PropertyIsLessThan.class);
intTypeToOpenGisTypeMap.put(new Long(COMPARE_LESS_THAN_EQUAL), PropertyIsLessThanOrEqualTo.class);
intTypeToOpenGisTypeMap.put(new Long(NULL_CHECK), PropertyIsNull.class);
intTypeToOpenGisTypeMap.put(new Long(LIKE), PropertyIsLike.class);
intTypeToOpenGisTypeMap.put(new Long(BETWEEN), PropertyIsBetween.class);
intTypeToOpenGisTypeMap.put(new Long(LOGIC_AND), And.class);
intTypeToOpenGisTypeMap.put(new Long(LOGIC_OR), Or.class);
intTypeToOpenGisTypeMap.put(new Long(LOGIC_NOT), Not.class);
}
private long ops = NO_OP;
private Set functions=new HashSet();
public FilterCapabilities(long filterCapabilitiesType) {
addType(filterCapabilitiesType);
}
public FilterCapabilities() {
this(NO_OP);
}
public FilterCapabilities(Class type) {
addType(type);
}
/**
* Adds a new support type to capabilities.
*
* @param type The one of the masks enumerated in this class
*/
public void addType( long type ) {
// avoid infinite recursion with addType(class)
if((ops & type) != 0)
return;
ops = ops | type;
// the type can be a mask signifying multiple filter types, we have to add them all
for (Iterator it = intTypeToOpenGisTypeMap.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
long key = ((Long) entry.getKey()).longValue();
if((key & type) != 0) {
Object filter = entry.getValue();
if(filter != null) {
if(filter instanceof Class[]) {
Class[] filters = (Class[]) filter;
for (int i = 0; i < filters.length; i++) {
addType(filters[i], false);
}
} else {
addType((Class) filter, false);
}
}
}
}
}
/**
* Adds a new support type to capabilities. For 2.2 only function expression support is added this way.
* As of geotools 2.3 this will be the supported way of adding to Filtercapabilities.
*
* @param type the Class that indicates the new support.
*/
public void addType( Class type ){
if( org.opengis.filter.Filter.class.isAssignableFrom(type)
||
org.opengis.filter.expression.Expression.class.isAssignableFrom(type)){
addType(FUNCTIONS);
functions.add(type);
}
}
/**
* Adds a new support type to capabilities. For 2.2 only function expression support is added this way.
* As of geotools 2.3 this will be the supported way of adding to Filtercapabilities.
*
* @param type the Class that indicates the new support.
*/
public void addType(Class type, boolean addFunctionType ){
if( org.opengis.filter.Filter.class.isAssignableFrom(type)
||
org.opengis.filter.expression.Expression.class.isAssignableFrom(type)){
if(addFunctionType) addType(FUNCTIONS);
functions.add(type);
}
}
/**
* Add all the capabilities in the provided FilterCapabilities to this capabilities.
*
* @param capabilities capabilities to add.
*/
public void addAll( FilterCapabilities capabilities){
addType(capabilities.ops);
functions.addAll(capabilities.functions);
}
/**
* Adds a new support type to capabilities.
*
* @param type The {@link FilterType} type that is supported
* @deprecated
* @see #convertFilterTypeToMask(short)
* @see #addType(long)
*/
public void addType( short type ) {
addAll(convertFilterTypeToMask(type));
}
/**
* Returns the mask that is equivalent to the FilterType constant.
*
* @param type a constant from {@link FilterType}
* @return the mask that is equivalent to the FilterType constant.
*/
public FilterCapabilities convertFilterTypeToMask(short type) {
if( type==FilterType.ALL )
return FilterNameTypeMapping.NO_OP_CAPS;
if( type==FilterType.NONE )
return FilterNameTypeMapping.ALL_CAPS;
Object object = FilterNameTypeMapping.filterTypeToFilterCapabilitiesMap.get(new Short(type));
return (FilterCapabilities)object;
}
/**
* Determines if the filter passed in is supported.
*
* @param filter The Filter to be tested.
*
* @return true if supported, false otherwise.
*/
public boolean supports(org.opengis.filter.Filter filter) {
for (Iterator ifunc = functions.iterator(); ifunc.hasNext();) {
if (((Class)ifunc.next()).isAssignableFrom(filter.getClass()))
return true;
}
if (functions.contains(filter.getClass()))
return true;
short filterType = Filters.getFilterType( filter );
return supports(filterType);
}
/**
* Determines if the filter and all its sub filters are supported. Is most
* important for logic filters, as they are the only ones with subFilters.
* Null filters should not be used here, if nothing should be filtered
* than Filter.INCLUDE can be used. Embedded nulls can be a particular
* source of problems, buried in logic filters.
*
* @param filter the filter to be tested.
*
* @return true if all sub filters are supported, false otherwise.
*
* @throws IllegalArgumentException If a null filter is passed in. As this
* function is recursive a null in a logic filter will also cause
* an error.
*/
public boolean fullySupports(org.opengis.filter.Filter filter) {
boolean supports = true;
if (filter == null) {
throw new IllegalArgumentException("Null filters can not be "
+ "unpacked, did you mean " + "Filter.INCLUDE?");
}
short filterType = Filters.getFilterType(filter);
if (AbstractFilter.isLogicFilter(filterType)) {
Iterator filters = ((LogicFilter) filter).getFilterIterator();
org.opengis.filter.Filter testFilter = null;
//short testFtype = 0;
while (filters.hasNext()) {
testFilter = (org.opengis.filter.Filter) filters.next();
if (!(this.fullySupports(testFilter))) {
supports = false;
break;
}
}
} else {
supports = this.supports(filter);
}
return supports;
}
/**
* Determines if the filter type passed in is supported.
*
* @param type The AbstractFilter type to be tested
*
* @return true if supported, false otherwise.
* @deprecated
*/
public boolean supports( short type ) {
return supports(convertFilterTypeToMask(type));
}
public boolean supports(long type) {
return (ops & type) == type;
}
public boolean supports(FilterCapabilities type) {
return (ops & type.ops) == type.ops && functions.containsAll(type.functions);
}
public boolean supports(Class type){
return functions.contains(type);
}
public long getScalarOps() {
return ops & (SIMPLE_ARITHMETIC|SIMPLE_COMPARISONS|FID|FUNCTIONS|LIKE|LOGICAL|NULL_CHECK|BETWEEN);
}
public long getSpatialOps() {
return ops & (SPATIAL_BBOX|SPATIAL_BEYOND|SPATIAL_CONTAINS|SPATIAL_CROSSES
|SPATIAL_DISJOINT|SPATIAL_DWITHIN|SPATIAL_EQUALS|SPATIAL_INTERSECT
|SPATIAL_OVERLAPS|SPATIAL_TOUCHES|SPATIAL_WITHIN);
}
/**
* Translates a String into an object that represents the operation
*
* @param name String, operation name
*
* @return one of the {@link FilterCapabilities} constants
*/
public static FilterCapabilities findOperation(String name) {
return FilterNameTypeMapping.findOperation(name);
}
/**
* Translates a String into an object that represents function expression
*
* @param name String, expression name
*
* @return one of the {@link FilterCapabilities} constants
*/
public static FilterCapabilities findFunction(String name) {
return FilterNameTypeMapping.findFunction(name);
}
}