/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2005-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.brewer.color;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.Filters;
import org.geotools.filter.IllegalFilterException;
import org.geotools.filter.function.Classifier;
import org.geotools.filter.function.ExplicitClassifier;
import org.geotools.filter.function.RangedClassifier;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.Fill;
import org.geotools.styling.Graphic;
import org.geotools.styling.Mark;
import org.geotools.styling.Rule;
import org.geotools.styling.Stroke;
import org.geotools.styling.StyleBuilder;
import org.geotools.styling.StyleFactory;
import org.geotools.styling.Symbolizer;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.filter.And;
import org.opengis.filter.BinaryComparisonOperator;
import org.opengis.filter.BinaryLogicOperator;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.Or;
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.expression.Expression;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.PropertyName;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
/**
* Generates a style/featureTypeStyle using ColorBrewer.
* <br>
* WARNING: this is unstable and subject to radical change.
*
* @author Cory Horner, Refractions Research Inc.
* @source $URL$
*/
public class StyleGenerator {
private static final java.util.logging.Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.brewer.color");
public final static int ELSEMODE_IGNORE = 0;
public final static int ELSEMODE_INCLUDEASMIN = 1;
public final static int ELSEMODE_INCLUDEASMAX = 2;
private static FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
private static StyleFactory sf = CommonFactoryFinder.getStyleFactory(null);
private static StyleBuilder sb = new StyleBuilder(sf, ff);
protected StyleGenerator() {
}
/**
* Obtains the colour for the indexed rule. If an else rule is also to be
* created from the colour palette, the appropriate offset is applied.
*
* @param index
*/
private static Color getColor(int elseMode, Color[] colors, int index) {
if (elseMode == ELSEMODE_IGNORE) {
return colors[index];
} else if (elseMode == ELSEMODE_INCLUDEASMIN) {
return colors[index + 1];
} else if (elseMode == ELSEMODE_INCLUDEASMAX) {
return colors[index];
} else {
return null;
}
}
private static Color getElseColor(int elseMode, Color[] colors) {
if (elseMode == ELSEMODE_INCLUDEASMIN) {
return colors[0];
} else if (elseMode == ELSEMODE_INCLUDEASMAX) {
return colors[colors.length - 1];
} else {
return null;
}
}
/**
* Merges a classifier, array of colors and other data into a
* FeatureTypeStyle object. Yes, this constructor is insane and likely to
* change very soon.
*
* @param classifier
* @param colors
* @param typeId
* semantic type identifier, which will be prefixed with
* "colorbrewer:"
* @param geometryAttrType
* @param elseMode
* @param opacity
* @param defaultStroke
* @return
* @throws IllegalFilterException
*/
public static FeatureTypeStyle createFeatureTypeStyle(Classifier classifier,
Expression expression, Color[] colors, String typeId,
GeometryDescriptor geometryAttrType, int elseMode, double opacity, Stroke defaultStroke)
throws IllegalFilterException {
//init nulls
if (defaultStroke == null) {
defaultStroke = sb.createStroke();
}
//answer goes here
FeatureTypeStyle fts = sf.createFeatureTypeStyle();
// update the number of classes
int numClasses = classifier.getSize();
// if (elseMode == ELSEMODE_IGNORE) {
// numClasses++;
// }
//numeric
if (classifier instanceof RangedClassifier) {
RangedClassifier ranged = (RangedClassifier) classifier;
Object localMin = null;
Object localMax = null;
// for each class
for (int i = 0; i < ranged.getSize(); i++) {
// obtain min/max values
localMin = ranged.getMin(i);
localMax = ranged.getMax(i);
Rule rule = createRuleRanged(ranged, expression, localMin, localMax,
geometryAttrType, i, elseMode, colors, opacity, defaultStroke);
fts.addRule(rule);
}
} else if (classifier instanceof ExplicitClassifier) {
ExplicitClassifier explicit = (ExplicitClassifier) classifier;
// for each class
for (int i = 0; i < explicit.getSize(); i++) {
Set value = (Set) explicit.getValues(i);
Rule rule = createRuleExplicit(explicit, expression, value, geometryAttrType, i,
elseMode, colors, opacity, defaultStroke);
fts.addRule(rule);
}
} else {
LOGGER.log(Level.SEVERE, "Error: no handler for this Classifier type");
}
// add an else rule to capture any missing features?
if (elseMode != ELSEMODE_IGNORE) {
Symbolizer symb = createSymbolizer(geometryAttrType, getElseColor(elseMode, colors),
opacity, defaultStroke);
Rule elseRule = sb.createRule(symb);
elseRule.setIsElseFilter(true);
elseRule.setTitle("Else");
elseRule.setName("else");
fts.addRule(elseRule);
}
// sort the FeatureTypeStyle rules
Rule[] rule = fts.getRules();
if (elseMode == ELSEMODE_INCLUDEASMIN) {
//move last rule to the front
for (int i = rule.length - 1; i > 0; i--) {
Rule tempRule = rule[i];
rule[i] = rule[i - 1];
rule[i - 1] = tempRule;
}
}
//our syntax will be: ColorBrewer:id
fts.setSemanticTypeIdentifiers(new String[] { "generic:geometry", "colorbrewer:" + typeId });
return fts;
}
/**
* Creates a symbolizer for the given geometry
*
* @param sb
* @param geometryAttrType
* @param color
* @param opacity
* @param defaultStroke stroke used for borders
*
*/
private static Symbolizer createSymbolizer(GeometryDescriptor geometryAttrType, Color color,
double opacity, Stroke defaultStroke) {
Symbolizer symb;
if (defaultStroke == null) {
defaultStroke = sb.createStroke(color, 1, opacity);
}
if ((geometryAttrType.getType().getBinding() == MultiPolygon.class)
|| (geometryAttrType.getType().getBinding() == Polygon.class)) {
Fill fill = sb.createFill(color, opacity);
symb = sb.createPolygonSymbolizer(defaultStroke, fill);
} else if (geometryAttrType.getType().getBinding() == LineString.class) {
symb = sb.createLineSymbolizer(color);
} else if ((geometryAttrType.getType().getBinding() == MultiPoint.class)
|| (geometryAttrType.getType().getBinding() == Point.class)) {
Fill fill = sb.createFill(color, opacity);
Mark square = sb.createMark(StyleBuilder.MARK_SQUARE, fill, defaultStroke);
Graphic graphic = sb.createGraphic(null, square, null); //, 1, 4, 0);
symb = sb.createPointSymbolizer(graphic);
//TODO: handle Text and Raster
} else {
//we don't know what the heck you are, *snip snip* you're a line.
symb = sb.createLineSymbolizer(color);
}
return symb;
}
/**
* Truncates an unneeded trailing decimal zero (1.0 --> 1) by converting to
* an Integer object.
*
* @param value
*
* @return Integer(value) if applicable
*/
private static Object chopInteger(Object value) {
if ((value instanceof Number) && (value.toString().endsWith(".0"))) {
return new Integer(((Number) value).intValue());
} else {
return value;
}
}
/**
* Generates a quick name for each rule with a leading zero.
*
* @param count
*
*/
private static String getRuleName(int count) {
String strVal = new Integer(count).toString();
if (strVal.length() == 1) {
return "rule0" + strVal;
} else {
return "rule" + strVal;
}
}
private static Rule createRuleRanged(RangedClassifier classifier, Expression expression,
Object localMin, Object localMax, GeometryDescriptor geometryAttrType, int i,
int elseMode, Color[] colors, double opacity, Stroke defaultStroke)
throws IllegalFilterException {
// 1.0 --> 1
// (this makes our styleExpressions more readable. Note that the
// filter always converts to double, so it doesn't care what we
// do).
localMin = chopInteger(localMin);
localMax = chopInteger(localMax);
// generate a title
String title = classifier.getTitle(i);
// construct filters
Filter filter = null;
if (localMin == localMax) {
// build filter: =
filter = ff.equals(expression, ff.literal(localMax));
} else {
// build filter: [min <= x] AND [x < max]
Filter lowBoundFilter = null;
Filter hiBoundFilter = null;
if(localMin != null) {
lowBoundFilter = ff.greaterOrEqual(expression, ff.literal(localMin));
}
if(localMax != null) {
// if this is the global maximum, include the max value
if (i == (classifier.getSize() - 1)) {
hiBoundFilter = ff.lessOrEqual(expression, ff.literal(localMax));
} else {
hiBoundFilter = ff.less(expression, ff.literal(localMax));
}
}
if ((localMin != null) && (localMax != null)) {
filter = ff.and(lowBoundFilter, hiBoundFilter);
} else if ((localMin == null) && (localMax != null)) {
filter = hiBoundFilter;
} else if ((localMin != null) && (localMax == null)) {
filter = lowBoundFilter;
}
}
// create a symbolizer
Symbolizer symb = createSymbolizer(geometryAttrType, getColor(elseMode, colors, i),
opacity, defaultStroke);
// create a rule
Rule rule = sb.createRule(symb);
rule.setFilter(filter);
rule.setTitle(title);
rule.setName(getRuleName(i + 1));
return rule;
}
private static Rule createRuleExplicit(ExplicitClassifier explicit, Expression expression,
Set value, GeometryDescriptor geometryAttrType, int i, int elseMode, Color[] colors,
double opacity, Stroke defaultStroke) {
// create a sub filter for each unique value, and merge them
// into the logic filter
Object[] items = value.toArray();
Arrays.sort(items);
String title = "";
List<Filter> filters = new ArrayList<Filter>();
for (int item = 0; item < items.length; item++) {
Filter filter;
if (items[item] == null) {
filter = ff.isNull(expression);
} else {
filter = ff.equals(expression, ff.literal(items[item]));
}
// add to the title
if (items[item] == null) {
title += "NULL";
} else {
title += items[item].toString();
}
if ((item + 1) != items.length) {
title += ", ";
}
filters.add(filter);
}
// create the symbolizer
Symbolizer symb = createSymbolizer(geometryAttrType, getColor(elseMode, colors, i),
opacity, defaultStroke);
// create the rule
Rule rule = sb.createRule(symb);
if (filters.size() == 1){
rule.setFilter(filters.get(0));
}else if (filters.size() > 1){
rule.setFilter(ff.or(filters));
}
rule.setTitle(title);
rule.setName(getRuleName(i + 1));
return rule;
}
public static void modifyFTS(FeatureTypeStyle fts, int ruleIndex, String styleExpression)
throws IllegalFilterException {
Rule[] rule = fts.getRules();
Rule thisRule = rule[ruleIndex];
Filter filter = thisRule.getFilter();
if (filter instanceof And) { //ranged expression
//figure out the appropriate values
String[] newValue = styleExpression.split("\\.\\."); //$NON-NLS-1$
if (newValue.length != 2) {
throw new IllegalArgumentException(
"StyleExpression has incorrect syntax; min..max expected.");
}
List<Filter> children = ((BinaryLogicOperator) filter).getChildren();
if (children.size() > 2) {
throw new IllegalArgumentException(
"This method currently only supports logical filters with exactly 2 children.");
}
// we're expecting 2 compare subfilters
PropertyIsGreaterThanOrEqualTo filter1 = (PropertyIsGreaterThanOrEqualTo) children.get(0);
BinaryComparisonOperator filter2 = (BinaryComparisonOperator) children.get(1);
//filter1 should be 1 <= x and filter2 should be x <(=) 5
if (!(filter1.getExpression2().equals(filter2.getExpression1()))) {
throw new IllegalArgumentException(
"Subfilters or subExpressions in incorrect order");
}
if (filter1.getExpression1().toString() != newValue[0]) {
//lower bound value has changed, update
filter1 = ff.greaterOrEqual(filter1.getExpression1(), ff.literal(newValue[0]));
}
if (filter2.getExpression2().toString() != newValue[1]) {
//upper bound value has changed, update
if(filter2 instanceof PropertyIsLessThan) {
filter2 = ff.less(filter1.getExpression1(), ff.literal(newValue[1]));
} else if(filter2 instanceof PropertyIsLessThanOrEqualTo) {
filter2 = ff.lessOrEqual(filter1.getExpression1(), ff.literal(newValue[1]));
} else {
throw new IllegalArgumentException("Filter 2 in the comparison is not less or less or equal??");
}
}
thisRule.setFilter(filter); // style events don't handle filters yet, so fire the change event for filter
//TODO: adjust the previous and next filters (uses isFirst, isLast)
} else if (filter instanceof Or || filter instanceof PropertyIsEqualTo) {
// explicit expression obtain the expression containing the attribute
Expression attrExpression;
if (filter instanceof Or) {
attrExpression = ((BinaryComparisonOperator) ((Or) filter).getChildren().get(0)).getExpression1();
} else { //COMPARE_EQUALS (simple explicit expression)
attrExpression = ((PropertyIsEqualTo) filter).getExpression1();
}
//recreate the filter with the new values
rule[ruleIndex].setFilter(toExplicitFilter(styleExpression, attrExpression));
//TODO: remove duplicate values from other filters
} else {
throw new IllegalArgumentException("Unrecognized filter type.");
}
}
public static String toStyleExpression(Filter filter) {
short filterType = Filters.getFilterType(filter);
if (filter instanceof And) { //looks like a ranged filter
return toRangedStyleExpression((And) filter);
} else { //it's probably a filter with explicitly defined values
return toExplicitStyleExpression(filter);
}
}
public static String[] toStyleExpression(Filter[] filter) {
String[] styleExpression = new String[filter.length];
for (int i = 0; i < filter.length; i++) {
styleExpression[i] = toStyleExpression(filter[i]);
}
return styleExpression;
}
/**
* <p>
* Converts an array of styleExpressions and attributes into Filters
* </p>
* <p>
* <code>styleExpression[0] = "1..5";</code><br>
* <code>styleExpression[1] = "5..10";</code><br>
* <code>styleExpression[2] = "11, -13";</code><br>
* <code>---></code><br>
* <code>filter[0] = [[1 <= attr] AND [attr < 5]]</code><br>
* <code>filter[1] = [[6 <= attr] AND [attr <= 10]]</code><br>
* <code>filter[2] = [[attr = 11] OR [attr = -13]]</code>
* </p>
*
* @param styleExpression
* strings of ranged expressions "lowValue..highValue" or
* explicit values "value1, value2"
* @param featureType
* @param attributeTypeName
* @return an array with all the filters
* @throws IllegalFilterException
*/
public static Filter[] toFilter(String[] styleExpression, SimpleFeatureType[] featureType,
String[] attributeTypeName) throws IllegalFilterException {
Filter[] filter = new Filter[styleExpression.length];
// prepare the styleExpressions (fix out if they are ranged, and if so
// their min and max values too
boolean[] isRangedExpr = new boolean[styleExpression.length];
List<String> min = new ArrayList<String>();
String[] max = new String[styleExpression.length];
for (int i = 0; i < styleExpression.length; i++) {
if (isRanged(styleExpression[i])) {
isRangedExpr[i] = true;
String[] exprPart = styleExpression[i].split("\\.\\."); //$NON-NLS-1$
min.add(exprPart[0]);
max[i] = exprPart[1];
} else {
isRangedExpr[i] = false;
}
}
// create each filter
for (int i = 0; i < styleExpression.length; i++) {
// is it ranged or specific?
if (isRangedExpr[i]) {
boolean upperBoundClosed = true;
// check for lower bounds of the same value as the current upper
// bound
if (min.contains(max[i])) {
upperBoundClosed = false;
}
filter[i] = toRangedFilter(styleExpression[i], featureType[i],
attributeTypeName[i], upperBoundClosed);
} else { // specific
filter[i] = toExplicitFilter(styleExpression[i], featureType[i],
attributeTypeName[i]);
}
}
return filter;
}
/**
* <p>
* Creates a filter for a range of values.
* </p>
* <p>
* Examples:<br>
* "1..5", closed=true --> [[1 <= attr] AND [attr <= 5]]<br>
* "1..10", closed=false --> [[1 <= attr] AND [attr < 10]]
* "..10, closed=true --> [attr <= 10]
* </p>
*
* @param styleExpression
* the ranged style expression (minValue..maxValue)
* @param featureType
* the featureType
* @param attributeTypeName
* the attributeTypeName whose values correspond to
* @param upperBoundClosed
* does the upper bound include the max value? (true: <=, false: <)
* @return a filter
* @throws IllegalFilterException
*/
public static Filter toRangedFilter(String styleExpression, SimpleFeatureType featureType,
String attributeTypeName, boolean upperBoundClosed)
throws IllegalFilterException {
PropertyName attrib = ff.property(attributeTypeName);
String[] strs = styleExpression.split("\\.\\."); //$NON-NLS-1$
if (strs.length != 2) {
throw new IllegalArgumentException(
"A ranged filter could not be created from the styleExpression given.");
}
Literal localMin = ff.literal(strs[0]);
Literal localMax = ff.literal(strs[1]);
Filter lowerBound = ff.lessOrEqual(localMin, localMax);
Filter upperBound;
if (upperBoundClosed) {
upperBound = ff.lessOrEqual(attrib, localMax);
} else {
upperBound = ff.less(attrib, localMax);
}
return ff.and(lowerBound, upperBound);
}
/**
* <p>Converts a filter into a styleExpression with ranged values.</p>
* <p>Example:<br>
* <code>[[1 <= attr] AND [attr < 5]] --> "1..5"</code></p>
*
* @param filter A LOGIC_AND filter containing 2 CompareFilters or a single CompareFilter.
* @return a styleExpression of the syntax "min..max"
*/
private static String toRangedStyleExpression(Filter filter) {
if (filter instanceof BinaryLogicOperator) {
BinaryLogicOperator lFilter = (BinaryLogicOperator) filter;
if (!(filter instanceof And)) {
throw new IllegalArgumentException(
"Only logic filters constructed using the LOGIC_AND filterType are currently supported by this method.");
}
List<Filter> children = lFilter.getChildren();
// we're expecting 2 subfilters
Filter filter1 = children.get(0);
Filter filter2 = children.get(1);
if (children.size() > 2) {
throw new IllegalArgumentException(
"This method currently only supports logical filters with exactly 2 children.");
}
if (!(filter1 instanceof BinaryComparisonOperator) || !(filter2 instanceof BinaryComparisonOperator)) {
throw new IllegalArgumentException(
"Only compare filters as logical filter children are currently supported by this method.");
}
// find min and max values
Expression min1;
Expression min2;
Expression max1;
Expression max2;
if (filter1 instanceof PropertyIsLessThanOrEqualTo || filter1 instanceof PropertyIsLessThan) {
min1 = ((BinaryComparisonOperator) filter1).getExpression1();
max1 = ((BinaryComparisonOperator) filter1).getExpression2();
} else if (filter1 instanceof PropertyIsGreaterThanOrEqualTo ||
filter1 instanceof PropertyIsGreaterThan) {
min1 = ((BinaryComparisonOperator) filter1).getExpression2();
max1 = ((BinaryComparisonOperator) filter1).getExpression1();
} else {
throw new IllegalArgumentException("Unsupported FilterType");
}
if (filter2 instanceof PropertyIsLessThanOrEqualTo || filter2 instanceof PropertyIsLessThan) {
min2 = ((BinaryComparisonOperator) filter2).getExpression1();
max2 = ((BinaryComparisonOperator) filter2).getExpression2();
} else if (filter2 instanceof PropertyIsGreaterThanOrEqualTo ||
filter2 instanceof PropertyIsGreaterThan) {
min2 = ((BinaryComparisonOperator) filter2).getExpression2();
max2 = ((BinaryComparisonOperator) filter2).getExpression1();
} else {
throw new IllegalArgumentException("Unsupported FilterType");
}
//look for 2 equal expressions
if (max1.equals(min2)) {
return min1.toString() + ".." + max2.toString();
} else if (max2.equals(min1)) {
return min2.toString() + ".." + max1.toString();
} else {
throw new IllegalArgumentException(
"Couldn't find the expected arrangement of Expressions");
}
} else if (filter instanceof BinaryComparisonOperator) {
// what the heck??
}
throw new UnsupportedOperationException("Don't know how to handle this filter");
}
/**
* Determines if a string is an instance of a ranged expression or unique values.
*/
public static boolean isRanged(String styleExpression) {
return styleExpression.matches(".+\\.{2}.+");
}
/**
* <p>
* Creates a filter with each value explicitly defined.
* </p>
* <p>
* Examples:<br>
* "LIB" --> [PARTY = LIB]<br>
* "LIB, NDP" --> [[PARTY = LIB] OR [PARTY = NDP]]
* </p>
*
* @param styleExpression
* the list of attribute values, separated by commas (and
* optional spaces)
* @param attributeTypeName
* A Sting with the attributeTypeName whose values correspond to
* @return a filter
* @throws IllegalFilterException
*/
public static Filter toExplicitFilter(String styleExpression, SimpleFeatureType featureType,
String attributeTypeName) throws IllegalFilterException {
// eliminate spaces after commas
String expr = styleExpression.replaceAll(",\\s+", ","); //$NON-NLS-1$//$NON-NLS-2$
String[] attribValue = expr.split(","); //$NON-NLS-1$
// create the first filter
PropertyName attribExpr = ff.property(attributeTypeName);
PropertyIsEqualTo cFilter = ff.equals(attribExpr, ff.literal(attribValue[0]));
if (attribValue.length == 1) {
return cFilter;
}
// more than one value exists, so wrap them inside a logical OR
List<Filter> filters = new ArrayList<Filter>();
filters.add(cFilter);
for (int i = 1; i < attribValue.length; i++) {
cFilter = ff.equals(attribExpr, ff.literal(attribValue[i]));
filters.add(cFilter);
}
return ff.or(filters);
}
/**
* <p>
* Creates a filter with each value explicitly defined.
* </p>
* <p>
* Examples:<br>
* "LIB" --> [PARTY = LIB]<br>
* "LIB, NDP" --> [[PARTY = LIB] OR [PARTY = NDP]]
* </p>
*
* @param styleExpression
* the list of attribute values, separated by commas (and
* optional spaces)
* @param attribExpr
* an Expression to compare each value with (simple case = attributeExpression)
* @return a filter
* @throws IllegalFilterException
*/
public static Filter toExplicitFilter(String styleExpression, Expression attribExpr)
throws IllegalFilterException {
// eliminate spaces after commas
String expr = styleExpression.replaceAll(",\\s+", ","); //$NON-NLS-1$//$NON-NLS-2$
String[] attribValue = expr.split(","); //$NON-NLS-1$
// create the first filter
PropertyIsEqualTo cFilter = ff.equals(attribExpr, ff.literal(attribValue[0]));
if (attribValue.length == 1) {
return cFilter;
}
// more than one value exists, so wrap them inside a logical OR
List<Filter> filters = new ArrayList<Filter>();
filters.add(cFilter);
for (int i = 1; i < attribValue.length; i++) {
cFilter = ff.equals(attribExpr, ff.literal(attribValue[i]));
filters.add(cFilter);
}
return ff.or(filters);
}
/**
* <p>
* Converts a filter into a styleExpression with explicitly defined values.
* </p>
* <p>
* Example:<br>
* <code>[[attr = 49] OR [attr = 92]] --> "49, 92"</code>
* </p>
*
* @param filter
*/
private static String toExplicitStyleExpression(Filter filter) {
String styleExpression = "";
if (filter instanceof PropertyIsEqualTo) {
// figure out which side is the attributeExpression, and which side
// is the LiteralExpression
PropertyIsEqualTo compareFilter = (PropertyIsEqualTo) filter;
Expression leftExpression = compareFilter.getExpression1();
Expression rightExpression = compareFilter.getExpression2();
if ((leftExpression instanceof PropertyName)
&& (rightExpression instanceof Literal)) {
styleExpression = rightExpression.toString();
} else if ((leftExpression instanceof Literal)
&& (rightExpression instanceof PropertyName)) {
styleExpression = leftExpression.toString();
} else {
throw new IllegalArgumentException(
"Could not extract an Explicit Style Expression from the CompareFilter");
}
} else if (filter instanceof Or) {
// descend into the child elements of this filter
Or parentFilter = (Or) filter;
Iterator iterator = parentFilter.getChildren().iterator();
while (iterator.hasNext()) {
// recursive call
styleExpression += toExplicitStyleExpression((Filter) iterator.next());
if (iterator.hasNext()) {
styleExpression += ", ";
}
}
}
return styleExpression;
}
}