/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2011, 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.data.sfs; import java.io.StringWriter; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.opengis.filter.And; import org.opengis.filter.BinaryComparisonOperator; import org.opengis.filter.ExcludeFilter; import org.opengis.filter.Filter; import org.opengis.filter.Id; import org.opengis.filter.IncludeFilter; 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.Literal; import org.opengis.filter.expression.PropertyName; 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; import org.opengis.filter.temporal.After; import org.opengis.filter.temporal.AnyInteracts; import org.opengis.filter.temporal.Before; import org.opengis.filter.temporal.Begins; import org.opengis.filter.temporal.BegunBy; import org.opengis.filter.temporal.BinaryTemporalOperator; import org.opengis.filter.temporal.During; import org.opengis.filter.temporal.EndedBy; import org.opengis.filter.temporal.Ends; import org.opengis.filter.temporal.Meets; import org.opengis.filter.temporal.MetBy; import org.opengis.filter.temporal.OverlappedBy; import org.opengis.filter.temporal.TContains; import org.opengis.filter.temporal.TEquals; import org.opengis.filter.temporal.TOverlaps; import org.opengis.filter.FilterVisitor; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; import java.net.URLEncoder; import org.geotools.geojson.geom.GeometryJSON; /** * * This class is pulled from GeoRest DataStore implementation and modified * @author */ class SFSFilterVisitor implements FilterVisitor { protected static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.data.simplefeatureservice"); private Map<String, String> properties = new HashMap<String, String>(); private List<String> queryable = new ArrayList<String>(); private boolean sortProperties; private boolean hasProperties; private static final String strEncoding="UTF-8"; public SFSFilterVisitor() { } public SFSFilterVisitor(boolean sortProperties) { this.sortProperties = sortProperties; } public boolean isSortProperties() { return sortProperties; } public void setSortProperties(boolean sortProperties) { this.sortProperties = sortProperties; } /** * Finish up building the URL, and return it as a string. * * @param builder * StringBuilder that contains the base URL from which to start adding URL * properties. * @param hasProperties * Does the given URL already contains other properties? * @return Returns the URL with the newly added properties for filtering. */ public String finish(StringBuilder builder, boolean hasProperties) throws UnsupportedEncodingException { this.hasProperties = hasProperties; List<String> props = new ArrayList<String>(properties.keySet()); if (sortProperties) { Collections.sort(queryable); Collections.sort(props); } // Add all properties to the URL: for (String property : props) { builder.append(getGlueChar()); builder.append(URLEncoder.encode(property, strEncoding)); builder.append("="); builder.append(URLEncoder.encode(properties.get(property), strEncoding)); } // Add queryable properties to the URL if needed: if (queryable != null && queryable.size() > 0) { builder.append(getGlueChar()); builder.append("queryable="); for (int i = 0; i < queryable.size(); i++) { builder.append(URLEncoder.encode(queryable.get(i), strEncoding)); if (i < queryable.size() - 1) { builder.append(","); } } } // Cleanup: properties.clear(); queryable.clear(); return builder.toString(); } public Object visitNullFilter(Object extraData) { return extraData; } public Object visit(ExcludeFilter filter, Object extraData) { throw new UnsupportedOperationException("visit (ExcludeFilter filter, Object extraData)"); } public Object visit(IncludeFilter filter, Object extraData) { return extraData; } public Object visit(And filter, Object extraData) { Iterator<?> iter = filter.getChildren().iterator(); while (iter.hasNext()) { Filter element = (Filter) iter.next(); element.accept(this, extraData); } return extraData; } public Object visit(Id filter, Object extraData) { throw new UnsupportedOperationException("visit (Not filter, Object extraData)"); } public Object visit(Not filter, Object extraData) { throw new UnsupportedOperationException("visit (Not filter, Object extraData)"); } public Object visit(Or filter, Object extraData) { throw new UnsupportedOperationException("visit (Or filter, Object extraData)"); } public Object visit(PropertyIsBetween filter, Object extraData) { throw new UnsupportedOperationException( "visit (PropertyIsBetween filter, Object extraData)"); } public Object visit(PropertyIsEqualTo filter, Object extraData) { String propertyName = ((PropertyName) filter.getExpression1()).getPropertyName(); checkPropertyFilter(propertyName, "__eq", filter); return extraData; } public Object visit(PropertyIsNotEqualTo filter, Object extraData) { String propertyName = ((PropertyName) filter.getExpression1()).getPropertyName(); checkPropertyFilter(propertyName, "__ne", filter); return extraData; } public Object visit(PropertyIsGreaterThan filter, Object extraData) { String propertyName = ((PropertyName) filter.getExpression1()).getPropertyName(); checkPropertyFilter(propertyName, "__gt", filter); return extraData; } public Object visit(PropertyIsGreaterThanOrEqualTo filter, Object extraData) { String propertyName = ((PropertyName) filter.getExpression1()).getPropertyName(); checkPropertyFilter(propertyName, "__gte", filter); return extraData; } public Object visit(PropertyIsLessThan filter, Object extraData) { String propertyName = ((PropertyName) filter.getExpression1()).getPropertyName(); checkPropertyFilter(propertyName, "__lt", filter); return extraData; } public Object visit(PropertyIsLessThanOrEqualTo filter, Object extraData) { String propertyName = ((PropertyName) filter.getExpression1()).getPropertyName(); checkPropertyFilter(propertyName, "__lte", filter); return extraData; } public Object visit(PropertyIsLike filter, Object extraData) { String propertyName = ((PropertyName) filter.getExpression()).getPropertyName(); String operator = filter.isMatchingCase() ? "__like" : "__ilike"; if (!properties.containsKey(propertyName + operator)) { String value = filter.getLiteral(); if (value != null) { properties.put(propertyName + operator, value); if (!queryable.contains(propertyName)) { queryable.add(propertyName); } } } return extraData; } public Object visit(PropertyIsNull filter, Object extraData) { throw new UnsupportedOperationException("visit (PropertyIsNull filter, Object extraData)"); } public Object visit(BBOX filter, Object extraData) { Polygon polygon = (Polygon) filter.getExpression2().evaluate(extraData); Envelope box = polygon.getEnvelopeInternal(); if (!properties.containsKey("bbox")) { properties.put("bbox", box.getMinX() + "," + box.getMinY() + "," + box.getMaxX() + "," + box.getMaxY()); } else { throw new IllegalArgumentException("Filter cannot contain more than one bounding box."); } if (!properties.containsKey("epsg") && polygon.getSRID() != 0) { properties.put("epsg", polygon.getSRID() + ""); } return extraData; } public Object visit(Beyond filter, Object extraData) { throw new UnsupportedOperationException("visit (Beyond filter, Object extraData)"); } public Object visit(Contains filter, Object extraData) { throw new UnsupportedOperationException("visit (Contains filter, Object extraData)"); } public Object visit(Crosses filter, Object extraData) { throw new UnsupportedOperationException("visit (Crosses filter, Object extraData)"); } public Object visit(Disjoint filter, Object extraData) { throw new UnsupportedOperationException("visit (Disjoint filter, Object extraData)"); } public Object visit(DWithin filter, Object extraData) { // check we actually support this thing boolean valid = true; if(!(filter.getExpression1() instanceof PropertyName)) { valid = false; } if(!(filter.getExpression2() instanceof Literal)) { valid = false; } if(!valid) { throw new UnsupportedOperationException("DWithin filter on this store is supported only " + "if the first operand is the default geometry property and the second " + "is a geometry literal"); } Geometry geometry = (Geometry) filter.getExpression2().evaluate(extraData); if(geometry instanceof Point) { Point point = (Point) geometry; if (!properties.containsKey("lon")) { properties.put("lon", String.valueOf(point.getX())); } else { throw new IllegalArgumentException("Long. is already set"); } if (!properties.containsKey("lat")) { properties.put("lat", String.valueOf(point.getY())); } else { throw new IllegalArgumentException("Lat is already set"); } } else { writeGeometry(geometry); } if (!properties.containsKey("tolerance")) { properties.put("tolerance", String.valueOf(filter.getDistance())); } else { throw new IllegalArgumentException("tolerance is already set"); } if (!properties.containsKey("epsg") ) { properties.put("epsg", filter.getDistanceUnits() + ""); } return extraData; } public Object visit(Equals filter, Object extraData) { throw new UnsupportedOperationException("visit (Equals filter, Object extraData)"); } public Object visit(Intersects filter, Object extraData) { Geometry goem = (Geometry) filter.getExpression2().evaluate(null); writeGeometry(goem); return extraData; } private void writeGeometry(Geometry geom) { if (!properties.containsKey("geometry")) { String strGeoJSON = ""; GeometryJSON gjson = new GeometryJSON(); try { StringWriter sw = new StringWriter(); gjson.write(geom, sw); strGeoJSON = sw.toString(); } catch (IOException ex) { LOGGER.log(Level.SEVERE, " Exception at visit : Intersect Filter " + ex.getMessage(), ex); } properties.put("geometry", strGeoJSON); } else { throw new IllegalArgumentException("Geometry is already sey"); } } public Object visit(Overlaps filter, Object extraData) { throw new UnsupportedOperationException("visit (Overlaps filter, Object extraData)"); } public Object visit(Touches filter, Object extraData) { throw new UnsupportedOperationException("visit (Touches filter, Object extraData)"); } public Object visit(Within filter, Object extraData) { throw new UnsupportedOperationException("visit (Within filter, Object extraData)"); } public Object visit(After after, Object extraData) { throw new UnsupportedOperationException("visit (After filter, Object extraData)"); } public Object visit(AnyInteracts anyInteracts, Object extraData) { throw new UnsupportedOperationException("visit (AnyInteracts filter, Object extraData)"); } public Object visit(Before before, Object extraData) { throw new UnsupportedOperationException("visit (Before filter, Object extraData)"); } public Object visit(Begins begins, Object extraData) { throw new UnsupportedOperationException("visit (Begins filter, Object extraData)"); } public Object visit(BegunBy begunBy, Object extraData) { throw new UnsupportedOperationException("visit (BegunBy filter, Object extraData)"); } public Object visit(During during, Object extraData) { throw new UnsupportedOperationException("visit (During filter, Object extraData)"); } public Object visit(EndedBy endedBy, Object extraData) { throw new UnsupportedOperationException("visit (EndedBy filter, Object extraData)"); } public Object visit(Ends ends, Object extraData) { throw new UnsupportedOperationException("visit (Ends filter, Object extraData)"); } public Object visit(Meets meets, Object extraData) { throw new UnsupportedOperationException("visit (Touches filter, Object extraData)"); } public Object visit(MetBy metBy, Object extraData) { throw new UnsupportedOperationException("visit (Touches filter, Object extraData)"); } public Object visit(OverlappedBy overlappedBy, Object extraData) { throw new UnsupportedOperationException("visit (Touches filter, Object extraData)"); } public Object visit(TContains contains, Object extraData) { throw new UnsupportedOperationException("visit (Touches filter, Object extraData)"); } public Object visit(TEquals equals, Object extraData) { throw new UnsupportedOperationException("visit (Touches filter, Object extraData)"); } public Object visit(TOverlaps contains, Object extraData) { throw new UnsupportedOperationException("visit (Touches filter, Object extraData)"); } // Private methods: private String getGlueChar() { if (!hasProperties) { hasProperties = true; return "?"; } return "&"; } private void checkPropertyFilter(String propertyName, String extension, BinaryComparisonOperator filter) { String combined = propertyName + extension; if (!properties.containsKey(combined)) { Object value = ((Literal) filter.getExpression2()).getValue(); if (value != null) { properties.put(combined, value.toString()); if (!queryable.contains(propertyName)) { queryable.add(propertyName); } } } } }