/*
* 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.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;
import org.geotools.factory.CommonFactoryFinder;
import org.opengis.filter.And;
import org.opengis.filter.FilterVisitor;
import org.opengis.filter.Or;
/**
* Defines a logic filter (the only filter type that contains other filters).
* This filter holds one or more filters together and relates them logically
* with an internally defined type (AND, OR, NOT).
*
* @author Rob Hranac, TOPP
* @source $URL$
* @version $Id$
*/
public abstract class LogicFilterImpl extends BinaryLogicAbstract implements LogicFilter {
/** The logger for the default core module. */
private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.core");
protected LogicFilterImpl(org.opengis.filter.FilterFactory factory) {
this(factory,new ArrayList());
}
protected LogicFilterImpl(org.opengis.filter.FilterFactory factory, List children) {
super(factory,children);
}
/**
* Constructor with type (must be valid).
*
* @param filterType The final relation between all sub filters.
*
* @throws IllegalFilterException If the filtertype is not a logic type.
* @deprecated Consructing with type constants should be replaced with
* an actual java type.
*/
protected LogicFilterImpl(short filterType) throws IllegalFilterException {
super(CommonFactoryFinder.getFilterFactory(null), new ArrayList());
LOGGER.finest("filtertype " + filterType);
if (isLogicFilter(filterType)) {
this.filterType = filterType;
} else {
throw new IllegalFilterException(
"Attempted to create logic filter with non-logic type.");
}
}
/**
* Convenience constructor to create a NOT logic filter.
*
* @param filter The initial sub filter.
* @param filterType The final relation between all sub filters.
*
* @throws IllegalFilterException Does not conform to logic filter
* structure
*/
protected LogicFilterImpl(Filter filter, short filterType)
throws IllegalFilterException {
super(CommonFactoryFinder.getFilterFactory(null), new ArrayList());
if (isLogicFilter(filterType)) {
this.filterType = filterType;
} else {
throw new IllegalFilterException(
"Attempted to create logic filter with non-logic type.");
}
children.add(filter);
}
/**
* Convenience constructor to create an AND/OR logic filter.
*
* @param filter1 An initial sub filter.
* @param filter2 An initial sub filter.
* @param filterType The final relation between all sub filters.
*
* @throws IllegalFilterException Does not conform to logic filter
* structure
*/
protected LogicFilterImpl(Filter filter1, Filter filter2, short filterType)
throws IllegalFilterException {
super(CommonFactoryFinder.getFilterFactory(null), new ArrayList());
if (isLogicFilter(filterType)) {
this.filterType = filterType;
} else {
throw new IllegalFilterException(
"Attempted to create logic filter with non-logic type.");
}
// Push the initial filter on the stack
children.add(filter1);
// Add the second filter via internal method to check for illegal NOT
this.addFilter(filter2);
}
/**
* Adds a sub filter to this filter.
*
* @param filter Specified filter to add to the sub filter list.
*
* @throws IllegalFilterException Does not conform to logic filter
* structure
*
* @task REVISIT: make all filters immutable. This should return a new
* filter.
*/
public final void addFilter(org.opengis.filter.Filter filter) throws IllegalFilterException {
if ((filterType != LOGIC_NOT) || (children.size() == 0)) {
children.add(filter);
} else {
throw new IllegalFilterException(
"Attempted to add an more than one filter to a NOT filter.");
}
}
/**
* Gets an iterator for the filters held by this logic filter.
*
* @return the iterator of the filters.
*/
public Iterator getFilterIterator() {
return children.iterator();
}
/**
* Implements a logical OR with this filter and returns the merged filter.
*
* @param filter Parent of the filter: must implement GMLHandlerGeometry.
*
* @return ORed filter.
*
* @task REVISIT: make immutable, should not modify the subfilters of the
* filter being ored.
*/
public Filter or(org.opengis.filter.Filter filter) {
// Just makes sure that we are not creating unnecessary new filters
// by popping onto stack if current filter is OR
//HACK: not sure what should be returned by this method
//HACK: assuming it is the result of each method
//REVISIT: should return a new copy, must implement cloneable to do so.
if (filterType == super.LOGIC_OR) {
if( filter instanceof Or ){
Or more = (Or) filter;
children.addAll( more.getChildren() );
}
else {
children.add(filter);
}
return this;
} else {
return super.or(filter);
}
}
/**
* Implements a logical AND with this filter and returns the merged filter.
*
* @param filter Parent of the filter: must implement GMLHandlerGeometry.
*
* @return ANDed filter.
*
* @task REVISIT: make immutable, should not modify the subfilters of the
* filter being anded.
*/
public Filter and(org.opengis.filter.Filter filter) {
// Just makes sure that we are not creating unnecessary new filters
// by popping onto stack if current filter is AND
//HACK: not sure what should be returned by this method
//HACK: assuming it is the result of each method
if (filterType == super.LOGIC_AND) {
if( filter instanceof And ){
And more = (And) filter;
children.addAll( more.getChildren() );
}
else {
children.add(filter);
}
return this;
} else {
return super.and(filter);
}
}
/**
* Implements a logical NOT with this filter and returns the merged filter.
*
* @return NOTed filter.
*/
public Filter not() {
// Just makes sure that we are not creating unnecessary new filters
// by popping off sub filter if current filter is NOT
//HACK: not sure what should be returned by this method
//HACK: assuming it is the result of each method
if (filterType == super.LOGIC_NOT) {
return (Filter) children.get(0);
} else {
return super.not();
}
}
/**
* package private method to get the internal storage of filters.
*
* @return the internal sub filter list.
*
* @deprecated use {@link #getChildren()}
*/
List getSubFilters() {
return children;
}
/**
* Returns a string representation of this filter.
*
* @return String representation of the logic filter.
*/
public String toString() {
String returnString = "[";
String operator = "";
Iterator iterator = children.iterator();
if (filterType == LOGIC_OR) {
operator = " OR ";
} else if (filterType == LOGIC_AND) {
operator = " AND ";
} else if (filterType == LOGIC_NOT) {
return "[ NOT " + iterator.next().toString() + " ]";
}
while (iterator.hasNext()) {
returnString = returnString + iterator.next().toString();
if (iterator.hasNext()) {
returnString = returnString + operator;
}
}
return returnString + "]";
}
/**
* 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, and then checks that the subFilters lists
* are the same size and that one list contains the other. This means
* that logic filters with different internal orders of subfilters are
* equal.
*
* @param obj - the object to compare this LogicFilter against.
*
* @return true if specified object is equal to this filter; false
* otherwise.
*/
public boolean equals(Object obj) {
if (obj == this )
return true;
if ((obj != null) && (obj.getClass() == this.getClass())) {
LogicFilterImpl logFilter = (LogicFilterImpl) obj;
LOGGER.finest("filter type match:"
+ (logFilter.getFilterType() == this.filterType));
LOGGER.finest("same size:"
+ (logFilter.getSubFilters().size() == this.children.size())
+ "; inner size: " + logFilter.getSubFilters().size()
+ "; outer size: " + this.children.size());
LOGGER.finest("contains:"
+ logFilter.getSubFilters().containsAll(this.children));
return ((logFilter.getFilterType() == this.filterType)
&& (logFilter.getSubFilters().size() == this.children.size())
&& logFilter.getSubFilters().containsAll(this.children));
} 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) + children.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);
}