001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.filter;
017:
018: import java.io.IOException;
019: import java.util.Arrays;
020: import java.util.HashMap;
021: import java.util.Map;
022: import java.util.Set;
023: import java.util.logging.Logger;
024:
025: import org.geotools.data.oracle.sdo.SDO;
026: import org.geotools.data.DataSourceException;
027: import org.opengis.filter.ExcludeFilter;
028: import org.opengis.filter.Id;
029: import org.opengis.filter.IncludeFilter;
030: import org.opengis.filter.PropertyIsBetween;
031: import org.opengis.filter.PropertyIsLike;
032: import org.opengis.filter.PropertyIsNull;
033: import org.opengis.filter.spatial.BBOX;
034: import org.opengis.filter.spatial.Beyond;
035: import org.opengis.filter.spatial.Contains;
036: import org.opengis.filter.spatial.Crosses;
037: import org.opengis.filter.spatial.DWithin;
038: import org.opengis.filter.spatial.Disjoint;
039: import org.opengis.filter.spatial.Equals;
040: import org.opengis.filter.spatial.Intersects;
041: import org.opengis.filter.spatial.Overlaps;
042: import org.opengis.filter.spatial.Touches;
043: import org.opengis.filter.spatial.Within;
044:
045: import com.vividsolutions.jts.geom.Coordinate;
046: import com.vividsolutions.jts.geom.CoordinateSequence;
047: import com.vividsolutions.jts.geom.CoordinateSequenceFactory;
048: import com.vividsolutions.jts.geom.Envelope;
049: import com.vividsolutions.jts.geom.Geometry;
050: import com.vividsolutions.jts.geom.LineString;
051: import com.vividsolutions.jts.geom.MultiLineString;
052: import com.vividsolutions.jts.geom.MultiPolygon;
053: import com.vividsolutions.jts.geom.Point;
054: import com.vividsolutions.jts.geom.Polygon;
055:
056: /**
057: * Encodes Geometry filters into valid oracle SDO statements.
058: *
059: * <p>
060: * At this stage it only supports the GEOMETRY_BBOX types.
061: * </p>
062: *
063: * <p>
064: * Encoded filters get written to the protected Writer called <code>out</code>
065: * </p>
066: *
067: * @author $Author: seangeo $
068: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/oracle-spatial/src/main/java/org/geotools/filter/SQLEncoderOracle.java $
069: * @version $Id: SQLEncoderOracle.java 27862 2007-11-12 19:51:19Z desruisseaux $
070: */
071: public class SQLEncoderOracle extends SQLEncoder {
072:
073: /** Logger - for logging */
074: private static final Logger LOGGER = org.geotools.util.logging.Logging
075: .getLogger("org.geotools.filter.SQLEncoderOracle");
076:
077: /** Contains filter type to SDO_RELATE mask type mappings */
078: private static final Map SDO_RELATE_MASK_MAP = new HashMap();
079:
080: /** The standard SQL multicharacter wild card. */
081: private static final String SQL_WILD_MULTI = "%";
082:
083: /** The standard SQL single character wild card. */
084: private static final String SQL_WILD_SINGLE = "_";
085:
086: /** Default tolerance for spatial queries. */
087: private static final String TOLERANCE = "0.001";
088:
089: static {
090: SDO_RELATE_MASK_MAP.put(new Short(
091: AbstractFilter.GEOMETRY_CONTAINS), "contains");
092: SDO_RELATE_MASK_MAP.put(new Short(
093: AbstractFilter.GEOMETRY_CROSSES), "overlapbydisjoint");
094: SDO_RELATE_MASK_MAP.put(new Short(
095: AbstractFilter.GEOMETRY_EQUALS), "equal");
096: SDO_RELATE_MASK_MAP
097: .put(new Short(AbstractFilter.GEOMETRY_OVERLAPS),
098: "overlapbyintersect");
099: SDO_RELATE_MASK_MAP.put(new Short(
100: AbstractFilter.GEOMETRY_TOUCHES), "touch");
101: SDO_RELATE_MASK_MAP.put(new Short(
102: AbstractFilter.GEOMETRY_WITHIN), "inside");
103: SDO_RELATE_MASK_MAP.put(new Short(
104: AbstractFilter.GEOMETRY_DISJOINT), "anyinteract");
105:
106: //Ok, back to using these, as the not disjoint turned out to be a big
107: //performance hit. I would really like to see some solid testing on
108: //these though, as with a trivial case it really did not seem to work
109: //right, not disjoint was giving different answers than anyinteract.
110: SDO_RELATE_MASK_MAP.put(
111: new Short(AbstractFilter.GEOMETRY_BBOX), "anyinteract");
112:
113: SDO_RELATE_MASK_MAP.put(new Short(
114: AbstractFilter.GEOMETRY_INTERSECTS), "anyinteract");
115: }
116:
117: /** The escaped version of the multiple wildcard for the REGEXP pattern. */
118: //private String escapedWildcardMulti = "\W.\\*"; // "\\*"
119: /** The escaped version of the single wildcard for the REGEXP pattern. */
120: //private String escapedWildcardSingle = "\\.\\?";
121: /**
122: * The Spatial Reference System IDs Keyed by ColumnName, value is Integer
123: * SRID number
124: */
125: private Map srids;
126: private String fidColumn;
127: private String currentGeomColumnName = null;
128: boolean inGeomFilter = false;
129:
130: public SQLEncoderOracle(String fidColumn, int defaultSRID) {
131: this (new HashMap());
132: this .fidColumn = fidColumn;
133: srids.put(null, new Integer(defaultSRID));
134:
135: setSqlNameEscape("\"");
136: }
137:
138: public SQLEncoderOracle(int defaultSRID) {
139: this (null, new HashMap());
140: srids.put(null, new Integer(defaultSRID));
141: }
142:
143: /**
144: * Creates a new SQLEncoderOracle with a specified SRID.
145: *
146: * @param fidColumn DOCUMENT ME!
147: * @param srids The Spatial Reference ID to use when generating SDO SQL
148: * statements.
149: */
150: public SQLEncoderOracle(String fidColumn, Map srids) {
151: currentGeomColumnName = null;
152: this .fidColumn = fidColumn;
153: this .srids = srids;
154:
155: Set geomCols = srids.keySet();
156:
157: if (geomCols.size() > 0) {
158: currentGeomColumnName = (String) geomCols.iterator().next();
159: }
160:
161: LOGGER.fine("SQLEncoderOracle: Geometric Column is: "
162: + currentGeomColumnName);
163:
164: setSqlNameEscape("\"");
165: }
166:
167: /**
168: * Creates a new SQLEncoderOracle with a specified SRID.
169: *
170: * @param srids The Spatial Reference ID to use when generating SDO SQL
171: * statements.
172: */
173: public SQLEncoderOracle(Map srids) {
174: this (null, srids);
175: }
176:
177: /**
178: * Sets the capabilities of this filter.
179: *
180: * @return FilterCapabilities for this Filter
181: */
182: protected FilterCapabilities createFilterCapabilities() {
183: FilterCapabilities capabilities = new FilterCapabilities();
184:
185: capabilities.addType(FilterCapabilities.LOGICAL);
186: capabilities.addAll(FilterCapabilities.LOGICAL_OPENGIS);
187: capabilities.addType(FilterCapabilities.SIMPLE_COMPARISONS);
188: capabilities
189: .addAll(FilterCapabilities.SIMPLE_COMPARISONS_OPENGIS);
190: capabilities.addType(FilterCapabilities.NULL_CHECK);
191: capabilities.addType(PropertyIsNull.class);
192: capabilities.addType(FilterCapabilities.BETWEEN);
193: capabilities.addType(PropertyIsBetween.class);
194: capabilities.addType(FilterCapabilities.FID);
195: capabilities.addType(Id.class);
196: capabilities.addType(FilterCapabilities.NONE);
197: capabilities.addType(IncludeFilter.class);
198: capabilities.addType(FilterCapabilities.ALL);
199: capabilities.addType(ExcludeFilter.class);
200: capabilities.addType(FilterCapabilities.LIKE);
201: capabilities.addType(PropertyIsLike.class);
202: capabilities.addType(FilterCapabilities.SPATIAL_BBOX);
203: capabilities.addType(BBOX.class);
204: capabilities.addType(FilterCapabilities.SPATIAL_CONTAINS);
205: capabilities.addType(Contains.class);
206: capabilities.addType(FilterCapabilities.SPATIAL_CROSSES);
207: capabilities.addType(Crosses.class);
208: capabilities.addType(FilterCapabilities.SPATIAL_DISJOINT);
209: capabilities.addType(Disjoint.class);
210: capabilities.addType(FilterCapabilities.SPATIAL_EQUALS);
211: capabilities.addType(Equals.class);
212: capabilities.addType(FilterCapabilities.SPATIAL_INTERSECT);
213: capabilities.addType(Intersects.class);
214: capabilities.addType(FilterCapabilities.SPATIAL_OVERLAPS);
215: capabilities.addType(Overlaps.class);
216: capabilities.addType(FilterCapabilities.SPATIAL_TOUCHES);
217: capabilities.addType(Touches.class);
218: capabilities.addType(FilterCapabilities.SPATIAL_WITHIN);
219: capabilities.addType(Within.class);
220: capabilities.addType(FilterCapabilities.SPATIAL_DWITHIN);
221: capabilities.addType(DWithin.class);
222: capabilities.addType(FilterCapabilities.SPATIAL_BEYOND);
223: capabilities.addType(Beyond.class);
224:
225: return capabilities;
226: }
227:
228: /**
229: * Reverting back to just using anyinteract, as Thijs says this is a bad
230: * performance hit
231: * This is a special case for bbox and intersects filters, as the former
232: * using of 'anyinteract' does not seem to be exactly a not disjoint,
233: * which is what is needed according to ogc specs.
234: *
235: * @param geomFilter DOCUMENT ME!
236: *
237: * @throws IOException DOCUMENT ME!
238: */
239: /* private void doNotDisjointFilter(GeometryFilter geomFilter)
240: throws IOException {
241: //String mask = (String) SDO_RELATE_MASK_MAP.get(new Short(
242: // geomFilter.getFilterType()));
243: Expression left = geomFilter.getLeftGeometry();
244: Expression right = geomFilter.getRightGeometry();
245:
246: if (((left != null) || (currentGeomColumnName != null))
247: && (right != null)) {
248: inGeomFilter = true;
249: out.write("NOT SDO_RELATE(");
250:
251: if (left != null) {
252: left.accept(this);
253: } else {
254: out.write(currentGeomColumnName);
255: }
256:
257: out.write(",");
258: right.accept(this);
259: out.write(",'mask=disjoint querytype=WINDOW') = 'TRUE' ");
260: inGeomFilter = false;
261: } else {
262: LOGGER.warning("Invalid filter. Cannot have a Geometry filter "
263: + "with only one expression.");
264: }
265: }*/
266:
267: private void doSdoRelate(GeometryFilter geomFilter)
268: throws IOException {
269: String mask = (String) SDO_RELATE_MASK_MAP.get(new Short(
270: geomFilter.getFilterType()));
271: AttributeExpression attExpr;
272: LiteralExpression geomExpr;
273: Expression left = geomFilter.getLeftGeometry();
274: Expression right = geomFilter.getRightGeometry();
275: if (left instanceof AttributeExpression
276: && right instanceof LiteralExpression) {
277: attExpr = (AttributeExpression) left;
278: geomExpr = (LiteralExpression) right;
279: } else if (right instanceof AttributeExpression
280: && left instanceof LiteralExpression) {
281: attExpr = (AttributeExpression) right;
282: geomExpr = (LiteralExpression) left;
283: } else {
284: String err = "Oracle currently supports one geometry and one "
285: + "attribute expr. You gave: "
286: + left
287: + ", "
288: + right;
289: throw new DataSourceException(err);
290: }
291:
292: if (((attExpr != null) || (currentGeomColumnName != null))
293: && (geomExpr != null) && (mask != null)) {
294: inGeomFilter = true;
295: out.write("SDO_RELATE(");
296:
297: if (attExpr != null) {
298: attExpr.accept(this );
299: } else {
300: out.write("\"" + currentGeomColumnName + "\"");
301: }
302:
303: out.write(",");
304: geomExpr.accept(this );
305: // for disjoint we ask for no interaction, anyinteract == false
306: if (geomFilter.getFilterType() == AbstractFilter.GEOMETRY_DISJOINT) {
307: out.write(",'mask=" + mask
308: + " querytype=WINDOW') <> 'TRUE' ");
309: } else {
310: out.write(",'mask=" + mask
311: + " querytype=WINDOW') = 'TRUE' ");
312: }
313:
314: inGeomFilter = false;
315: } else {
316: LOGGER
317: .warning("Invalid filter. Cannot have a Geometry filter "
318: + "with only one expression.");
319: }
320: }
321:
322: /**
323: * Performs a geometry distance filter, must be either a dwithin or a
324: * a beyond filter. Uses the SDO_WITHIN_DISTANCE function, dwithin matches
325: * for true, beyond for false.
326: *
327: * @param geomFilter the filter to use, must be a dwithin or beyond
328: */
329: private void doSdoDistance(GeometryDistanceFilter geomFilter)
330: throws IOException {
331: // String mask = (String) SDO_RELATE_MASK_MAP.get(new Short(
332: // geomFilter.getFilterType()));
333: //TODO: don't assume left and right are correct order, see doSdoRelate
334: Expression left = geomFilter.getLeftGeometry();
335: Expression right = geomFilter.getRightGeometry();
336: double distance = geomFilter.getDistance();
337: //only dwithin and beyond, dwithin matches true for sdo_within_distance
338: boolean isDWithin = geomFilter.getFilterType() == AbstractFilter.GEOMETRY_DWITHIN;
339: String boolValue = isDWithin ? "TRUE" : "FALSE";
340:
341: if ((left != null) && (right != null)) {
342: inGeomFilter = true;
343: out.write("SDO_WITHIN_DISTANCE(");
344: left.accept(this );
345: out.write(",");
346: right.accept(this );
347: out.write(",'distance=" + distance + "') = '" + boolValue
348: + "' ");
349: inGeomFilter = false;
350: } else {
351: LOGGER
352: .warning("Invalid filter for DWithin. Cannot have a Geometry filter "
353: + "with only one expression.");
354: }
355: }
356:
357: /**
358: * Converts JTS Geometry to a String version of a SDO Geometry. This
359: * should move to a utility class, as we now have more than one class
360: * using this (which is why it changed to public static)
361: *
362: * TODO: Multi eometries
363: *
364: * @param geometry The JTS Geometry to convert.
365: * @param srid DOCUMENT ME!
366: *
367: * @return A String representation of the SDO Geometry.
368: */
369: public static String toSDOGeom(Geometry geometry, int srid) {
370: if (Point.class.isAssignableFrom(geometry.getClass())) {
371: return toSDOGeom((Point) geometry, srid);
372: } else if (LineString.class.isAssignableFrom(geometry
373: .getClass())) {
374: return toSDOGeom((LineString) geometry, srid);
375: } else if (Polygon.class.isAssignableFrom(geometry.getClass())) {
376: if (geometry.equals(geometry.getEnvelope())) {
377: return toSDOGeom(geometry.getEnvelopeInternal(), srid);
378: } else {
379: return toSDOGeom((Polygon) geometry, srid);
380: }
381: } else if (MultiLineString.class.isAssignableFrom(geometry
382: .getClass())) {
383: return toSDOGeom((MultiLineString) geometry, srid);
384: } else if (MultiPolygon.class.isAssignableFrom(geometry
385: .getClass())) {
386: return toSDOGeom((MultiPolygon) geometry, srid);
387: } else {
388: LOGGER
389: .warning("Got a literal geometry that I can't handle: "
390: + geometry.getClass().getName());
391:
392: return "";
393: }
394: }
395:
396: /**
397: * TODO: Encode more then 1
398: * @param line
399: * @param srid
400: */
401: private static String toSDOGeom(MultiLineString line, int srid) {
402: if (line.getNumGeometries() == 1) {
403: return toSDOGeom(line.getGeometryN(0), srid);
404: }
405: throw new UnsupportedOperationException(
406: "Cannot encode MultiLineString (yet)");
407: }
408:
409: /**
410: * TODO: Encode more then 1
411: * @param line
412: * @param srid
413: */
414: private static String toSDOGeom(MultiPolygon polygon, int srid) {
415: if (polygon.getNumGeometries() == 1) {
416: return toSDOGeom(polygon.getGeometryN(0), srid);
417: }
418: throw new UnsupportedOperationException(
419: "Cannot encode MultiPolygon (yet)");
420: }
421:
422: /**
423: * Converts a LineString Geometry in an SDO SQL geometry construction
424: * statement.
425: *
426: * <p>
427: * 2D geometries is assumed. If higher dimensional geometries are used the
428: * query will be encoded as a 2D geometry.
429: * </p>
430: *
431: * @param line The line to encode.
432: * @param srid DOCUMENT ME!
433: *
434: * @return An SDO SQL geometry object construction statement
435: */
436: private static String toSDOGeom(LineString line, int srid) {
437: if (SDO.D(line) > 2) {
438: LOGGER
439: .warning(""
440: + SDO.D(line)
441: + " dimensioned geometry provided."
442: + " This encoder only supports 2D geometries. The query will be constructed as"
443: + " a 2D query.");
444: }
445:
446: StringBuffer buffer = new StringBuffer("MDSYS.SDO_GEOMETRY(");
447:
448: buffer.append(SDO.D(line));
449: buffer.append("002,");
450:
451: if (srid > 0) {
452: LOGGER.fine("Using layer SRID: " + srid);
453: buffer.append(srid);
454: } else {
455: LOGGER.fine("Using NULL SRID: ");
456: buffer.append("NULL");
457: }
458:
459: buffer.append(",NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),");
460: buffer.append("MDSYS.SDO_ORDINATE_ARRAY(");
461:
462: Coordinate[] coordinates = line.getCoordinates();
463:
464: for (int i = 0; i < coordinates.length; i++) {
465: buffer.append(coordinates[i].x);
466: buffer.append(",");
467: buffer.append(coordinates[i].y);
468:
469: if (i != (coordinates.length - 1)) {
470: buffer.append(",");
471: }
472: }
473:
474: buffer.append("))");
475:
476: return buffer.toString();
477: }
478:
479: /**
480: * Converts a Point Geometry in an SDO SQL geometry construction statement.
481: *
482: * <p>
483: * 2D geometries is assumed. If higher dimensional geometries are used the
484: * query will be encoded as a 2D geometry.
485: * </p>
486: *
487: * @param point The point to encode.
488: * @param srid DOCUMENT ME!
489: *
490: * @return An SDO SQL geometry object construction statement
491: */
492: private static String toSDOGeom(Point point, int srid) {
493: if (SDO.D(point) > 2) {
494: LOGGER
495: .warning(""
496: + SDO.D(point)
497: + " dimensioned geometry provided."
498: + " This encoder only supports 2D geometries. The query will be constructed as"
499: + " a 2D query.");
500: }
501:
502: StringBuffer buffer = new StringBuffer("MDSYS.SDO_GEOMETRY(");
503:
504: buffer.append(SDO.D(point));
505: buffer.append("001,");
506:
507: if (srid > 0) {
508: LOGGER.fine("Using layer SRID: " + srid);
509: buffer.append(srid);
510: } else {
511: LOGGER.fine("Using NULL SRID: ");
512: buffer.append("NULL");
513: }
514:
515: buffer.append(",MDSYS.SDO_POINT_TYPE(");
516: buffer.append(point.getX());
517: buffer.append(",");
518: buffer.append(point.getY());
519: buffer.append(",NULL),NULL,NULL)");
520:
521: return buffer.toString();
522: }
523:
524: /**
525: * Converts a Polygon Geometry in an SDO SQL geometry construction
526: * statement.
527: *
528: * <p>
529: * 2D geometries is assumed. If higher dimensional geometries are used the
530: * query will be encoded as a 2D geometry.
531: * </p>
532: *
533: * @param polygon The polygon to encode.
534: * @param srid DOCUMENT ME!
535: *
536: * @return An SDO SQL geometry object construction statement
537: */
538: private static String toSDOGeom(Polygon polygon, int srid) {
539: StringBuffer buffer = new StringBuffer();
540:
541: if (SDO.D(polygon) > 2) {
542: LOGGER
543: .warning(""
544: + SDO.D(polygon)
545: + " dimensioned geometry provided."
546: + " This encoder only supports 2D geometries. The query will be constructed as"
547: + " a 2D query.");
548: }
549:
550: if (polygon.getExteriorRing() != null) {
551: buffer.append("MDSYS.SDO_GEOMETRY(");
552: buffer.append(SDO.D(polygon));
553: buffer.append("003,");
554:
555: if (srid > 0) {
556: LOGGER.fine("Using layer SRID: " + srid);
557: buffer.append(srid);
558: } else {
559: LOGGER.fine("Using NULL SRID: ");
560: buffer.append("NULL");
561: }
562:
563: buffer.append(",NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1),");
564: buffer.append("MDSYS.SDO_ORDINATE_ARRAY(");
565:
566: CoordinateSequenceFactory fact = polygon.getFactory()
567: .getCoordinateSequenceFactory();
568: CoordinateSequence exterior = polygon.getExteriorRing()
569: .getCoordinateSequence();
570: CoordinateSequence coordSeq = SDO.counterClockWise(fact,
571: exterior);
572:
573: for (int i = 0, size = coordSeq.size(); i < size; i++) {
574: Coordinate cur = coordSeq.getCoordinate(i);
575: buffer.append(cur.x);
576: buffer.append(",");
577: buffer.append(cur.y);
578:
579: if (i != (size - 1)) {
580: buffer.append(",");
581: }
582: }
583:
584: /* This could be expensive if coordSeq implementation is not an
585: an array. Leaving in for now as I can't test, and this is
586: more likely to work right.
587: Coordinate[] coordinates = coordSeq.toCoordinateArray();
588: for (int i = 0; i < coordinates.length; i++) {
589: buffer.append(coordinates[i].x);
590: buffer.append(",");
591: buffer.append(coordinates[i].y);
592:
593: if (i != (coordinates.length - 1)) {
594: buffer.append(",");
595: }
596: }*/
597:
598: buffer.append("))");
599: } else {
600: LOGGER
601: .warning("No Exterior ring on polygon. "
602: + "This encode only supports Polygons with exterior rings.");
603: }
604:
605: if (polygon.getNumInteriorRing() > 0) {
606: LOGGER.warning("Polygon contains Interior Rings. "
607: + "These rings will not be included in the query.");
608: }
609:
610: return buffer.toString();
611: }
612:
613: /**
614: * Converts an Envelope in an SDO SQL geometry construction statement.
615: *
616: * @param envelope The envelope to encode.
617: * @param srid DOCUMENT ME!
618: *
619: * @return An SDO SQL geometry object construction statement
620: */
621: private static String toSDOGeom(Envelope envelope, int srid) {
622: StringBuffer buffer = new StringBuffer();
623: buffer.append("MDSYS.SDO_GEOMETRY(");
624: buffer.append("2003,");
625:
626: if (srid > 0) {
627: LOGGER.fine("Using layer SRID: " + srid);
628: buffer.append(srid);
629: } else {
630: LOGGER.fine("Using NULL SRID: ");
631: buffer.append("NULL");
632: }
633:
634: buffer.append(",NULL,MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,3),");
635: buffer.append("MDSYS.SDO_ORDINATE_ARRAY(");
636: buffer.append(envelope.getMinX());
637: buffer.append(",");
638: buffer.append(envelope.getMinY());
639: buffer.append(",");
640: buffer.append(envelope.getMaxX());
641: buffer.append(",");
642: buffer.append(envelope.getMaxY());
643: buffer.append("))");
644:
645: return buffer.toString();
646: }
647:
648: /**
649: * Handles Geometry Filter encoding. Currently only supports the encoding
650: * of GEOMETRY_BBOX filters. If a GEOMETRY_BBOX filter is encounter it
651: * will be converted into an SDO_RELATE() function. If another filter is
652: * found, nothing will happen.
653: *
654: * @param geomFilter The geometry filter to encode.
655: *
656: * @see org.geotools.filter.FilterVisitor#visit(org.geotools.filter.GeometryFilter)
657: */
658: public void visit(GeometryFilter geomFilter) {
659: LOGGER.finer("Visiting a Geometry filter");
660:
661: try {
662: short filterType = geomFilter.getFilterType();
663: if ((filterType == AbstractFilter.GEOMETRY_DWITHIN)
664: || (filterType == AbstractFilter.GEOMETRY_BEYOND)) {
665: doSdoDistance((GeometryDistanceFilter) geomFilter);
666: //} else if (filterType == AbstractFilter.GEOMETRY_INTERSECTS
667: //|| filterType == AbstractFilter.GEOMETRY_BBOX) {
668: //doNotDisjointFilter(geomFilter);
669: } else if (SDO_RELATE_MASK_MAP.get(new Short(geomFilter
670: .getFilterType())) != null) {
671: doSdoRelate(geomFilter);
672: } else {
673: LOGGER.warning("Unknown filter type: "
674: + geomFilter.getFilterType());
675: }
676: } catch (IOException e) {
677: LOGGER.warning("IO Error exporting geometry filter");
678: }
679: }
680:
681: /**
682: * Writes the SQL for the Like Filter. Assumes the current java
683: * implemented wildcards for the Like Filter: . for multi and .? for
684: * single. And replaces them with the SQL % and _, respectively. Currently
685: * does nothing, and should not be called, not included in the
686: * capabilities.
687: *
688: * @param filter the Like Filter to be visited.
689: *
690: * @task TODO: LikeFilter doesn't work right...revisit this when it does.
691: * Need to think through the escape char, so it works right when
692: * Java uses one, and escapes correctly with an '_'.
693: */
694: public void visit(LikeFilter filter) {
695: try {
696: String pattern = filter.getPattern();
697:
698: String multi = "\\Q" + filter.getWildcardMulti() + "\\E";
699: pattern = pattern.replaceAll(multi, SQL_WILD_MULTI);
700:
701: String single = "\\Q" + filter.getWildcardSingle() + "\\E";
702: pattern = pattern.replaceAll(single, SQL_WILD_SINGLE);
703:
704: //pattern = pattern.replace('\\', ''); //get rid of java escapes.
705: out.write("UPPER(");
706: ((Expression) filter.getValue()).accept(this );
707: out.write(") LIKE ");
708: out.write("UPPER('" + pattern + "')");
709:
710: String esc = filter.getEscape();
711:
712: if (pattern.indexOf(esc) != -1) { //if it uses the escape char
713: out.write(" ESCAPE " + "'" + esc + "'"); //this needs testing
714: }
715:
716: //TODO figure out when to add ESCAPE clause, probably just for the
717: // '_' char.
718: } catch (java.io.IOException ioe) {
719: LOGGER.warning("Unable to export filter" + ioe);
720: }
721: }
722:
723: /**
724: * Converts a literal expression into a valid SDO object. Only handles
725: * Literal Geometries, all other literals are passed up to the parent.
726: *
727: * @param literal The Literal expression to encode.
728: *
729: * @see org.geotools.filter.FilterVisitor#visit(org.geotools.filter.LiteralExpression)
730: */
731: public void visit(LiteralExpression literal) {
732: if (literal.getType() == DefaultExpression.LITERAL_GEOMETRY) {
733: Geometry geometry = (Geometry) literal.getLiteral();
734:
735: try {
736: int srid = -1;
737: Integer sridO = (Integer) srids
738: .get(currentGeomColumnName);
739:
740: if (sridO == null) {
741: // try for default
742: sridO = (Integer) srids.get(null);
743: }
744:
745: if (sridO != null) {
746: srid = sridO.intValue();
747: }
748:
749: out.write(toSDOGeom(geometry, srid));
750: } catch (IOException e) {
751: LOGGER.warning("IO Error exporting Literal Geometry");
752: }
753: } else {
754: // can't do it, send it off to the parent
755: super .visit(literal);
756: }
757: }
758:
759: /**
760: * DOCUMENT ME!
761: *
762: * @param filter
763: *
764: * @see org.geotools.filter.SQLEncoder#visit(org.geotools.filter.FidFilter)
765: */
766: public void visit(FidFilter filter) {
767: if (fidColumn != null) {
768: String[] fids = filter.getFids();
769: LOGGER.finer("Exporting FID=" + Arrays.asList(fids));
770:
771: for (int i = 0; i < fids.length; i++) {
772: try {
773: out.write(fidColumn);
774: out.write(" = '");
775:
776: int pos;
777:
778: if ((pos = fids[i].indexOf('.')) != -1) {
779: out.write(fids[i].substring(pos + 1));
780: } else {
781: out.write(fids[i]);
782: }
783:
784: out.write("'");
785:
786: if (i < (fids.length - 1)) {
787: out.write(" OR ");
788: }
789: } catch (IOException e) {
790: LOGGER.warning("IO Error exporting FID Filter.");
791: }
792: }
793: } else {
794: super .visit(filter);
795: }
796: }
797:
798: /*
799: * (non-Javadoc)
800: *
801: * @see org.geotools.filter.SQLEncoder#visit(org.geotools.filter.AttributeExpression)
802: */
803: public void visit(AttributeExpression ae) throws RuntimeException {
804: super.visit(ae);
805:
806: if (inGeomFilter) {
807: currentGeomColumnName = ae.getAttributePath();
808: }
809: }
810: }
|