/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2003-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.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.logging.Logger; import org.geotools.data.DataSourceException; import org.geotools.data.oracle.sdo.SDO; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.filter.ExcludeFilter; import org.opengis.filter.Id; import org.opengis.filter.IncludeFilter; import org.opengis.filter.PropertyIsBetween; import org.opengis.filter.PropertyIsLike; import org.opengis.filter.PropertyIsNull; 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 com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateSequence; import com.vividsolutions.jts.geom.CoordinateSequenceFactory; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.MultiLineString; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; /** * Encodes Geometry filters into valid oracle SDO statements. * * <p> * At this stage it only supports the GEOMETRY_BBOX types. * </p> * * <p> * Encoded filters get written to the protected Writer called <code>out</code> * </p> * * @author $Author: seangeo $ * @source $URL$ * @version $Id$ */ public class SQLEncoderOracle extends SQLEncoder { /** Logger - for logging */ private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger( "org.geotools.filter.SQLEncoderOracle"); /** Contains filter type to SDO_RELATE mask type mappings */ private static final Map SDO_RELATE_MASK_MAP = new HashMap(); /** The standard SQL multicharacter wild card. */ private static final String SQL_WILD_MULTI = "%"; /** The standard SQL single character wild card. */ private static final String SQL_WILD_SINGLE = "_"; /** Default tolerance for spatial queries. */ private static final String TOLERANCE = "0.001"; static { SDO_RELATE_MASK_MAP.put(new Short(AbstractFilter.GEOMETRY_CONTAINS), "contains"); SDO_RELATE_MASK_MAP.put(new Short(AbstractFilter.GEOMETRY_CROSSES), "overlapbydisjoint"); SDO_RELATE_MASK_MAP.put(new Short(AbstractFilter.GEOMETRY_EQUALS), "equal"); SDO_RELATE_MASK_MAP.put(new Short(AbstractFilter.GEOMETRY_OVERLAPS), "overlapbyintersect"); SDO_RELATE_MASK_MAP.put(new Short(AbstractFilter.GEOMETRY_TOUCHES), "touch"); SDO_RELATE_MASK_MAP.put(new Short(AbstractFilter.GEOMETRY_WITHIN), "inside"); SDO_RELATE_MASK_MAP.put(new Short(AbstractFilter.GEOMETRY_DISJOINT), "anyinteract"); //Ok, back to using these, as the not disjoint turned out to be a big //performance hit. I would really like to see some solid testing on //these though, as with a trivial case it really did not seem to work //right, not disjoint was giving different answers than anyinteract. SDO_RELATE_MASK_MAP.put(new Short(AbstractFilter.GEOMETRY_BBOX), "anyinteract"); SDO_RELATE_MASK_MAP.put(new Short(AbstractFilter.GEOMETRY_INTERSECTS), "anyinteract"); } /** The escaped version of the multiple wildcard for the REGEXP pattern. */ //private String escapedWildcardMulti = "\W.\\*"; // "\\*" /** The escaped version of the single wildcard for the REGEXP pattern. */ //private String escapedWildcardSingle = "\\.\\?"; /** * The Spatial Reference System IDs Keyed by ColumnName, value is Integer * SRID number */ private Map srids; private String fidColumn; private String currentGeomColumnName = null; boolean inGeomFilter = false; public SQLEncoderOracle(String fidColumn, int defaultSRID) { this(new HashMap()); this.fidColumn = fidColumn; srids.put(null, new Integer(defaultSRID)); setSqlNameEscape("\""); } public SQLEncoderOracle(int defaultSRID) { this(null, new HashMap()); srids.put(null, new Integer(defaultSRID)); } /** * Creates a new SQLEncoderOracle with a specified SRID. * * @param fidColumn DOCUMENT ME! * @param srids The Spatial Reference ID to use when generating SDO SQL * statements. */ public SQLEncoderOracle(String fidColumn, Map srids) { currentGeomColumnName = null; this.fidColumn = fidColumn; this.srids = srids; Set geomCols = srids.keySet(); if (geomCols.size() > 0) { currentGeomColumnName = (String) geomCols.iterator().next(); } LOGGER.fine("SQLEncoderOracle: Geometric Column is: " + currentGeomColumnName); setSqlNameEscape("\""); } /** * Creates a new SQLEncoderOracle with a specified SRID. * * @param srids The Spatial Reference ID to use when generating SDO SQL * statements. */ public SQLEncoderOracle(Map srids) { this(null, srids); } /** * Sets the capabilities of this filter. * * @return FilterCapabilities for this Filter */ protected FilterCapabilities createFilterCapabilities() { FilterCapabilities capabilities = new FilterCapabilities(); capabilities.addType(FilterCapabilities.LOGICAL); capabilities.addAll(FilterCapabilities.LOGICAL_OPENGIS); capabilities.addType(FilterCapabilities.SIMPLE_COMPARISONS); capabilities.addAll(FilterCapabilities.SIMPLE_COMPARISONS_OPENGIS); capabilities.addType(FilterCapabilities.NULL_CHECK); capabilities.addType(PropertyIsNull.class); capabilities.addType(FilterCapabilities.BETWEEN); capabilities.addType(PropertyIsBetween.class); capabilities.addType(FilterCapabilities.FID); capabilities.addType(Id.class); capabilities.addType(FilterCapabilities.NONE); capabilities.addType(IncludeFilter.class); capabilities.addType(FilterCapabilities.ALL); capabilities.addType(ExcludeFilter.class); capabilities.addType(FilterCapabilities.LIKE); capabilities.addType(PropertyIsLike.class); capabilities.addType(FilterCapabilities.SPATIAL_BBOX); capabilities.addType(BBOX.class); capabilities.addType(FilterCapabilities.SPATIAL_CONTAINS); capabilities.addType(Contains.class); capabilities.addType(FilterCapabilities.SPATIAL_CROSSES); capabilities.addType(Crosses.class); capabilities.addType(FilterCapabilities.SPATIAL_DISJOINT); capabilities.addType(Disjoint.class); capabilities.addType(FilterCapabilities.SPATIAL_EQUALS); capabilities.addType(Equals.class); capabilities.addType(FilterCapabilities.SPATIAL_INTERSECT); capabilities.addType(Intersects.class); capabilities.addType(FilterCapabilities.SPATIAL_OVERLAPS); capabilities.addType(Overlaps.class); capabilities.addType(FilterCapabilities.SPATIAL_TOUCHES); capabilities.addType(Touches.class); capabilities.addType(FilterCapabilities.SPATIAL_WITHIN); capabilities.addType(Within.class); capabilities.addType(FilterCapabilities.SPATIAL_DWITHIN); capabilities.addType(DWithin.class); capabilities.addType(FilterCapabilities.SPATIAL_BEYOND); capabilities.addType(Beyond.class); return capabilities; } /** * Reverting back to just using anyinteract, as Thijs says this is a bad * performance hit * This is a special case for bbox and intersects filters, as the former * using of 'anyinteract' does not seem to be exactly a not disjoint, * which is what is needed according to ogc specs. * * @param geomFilter DOCUMENT ME! * * @throws IOException DOCUMENT ME! */ /* private void doNotDisjointFilter(GeometryFilter geomFilter) throws IOException { //String mask = (String) SDO_RELATE_MASK_MAP.get(new Short( // geomFilter.getFilterType())); Expression left = geomFilter.getLeftGeometry(); Expression right = geomFilter.getRightGeometry(); if (((left != null) || (currentGeomColumnName != null)) && (right != null)) { inGeomFilter = true; out.write("NOT SDO_RELATE("); if (left != null) { left.accept(this); } else { out.write(currentGeomColumnName); } out.write(","); right.accept(this); out.write(",'mask=disjoint querytype=WINDOW') = 'TRUE' "); inGeomFilter = false; } else { LOGGER.warning("Invalid filter. Cannot have a Geometry filter " + "with only one expression."); } }*/ private void doSdoRelate(GeometryFilter geomFilter) throws IOException { String mask = (String) SDO_RELATE_MASK_MAP.get(new Short( geomFilter.getFilterType())); AttributeExpression attExpr; LiteralExpression geomExpr; Expression left = geomFilter.getLeftGeometry(); Expression right = geomFilter.getRightGeometry(); if (left instanceof AttributeExpression && right instanceof LiteralExpression) { attExpr = (AttributeExpression) left; geomExpr = (LiteralExpression) right; } else if (right instanceof AttributeExpression && left instanceof LiteralExpression) { attExpr = (AttributeExpression) right; geomExpr = (LiteralExpression) left; } else { String err = "Oracle currently supports one geometry and one " + "attribute expr. You gave: " + left + ", " + right; throw new DataSourceException(err); } if (((attExpr != null) || (currentGeomColumnName != null)) && (geomExpr != null) && (mask != null)) { inGeomFilter = true; out.write("SDO_RELATE("); if (attExpr != null) { attExpr.accept(this); } else { out.write("\"" + currentGeomColumnName + "\""); } out.write(","); geomExpr.accept(this); // for disjoint we ask for no interaction, anyinteract == false if(geomFilter.getFilterType() == AbstractFilter.GEOMETRY_DISJOINT) { out.write(",'mask=" + mask + " querytype=WINDOW') <> 'TRUE' "); } else { out.write(",'mask=" + mask + " querytype=WINDOW') = 'TRUE' "); } inGeomFilter = false; } else { LOGGER.warning("Invalid filter. Cannot have a Geometry filter " + "with only one expression."); } } /** * Performs a geometry distance filter, must be either a dwithin or a * a beyond filter. Uses the SDO_WITHIN_DISTANCE function, dwithin matches * for true, beyond for false. * * @param geomFilter the filter to use, must be a dwithin or beyond */ private void doSdoDistance(GeometryDistanceFilter geomFilter) throws IOException { // String mask = (String) SDO_RELATE_MASK_MAP.get(new Short( // geomFilter.getFilterType())); //TODO: don't assume left and right are correct order, see doSdoRelate Expression left = geomFilter.getLeftGeometry(); Expression right = geomFilter.getRightGeometry(); double distance = geomFilter.getDistance(); //only dwithin and beyond, dwithin matches true for sdo_within_distance boolean isDWithin = geomFilter.getFilterType() == AbstractFilter.GEOMETRY_DWITHIN; String boolValue = isDWithin ? "TRUE" : "FALSE"; if ((left != null) && (right != null)) { inGeomFilter = true; out.write("SDO_WITHIN_DISTANCE("); left.accept(this); out.write(","); right.accept(this); out.write(",'distance=" + distance + "') = '" + boolValue + "' "); inGeomFilter = false; } else { LOGGER.warning("Invalid filter for DWithin. Cannot have a Geometry filter " + "with only one expression."); } } /** * Converts JTS Geometry to a String version of a SDO Geometry. This * should move to a utility class, as we now have more than one class * using this (which is why it changed to public static) * * TODO: Multi eometries * * @param geometry The JTS Geometry to convert. * @param srid DOCUMENT ME! * * @return A String representation of the SDO Geometry. */ public static String toSDOGeom(Geometry geometry, int srid) { if (Point.class.isAssignableFrom(geometry.getClass())) { return toSDOGeom((Point) geometry, srid); } else if (LineString.class.isAssignableFrom(geometry.getClass())) { return toSDOGeom((LineString) geometry, srid); } else if (Polygon.class.isAssignableFrom(geometry.getClass())) { if(geometry.equals(geometry.getEnvelope())) { return toSDOGeom(geometry.getEnvelopeInternal(), srid); } else { return toSDOGeom((Polygon) geometry, srid); } } else if (MultiLineString.class.isAssignableFrom(geometry.getClass())) { return toSDOGeom((MultiLineString) geometry, srid); } else if (MultiPolygon.class.isAssignableFrom(geometry.getClass())) { return toSDOGeom((MultiPolygon) geometry, srid); } else { LOGGER.warning("Got a literal geometry that I can't handle: " + geometry.getClass().getName()); return ""; } } /** * TODO: Encode more then 1 * @param line * @param srid */ private static String toSDOGeom(MultiLineString line, int srid) { if( line.getNumGeometries() == 1 ){ return toSDOGeom( line.getGeometryN(0), srid); } throw new UnsupportedOperationException("Cannot encode MultiLineString (yet)"); } /** * TODO: Encode more then 1 * @param line * @param srid */ private static String toSDOGeom(MultiPolygon polygon, int srid) { if( polygon.getNumGeometries() == 1 ){ return toSDOGeom( polygon.getGeometryN(0), srid); } throw new UnsupportedOperationException("Cannot encode MultiPolygon (yet)"); } /** * Converts a LineString Geometry in an SDO SQL geometry construction * statement. * * <p> * 2D geometries is assumed. If higher dimensional geometries are used the * query will be encoded as a 2D geometry. * </p> * * @param line The line to encode. * @param srid DOCUMENT ME! * * @return An SDO SQL geometry object construction statement */ private static String toSDOGeom(LineString line, int srid) { if (SDO.D(line) > 2) { LOGGER.warning("" + SDO.D(line) + " dimensioned geometry provided." + " This encoder only supports 2D geometries. The query will be constructed as" + " a 2D query."); } StringBuffer buffer = new StringBuffer("MDSYS.SDO_GEOMETRY("); buffer.append(SDO.D(line)); buffer.append("002,"); if (srid > 0) { LOGGER.fine("Using layer SRID: " + srid); buffer.append(srid); } else { LOGGER.fine("Using NULL SRID: "); buffer.append("NULL"); } buffer.append(",NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),"); buffer.append("MDSYS.SDO_ORDINATE_ARRAY("); Coordinate[] coordinates = line.getCoordinates(); for (int i = 0; i < coordinates.length; i++) { buffer.append(coordinates[i].x); buffer.append(","); buffer.append(coordinates[i].y); if (i != (coordinates.length - 1)) { buffer.append(","); } } buffer.append("))"); return buffer.toString(); } /** * Converts a Point Geometry in an SDO SQL geometry construction statement. * * <p> * 2D geometries is assumed. If higher dimensional geometries are used the * query will be encoded as a 2D geometry. * </p> * * @param point The point to encode. * @param srid DOCUMENT ME! * * @return An SDO SQL geometry object construction statement */ private static String toSDOGeom(Point point, int srid) { if (SDO.D(point) > 2) { LOGGER.warning("" + SDO.D(point) + " dimensioned geometry provided." + " This encoder only supports 2D geometries. The query will be constructed as" + " a 2D query."); } StringBuffer buffer = new StringBuffer("MDSYS.SDO_GEOMETRY("); buffer.append(SDO.D(point)); buffer.append("001,"); if (srid > 0) { LOGGER.fine("Using layer SRID: " + srid); buffer.append(srid); } else { LOGGER.fine("Using NULL SRID: "); buffer.append("NULL"); } buffer.append(",MDSYS.SDO_POINT_TYPE("); buffer.append(point.getX()); buffer.append(","); buffer.append(point.getY()); buffer.append(",NULL),NULL,NULL)"); return buffer.toString(); } /** * Converts a Polygon Geometry in an SDO SQL geometry construction * statement. * * <p> * 2D geometries is assumed. If higher dimensional geometries are used the * query will be encoded as a 2D geometry. * </p> * * @param polygon The polygon to encode. * @param srid DOCUMENT ME! * * @return An SDO SQL geometry object construction statement */ private static String toSDOGeom(Polygon polygon, int srid) { StringBuffer buffer = new StringBuffer(); if (SDO.D(polygon) > 2) { LOGGER.warning("" + SDO.D(polygon) + " dimensioned geometry provided." + " This encoder only supports 2D geometries. The query will be constructed as" + " a 2D query."); } if (polygon.getExteriorRing() != null) { buffer.append("MDSYS.SDO_GEOMETRY("); buffer.append(SDO.D(polygon)); buffer.append("003,"); if (srid > 0) { LOGGER.fine("Using layer SRID: " + srid); buffer.append(srid); } else { LOGGER.fine("Using NULL SRID: "); buffer.append("NULL"); } buffer.append(",NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1),"); buffer.append("MDSYS.SDO_ORDINATE_ARRAY("); CoordinateSequenceFactory fact = polygon.getFactory().getCoordinateSequenceFactory(); CoordinateSequence exterior = polygon.getExteriorRing().getCoordinateSequence(); CoordinateSequence coordSeq = SDO.counterClockWise(fact, exterior); for (int i = 0, size = coordSeq.size(); i < size; i++) { Coordinate cur = coordSeq.getCoordinate(i); buffer.append(cur.x); buffer.append(","); buffer.append(cur.y); if (i != (size - 1)) { buffer.append(","); } } /* This could be expensive if coordSeq implementation is not an an array. Leaving in for now as I can't test, and this is more likely to work right. Coordinate[] coordinates = coordSeq.toCoordinateArray(); for (int i = 0; i < coordinates.length; i++) { buffer.append(coordinates[i].x); buffer.append(","); buffer.append(coordinates[i].y); if (i != (coordinates.length - 1)) { buffer.append(","); } }*/ buffer.append("))"); } else { LOGGER.warning("No Exterior ring on polygon. " + "This encode only supports Polygons with exterior rings."); } if (polygon.getNumInteriorRing() > 0) { LOGGER.warning("Polygon contains Interior Rings. " + "These rings will not be included in the query."); } return buffer.toString(); } /** * Converts an Envelope in an SDO SQL geometry construction statement. * * @param envelope The envelope to encode. * @param srid DOCUMENT ME! * * @return An SDO SQL geometry object construction statement */ private static String toSDOGeom(Envelope envelope, int srid) { StringBuffer buffer = new StringBuffer(); buffer.append("MDSYS.SDO_GEOMETRY("); buffer.append("2003,"); if (srid > 0) { LOGGER.fine("Using layer SRID: " + srid); buffer.append(srid); } else { LOGGER.fine("Using NULL SRID: "); buffer.append("NULL"); } buffer.append(",NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,3),"); buffer.append("MDSYS.SDO_ORDINATE_ARRAY("); buffer.append(envelope.getMinX()); buffer.append(","); buffer.append(envelope.getMinY()); buffer.append(","); buffer.append(envelope.getMaxX()); buffer.append(","); buffer.append(envelope.getMaxY()); buffer.append("))"); return buffer.toString(); } /** * Handles Geometry Filter encoding. Currently only supports the encoding * of GEOMETRY_BBOX filters. If a GEOMETRY_BBOX filter is encounter it * will be converted into an SDO_RELATE() function. If another filter is * found, nothing will happen. * * @param geomFilter The geometry filter to encode. * * @see org.geotools.filter.FilterVisitor#visit(org.geotools.filter.GeometryFilter) */ public void visit(GeometryFilter geomFilter) { LOGGER.finer("Visiting a Geometry filter"); try { short filterType = geomFilter.getFilterType(); if ((filterType == AbstractFilter.GEOMETRY_DWITHIN) || (filterType == AbstractFilter.GEOMETRY_BEYOND)){ doSdoDistance((GeometryDistanceFilter)geomFilter); //} else if (filterType == AbstractFilter.GEOMETRY_INTERSECTS //|| filterType == AbstractFilter.GEOMETRY_BBOX) { //doNotDisjointFilter(geomFilter); } else if (SDO_RELATE_MASK_MAP.get(new Short(geomFilter.getFilterType())) != null) { doSdoRelate(geomFilter); } else { LOGGER.warning("Unknown filter type: " + geomFilter.getFilterType()); } } catch (IOException e) { LOGGER.warning("IO Error exporting geometry filter"); } } /** * Writes the SQL for the Like Filter. Assumes the current java * implemented wildcards for the Like Filter: . for multi and .? for * single. And replaces them with the SQL % and _, respectively. Currently * does nothing, and should not be called, not included in the * capabilities. * * @param filter the Like Filter to be visited. * * @task TODO: LikeFilter doesn't work right...revisit this when it does. * Need to think through the escape char, so it works right when * Java uses one, and escapes correctly with an '_'. */ public void visit(LikeFilter filter) { try { String pattern = filter.getPattern(); String multi = "\\Q"+filter.getWildcardMulti()+"\\E"; pattern = pattern.replaceAll( multi, SQL_WILD_MULTI); String single = "\\Q"+filter.getWildcardSingle()+"\\E"; pattern = pattern.replaceAll( single, SQL_WILD_SINGLE); //pattern = pattern.replace('\\', ''); //get rid of java escapes. out.write("UPPER("); ((Expression) filter.getValue()).accept(this); out.write(") LIKE "); out.write("UPPER('" + pattern + "')"); String esc = filter.getEscape(); if (pattern.indexOf(esc) != -1) { //if it uses the escape char out.write(" ESCAPE " + "'" + esc + "'"); //this needs testing } //TODO figure out when to add ESCAPE clause, probably just for the // '_' char. } catch (java.io.IOException ioe) { LOGGER.warning("Unable to export filter" + ioe); } } /** * Converts a literal expression into a valid SDO object. Only handles * Literal Geometries, all other literals are passed up to the parent. * * @param literal The Literal expression to encode. * * @see org.geotools.filter.FilterVisitor#visit(org.geotools.filter.LiteralExpression) */ public void visit(LiteralExpression literal) { if (literal.getType() == DefaultExpression.LITERAL_GEOMETRY) { Geometry geometry = (Geometry) literal.getLiteral(); try { int srid = -1; Integer sridO = (Integer) srids.get(currentGeomColumnName); if (sridO == null) { // try for default sridO = (Integer) srids.get(null); } if (sridO != null) { srid = sridO.intValue(); } out.write(toSDOGeom(geometry, srid)); } catch (IOException e) { LOGGER.warning("IO Error exporting Literal Geometry"); } } else { // can't do it, send it off to the parent super.visit(literal); } } /** * DOCUMENT ME! * * @param filter * * @see org.geotools.filter.SQLEncoder#visit(org.geotools.filter.FidFilter) */ public void visit(FidFilter filter) { if (fidColumn != null) { String[] fids = filter.getFids(); LOGGER.finer("Exporting FID=" + Arrays.asList(fids)); for (int i = 0; i < fids.length; i++) { try { out.write(fidColumn); out.write(" = '"); int pos; if ((pos = fids[i].indexOf('.')) != -1) { out.write(fids[i].substring(pos + 1)); } else { out.write(fids[i]); } out.write("'"); if (i < (fids.length - 1)) { out.write(" OR "); } } catch (IOException e) { LOGGER.warning("IO Error exporting FID Filter."); } } } else { super.visit(filter); } } /* * (non-Javadoc) * * @see org.geotools.filter.SQLEncoder#visit(org.geotools.filter.AttributeExpression) */ public void visit(AttributeExpression ae) throws RuntimeException { super.visit(ae); if (inGeomFilter) { if(featureType != null) { // handle default geometry as well AttributeDescriptor attributeType = (AttributeDescriptor) ae.evaluate( featureType ); currentGeomColumnName = attributeType.getLocalName(); } else { currentGeomColumnName = ae.getAttributePath(); } } } public void visit(IncludeFilter filter) { try { out.write("1 = 1"); } catch (java.io.IOException ioe) { throw new RuntimeException(IO_ERROR, ioe); } } public void visit(ExcludeFilter filter) { try { out.write("1 = 0"); } catch (java.io.IOException ioe) { throw new RuntimeException(IO_ERROR, ioe); } } @Override public void visit(CompareFilter filter) throws RuntimeException { DefaultExpression left = (DefaultExpression) filter.getLeftValue(); DefaultExpression right = (DefaultExpression) filter.getRightValue(); String type = (String) comparisions.get(new Integer( filter.getFilterType())); if ( !filter.isMatchingCase() ) { //only for == or != if ( filter.getFilterType() == Filter.COMPARE_EQUALS || filter.getFilterType() == Filter.COMPARE_NOT_EQUALS ) { //only for strings if ( left.getType() == Expression.LITERAL_STRING || right.getType() == Expression.LITERAL_STRING ) { try { out.write( "lower(" ); left.accept( this ); out.write( ")"); out.write( " " + type + " " ); out.write( "lower(" ); right.accept( this ); out.write( ")"); return; } catch(IOException e) { throw new RuntimeException("Error occurred writing filter", e); } } } } super.visit(filter); } }