001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002-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;
009: * version 2.1 of the License.
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.io.StringWriter;
020: import java.io.Writer;
021: import java.util.Arrays;
022: import java.util.HashMap;
023: import java.util.Map;
024: import java.util.logging.Logger;
025:
026: import org.geotools.data.jdbc.fidmapper.FIDMapper;
027: import org.geotools.feature.AttributeType;
028: import org.geotools.feature.FeatureType;
029: import org.geotools.util.Converters;
030: import org.opengis.filter.ExcludeFilter;
031: import org.opengis.filter.IncludeFilter;
032:
033: import com.vividsolutions.jts.geom.Geometry;
034:
035: /**
036: * Encodes a filter into a SQL WHERE statement. It should hopefully be generic
037: * enough that any SQL database will work with it, though it has only been
038: * tested with MySQL and Postgis. This generic SQL encoder should eventually
039: * be able to encode all filters except Geometry Filters (currently
040: * LikeFilters are not yet fully implemented, but when they are they should be
041: * generic enough). This is because the OGC's SFS for SQL document specifies
042: * two ways of doing SQL databases, one with native geometry types and one
043: * without. To implement an encoder for one of the two types simply subclass
044: * off of this encoder and put in the proper GeometryFilter visit method. Then
045: * add the filter types supported to the capabilities in the static
046: * capabilities.addType block.
047: *
048: * @author Chris Holmes, TOPP
049: *
050: * @deprecated Please use org.geotools.data.jdbc.FilterToSQL which uses
051: * opengis filters instead of these geotools filters.
052: *
053: * @task TODO: Implement LikeFilter encoding, need to figure out escape chars,
054: * the rest of the code should work right. Once fixed be sure to add
055: * the LIKE type to capabilities, so others know that they can be
056: * encoded.
057: * @task REVISIT: need to figure out exceptions, we're currently eating io
058: * errors, which is bad. Probably need a generic visitor exception.
059: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/jdbc/src/main/java/org/geotools/filter/SQLEncoder.java $
060: */
061: public class SQLEncoder implements org.geotools.filter.FilterVisitor2 {
062: /** error message for exceptions */
063: protected static final String IO_ERROR = "io problem writing filter";
064:
065: /** The filter types that this class can encode */
066: protected FilterCapabilities capabilities = null;
067:
068: /** Standard java logger */
069: private static Logger LOGGER = org.geotools.util.logging.Logging
070: .getLogger("org.geotools.filter");
071:
072: /** Map of comparison types to sql representation */
073: protected static Map comparisions = new HashMap();
074:
075: /** Map of spatial types to sql representation */
076: private static Map spatial = new HashMap();
077:
078: /** Map of logical types to sql representation */
079: private static Map logical = new HashMap();
080:
081: /** Map of expression types to sql representation */
082: private static Map expressions = new HashMap();
083:
084: static {
085: comparisions.put(new Integer(AbstractFilter.COMPARE_EQUALS),
086: "=");
087: comparisions.put(
088: new Integer(AbstractFilter.COMPARE_NOT_EQUALS), "!=");
089: comparisions.put(new Integer(
090: AbstractFilter.COMPARE_GREATER_THAN), ">");
091: comparisions.put(new Integer(
092: AbstractFilter.COMPARE_GREATER_THAN_EQUAL), ">=");
093: comparisions.put(new Integer(AbstractFilter.COMPARE_LESS_THAN),
094: "<");
095: comparisions.put(new Integer(
096: AbstractFilter.COMPARE_LESS_THAN_EQUAL), "<=");
097: comparisions.put(new Integer(AbstractFilter.LIKE), "LIKE");
098: comparisions.put(new Integer(AbstractFilter.NULL), "IS NULL");
099: comparisions
100: .put(new Integer(AbstractFilter.BETWEEN), "BETWEEN");
101:
102: expressions.put(new Integer(DefaultExpression.MATH_ADD), "+");
103: expressions
104: .put(new Integer(DefaultExpression.MATH_DIVIDE), "/");
105: expressions.put(new Integer(DefaultExpression.MATH_MULTIPLY),
106: "*");
107: expressions.put(new Integer(DefaultExpression.MATH_SUBTRACT),
108: "-");
109:
110: //more to come?
111: spatial.put(new Integer(AbstractFilter.GEOMETRY_EQUALS),
112: "Equals");
113: spatial.put(new Integer(AbstractFilter.GEOMETRY_DISJOINT),
114: "Disjoint");
115: spatial.put(new Integer(AbstractFilter.GEOMETRY_INTERSECTS),
116: "Intersects");
117: spatial.put(new Integer(AbstractFilter.GEOMETRY_TOUCHES),
118: "Touches");
119: spatial.put(new Integer(AbstractFilter.GEOMETRY_CROSSES),
120: "Crosses");
121: spatial.put(new Integer(AbstractFilter.GEOMETRY_WITHIN),
122: "Within");
123: spatial.put(new Integer(AbstractFilter.GEOMETRY_CONTAINS),
124: "Contains");
125: spatial.put(new Integer(AbstractFilter.GEOMETRY_OVERLAPS),
126: "Overlaps");
127: spatial.put(new Integer(AbstractFilter.GEOMETRY_BEYOND),
128: "Beyond");
129: spatial.put(new Integer(AbstractFilter.GEOMETRY_BBOX), "BBOX");
130:
131: logical.put(new Integer(AbstractFilter.LOGIC_AND), "AND");
132: logical.put(new Integer(AbstractFilter.LOGIC_OR), "OR");
133: logical.put(new Integer(AbstractFilter.LOGIC_NOT), "NOT");
134: }
135:
136: //use these when Like is implemented.
137: //The standard SQL multicharacter wild card.
138: //private static String SQL_WILD_MULTI = "%";
139: //The standard SQL single character wild card.
140: //private static String SQL_WILD_SINGLE = "_";
141: // The escaped version of the single wildcard for the REGEXP pattern.
142: //private static String escapedWildcardSingle = "\\.\\?";
143: // The escaped version of the multiple wildcard for the REGEXP pattern.
144: //private static String escapedWildcardMulti = "\\.\\*";
145:
146: /** Character used to escape database schema, table and column names */
147: private String sqlNameEscape = "";
148:
149: /** where to write the constructed string from visiting the filters. */
150: protected Writer out;
151:
152: /** the fid mapper used to encode the fid filters */
153: protected FIDMapper mapper;
154:
155: /** the schmema the encoder will be used to be encode sql for */
156: protected FeatureType featureType;
157:
158: /**
159: * A type to use as context when encoding literal.
160: * NOTE: when we move to geoapi filter visitor api, this will not be needed.
161: */
162: protected Class context = null;
163:
164: /**
165: * Empty constructor
166: */
167: public SQLEncoder() {
168: }
169:
170: /**
171: * Sets the featuretype the encoder is encoding sql for.
172: * <p>
173: * This is used for context for attribute expressions when encoding to sql.
174: * </p>
175: *
176: * @param featureType
177: */
178: public void setFeatureType(FeatureType featureType) {
179: this .featureType = featureType;
180: }
181:
182: /**
183: * Convenience constructor to perform the whole encoding process at once.
184: *
185: * @param out the writer to encode the SQL to.
186: * @param filter the Filter to be encoded.
187: *
188: * @throws SQLEncoderException If there were problems encoding
189: */
190: public SQLEncoder(Writer out, Filter filter)
191: throws SQLEncoderException {
192: if (getCapabilities().fullySupports(filter)) {
193: this .out = out;
194:
195: try {
196: out.write("WHERE ");
197: filter.accept(this );
198:
199: //out.write(";"); this should probably be added by client.
200: } catch (java.io.IOException ioe) {
201: throw new SQLEncoderException(
202: "Problem writing filter: ", ioe);
203: }
204: } else {
205: throw new SQLEncoderException("Filter type not supported");
206: }
207: }
208:
209: /**
210: * Sets the FIDMapper that will be used in subsequente visit calls. There
211: * must be a FIDMapper in order to invoke the FIDFilter encoder.
212: *
213: * @param mapper
214: */
215: public void setFIDMapper(FIDMapper mapper) {
216: this .mapper = mapper;
217: }
218:
219: /**
220: * Sets the capabilities of this filter.
221: *
222: * @return FilterCapabilities for this Filter
223: */
224: protected FilterCapabilities createFilterCapabilities() {
225: FilterCapabilities capabilities = new FilterCapabilities();
226:
227: capabilities.addType(FilterCapabilities.LOGICAL);
228: capabilities.addType(FilterCapabilities.SIMPLE_COMPARISONS);
229: capabilities.addType(FilterCapabilities.NULL_CHECK);
230: capabilities.addType(FilterCapabilities.BETWEEN);
231: capabilities.addType(FilterCapabilities.FID);
232: capabilities.addType(FilterCapabilities.NONE);
233: capabilities.addType(FilterCapabilities.ALL);
234:
235: return capabilities;
236: }
237:
238: /**
239: * Performs the encoding, sends the encoded sql to the writer passed in.
240: *
241: * @param out the writer to encode the SQL to.
242: * @param filter the Filter to be encoded.
243: *
244: * @throws SQLEncoderException If filter type not supported, or if there
245: * were io problems.
246: */
247: public void encode(Writer out, org.opengis.filter.Filter filter)
248: throws SQLEncoderException {
249: if (getCapabilities().fullySupports(filter)) {
250: this .out = out;
251:
252: try {
253: out.write("WHERE ");
254: Filters.accept(filter, this );
255:
256: //out.write(";");
257: } catch (java.io.IOException ioe) {
258: LOGGER.warning("Unable to export filter" + ioe);
259: throw new SQLEncoderException(
260: "Problem writing filter: ", ioe);
261: }
262: } else {
263: throw new SQLEncoderException("Filter type not supported");
264: }
265: }
266:
267: /**
268: * Performs the encoding, returns a string of the encoded SQL.
269: *
270: * @param filter the Filter to be encoded.
271: *
272: * @return the string of the SQL where statement.
273: *
274: * @throws SQLEncoderException If filter type not supported, or if there
275: * were io problems.
276: */
277: public String encode(org.opengis.filter.Filter filter)
278: throws SQLEncoderException {
279: StringWriter output = new StringWriter();
280: encode(output, filter);
281:
282: return output.getBuffer().toString();
283: }
284:
285: /**
286: * Describes the capabilities of this encoder.
287: *
288: * <p>
289: * Performs lazy creation of capabilities.
290: * </p>
291: *
292: * @return The capabilities supported by this encoder.
293: */
294: public synchronized FilterCapabilities getCapabilities() {
295: if (capabilities == null) {
296: capabilities = createFilterCapabilities();
297: }
298:
299: return capabilities; //maybe clone? Make immutable somehow
300: }
301:
302: /**
303: * This should never be called. This can only happen if a subclass of
304: * AbstractFilter failes to implement its own version of
305: * accept(FilterVisitor);
306: *
307: * @param filter The filter to visit
308: *
309: * @throws RuntimeException for IO Encoding problems.
310: *
311: * @task REVISIT: I don't think Filter.INCLUDE and Filter.EXCLUDE should be
312: * handled here. They should have their own methods, but they don't
313: * have interfaces, so I don't know if that's possible.
314: */
315: public void visit(Filter filter) {
316: try {
317: if (filter.getFilterType() == FilterType.NONE) {
318: out.write("TRUE");
319: } else if (filter.getFilterType() == FilterType.ALL) {
320: out.write("FALSE");
321: } else {
322: LOGGER.warning("exporting unknown filter type:"
323: + filter.toString());
324:
325: //throw new RuntimeException("Do not know how to export filter:"+filter.toString() );
326: }
327: } catch (java.io.IOException ioe) {
328: throw new RuntimeException(IO_ERROR, ioe);
329: }
330: }
331:
332: /**
333: * Writes the SQL for the Between Filter.
334: *
335: * @param filter the Filter to be visited.
336: *
337: * @throws RuntimeException for io exception with writer
338: */
339: public void visit(BetweenFilter filter) throws RuntimeException {
340: LOGGER.finer("exporting BetweenFilter");
341:
342: DefaultExpression left = (DefaultExpression) filter
343: .getLeftValue();
344: DefaultExpression right = (DefaultExpression) filter
345: .getRightValue();
346: DefaultExpression mid = (DefaultExpression) filter
347: .getMiddleValue();
348: LOGGER.finer("Filter type id is " + filter.getFilterType());
349: LOGGER
350: .finer("Filter type text is "
351: + comparisions.get(new Integer(filter
352: .getFilterType())));
353:
354: try {
355: mid.accept(this );
356: out.write(" BETWEEN ");
357: left.accept(this );
358: out.write(" AND ");
359: right.accept(this );
360: } catch (java.io.IOException ioe) {
361: throw new RuntimeException(IO_ERROR, ioe);
362: }
363: }
364:
365: /**
366: * Writes the SQL for the Like Filter. Assumes the current java
367: * implemented wildcards for the Like Filter: . for multi and .? for
368: * single. And replaces them with the SQL % and _, respectively. Currently
369: * does nothing, and should not be called, not included in the
370: * capabilities.
371: *
372: * @param filter the Like Filter to be visited.
373: *
374: * @throws UnsupportedOperationException always, as likes aren't
375: * implemented yet.
376: *
377: * @task REVISIT: Need to think through the escape char, so it works right
378: * when Java uses one, and escapes correctly with an '_'.
379: */
380: public void visit(LikeFilter filter)
381: throws UnsupportedOperationException {
382: char esc = filter.getEscape().charAt(0);
383: char multi = filter.getWildcardMulti().charAt(0);
384: char single = filter.getWildcardSingle().charAt(0);
385: String pattern = LikeFilterImpl.convertToSQL92(esc, multi,
386: single, filter.getPattern());
387:
388: DefaultExpression att = (DefaultExpression) filter.getValue();
389:
390: try {
391: att.accept(this );
392: out.write(" LIKE '");
393: out.write(pattern);
394: out.write("' ");
395: } catch (java.io.IOException ioe) {
396: throw new RuntimeException(IO_ERROR, ioe);
397: }
398: }
399:
400: /**
401: * Writes the SQL for the Logic Filter.
402: *
403: * @param filter the logic statement to be turned into SQL.
404: *
405: * @throws RuntimeException for io exception with writer
406: */
407: public void visit(LogicFilter filter) throws RuntimeException {
408: LOGGER.finer("exporting LogicFilter");
409:
410: filter.getFilterType();
411:
412: String type = (String) logical.get(new Integer(filter
413: .getFilterType()));
414:
415: try {
416: java.util.Iterator list = filter.getFilterIterator();
417:
418: if (filter.getFilterType() == AbstractFilter.LOGIC_NOT) {
419: out.write(" NOT (");
420: Filters.accept((org.opengis.filter.Filter) list.next(),
421: this );
422:
423: out.write(")");
424: } else { //AND or OR
425: out.write("(");
426:
427: while (list.hasNext()) {
428: Filters.accept((org.opengis.filter.Filter) list
429: .next(), this );
430:
431: if (list.hasNext()) {
432: out.write(" " + type + " ");
433: }
434: }
435:
436: out.write(")");
437: }
438: } catch (java.io.IOException ioe) {
439: throw new RuntimeException(IO_ERROR, ioe);
440: }
441: }
442:
443: /**
444: * Writes the SQL for a Compare Filter.
445: *
446: * DJB: note, postgis overwrites this implementation because of the way
447: * null is handled. This is for <PropertyIsNull> filters and <PropertyIsEqual> filters
448: * are handled. They will come here with "property = null".
449: * NOTE:
450: * SELECT * FROM <table> WHERE <column> isnull; -- postgresql
451: * SELECT * FROM <table> WHERE isnull(<column>); -- oracle???
452: *
453: * @param filter the comparison to be turned into SQL.
454: *
455: * @throws RuntimeException for io exception with writer
456: */
457: public void visit(CompareFilter filter) throws RuntimeException {
458: LOGGER.finer("exporting SQL ComparisonFilter");
459:
460: DefaultExpression left = (DefaultExpression) filter
461: .getLeftValue();
462: DefaultExpression right = (DefaultExpression) filter
463: .getRightValue();
464: LOGGER.finer("Filter type id is " + filter.getFilterType());
465: LOGGER
466: .finer("Filter type text is "
467: + comparisions.get(new Integer(filter
468: .getFilterType())));
469:
470: String type = (String) comparisions.get(new Integer(filter
471: .getFilterType()));
472:
473: try {
474: left.accept(this );
475: out.write(" " + type + " ");
476: right.accept(this );
477: } catch (java.io.IOException ioe) {
478: throw new RuntimeException(IO_ERROR, ioe);
479: }
480: }
481:
482: /**
483: * Writes the SQL for the Null Filter.
484: *
485: * @param filter the null filter to be written to SQL.
486: *
487: * @throws RuntimeException for io exception with writer
488: */
489: public void visit(NullFilter filter) throws RuntimeException {
490: LOGGER.finer("exporting NullFilter");
491:
492: DefaultExpression expr = (DefaultExpression) filter
493: .getNullCheckValue();
494:
495: //String type = (String) comparisions.get(new Integer(
496: // filter.getFilterType()));
497: try {
498: expr.accept(this );
499: out.write(" IS NULL ");
500: } catch (java.io.IOException ioe) {
501: throw new RuntimeException(IO_ERROR, ioe);
502: }
503: }
504:
505: /**
506: * Encodes an FidFilter.
507: *
508: * @param filter
509: *
510: * @throws RuntimeException DOCUMENT ME!
511: *
512: * @see org.geotools.filter.SQLEncoder#visit(org.geotools.filter.FidFilter)
513: */
514: public void visit(FidFilter filter) {
515: if (mapper == null) {
516: throw new RuntimeException(
517: "Must set a fid mapper before trying to encode FIDFilters");
518: }
519:
520: String[] fids = filter.getFids();
521: LOGGER.finer("Exporting FID=" + Arrays.asList(fids));
522:
523: // prepare column name array
524: String[] colNames = new String[mapper.getColumnCount()];
525:
526: for (int i = 0; i < colNames.length; i++) {
527: colNames[i] = mapper.getColumnName(i);
528: }
529:
530: for (int i = 0; i < fids.length; i++) {
531: try {
532: Object[] attValues = mapper.getPKAttributes(fids[i]);
533:
534: out.write("(");
535:
536: for (int j = 0; j < attValues.length; j++) {
537: out.write(escapeName(colNames[j]));
538: out.write(" = '");
539: out.write(attValues[j].toString()); //DJB: changed this to attValues[j] from attValues[i].
540: out.write("'");
541:
542: if (j < (attValues.length - 1)) {
543: out.write(" AND ");
544: }
545: }
546:
547: out.write(")");
548:
549: if (i < (fids.length - 1)) {
550: out.write(" OR ");
551: }
552: } catch (java.io.IOException e) {
553: throw new RuntimeException(IO_ERROR, e);
554: }
555: }
556: }
557:
558: /**
559: * Writes the SQL for the attribute Expression.
560: *
561: * @param expression the attribute to turn to SQL.
562: *
563: * @throws RuntimeException for io exception with writer
564: */
565: public void visit(AttributeExpression expression)
566: throws RuntimeException {
567: LOGGER.finer("exporting ExpressionAttribute");
568:
569: try {
570: //JD: evaluate the expression agains the feature type to get at the attribute, then
571: // encode the namee
572: context = null;
573: if (featureType != null) {
574: AttributeType attributeType = (AttributeType) expression
575: .evaluate(featureType);
576: if (attributeType != null) {
577: out.write(escapeName(attributeType.getName()));
578:
579: //provide context for a literal being compared to this attribute
580: context = attributeType.getType();
581: return;
582: }
583: }
584:
585: //if thigns are sane, we should get here
586: out.write(escapeName(expression.getAttributePath()));
587:
588: } catch (java.io.IOException ioe) {
589: throw new RuntimeException(
590: "IO problems writing attribute exp", ioe);
591: }
592: }
593:
594: /**
595: * Writes the SQL for the attribute Expression.
596: *
597: * @param expression the attribute to turn to SQL.
598: */
599: public void visit(Expression expression) {
600: LOGGER.warning("exporting unknown (default) expression");
601: }
602:
603: /**
604: * Export the contents of a Literal Expresion
605: *
606: * @param expression the Literal to export
607: *
608: * @throws RuntimeException for io exception with writer
609: */
610: public void visit(LiteralExpression expression)
611: throws RuntimeException {
612: LOGGER.finer("exporting LiteralExpression");
613:
614: //type to convert the literal to
615: Class target = null;
616:
617: if (context != null) {
618: //first try to evaluate the expression in the context of a type
619: target = (Class) context;
620: }
621:
622: if (target == null) {
623: //next try and use the filter code
624: short type = expression.getType();
625:
626: switch (type) {
627: case Expression.LITERAL_DOUBLE:
628: target = Double.class;
629: break;
630: case Expression.LITERAL_INTEGER:
631: target = Integer.class;
632: break;
633: case Expression.LITERAL_LONG:
634: target = Long.class;
635: break;
636: case Expression.LITERAL_STRING:
637: target = String.class;
638: break;
639: case Expression.LITERAL_GEOMETRY:
640: target = Geometry.class;
641: break;
642: default:
643: throw new RuntimeException("type: " + type
644: + "not supported");
645: }
646: }
647:
648: try {
649: if (target == Geometry.class
650: && expression.getLiteral() instanceof Geometry) {
651: //call this method for backwards compatability with subclasses
652: visitLiteralGeometry(expression);
653: return;
654: } else {
655: //convert the literal to the required type
656: //JD except for numerics, let the database do teh converstion
657: Object literal = null;
658: if (Number.class.isAssignableFrom(target)) {
659: //dont convert
660: } else {
661: //convert
662: literal = expression.evaluate(null, target);
663: }
664:
665: if (literal == null) {
666: //just use string
667: literal = expression.getLiteral().toString();
668: }
669:
670: //geometry hook
671: //if ( literal instanceof Geometry ) {
672: if (Geometry.class.isAssignableFrom(target)) {
673: visitLiteralGeometry(expression);
674: }
675: //else if ( literal instanceof Number ) {
676: else if (Number.class.isAssignableFrom(target)) {
677: out.write(literal.toString());
678: }
679: //else if ( literal instanceof String ) {
680: else if (String.class.isAssignableFrom(target)) {
681: // sigle quotes must be escaped to have a valid sql string
682: String escaped = literal.toString().replaceAll("'",
683: "''");
684: out.write("'" + escaped + "'");
685: return;
686: } else {
687: //convert back to a string
688: String encoding = (String) Converters.convert(
689: literal, String.class, null);
690: if (encoding == null) {
691: //could not convert back to string, use original l value
692: encoding = expression.getLiteral().toString();
693: }
694:
695: // sigle quotes must be escaped to have a valid sql string
696: out.write("'" + encoding.replaceAll("'", "''")
697: + "'");
698: }
699: }
700: } catch (IOException e) {
701: throw new RuntimeException("IO problems writing literal", e);
702: }
703:
704: }
705:
706: /**
707: * Subclasses must implement this method in order to encode geometry
708: * filters according to the specific database implementation
709: *
710: * @param expression
711: *
712: * @throws IOException DOCUMENT ME!
713: * @throws RuntimeException DOCUMENT ME!
714: */
715: protected void visitLiteralGeometry(LiteralExpression expression)
716: throws IOException {
717: throw new RuntimeException(
718: "Subclasses must implement this method in order to handle geometries");
719: }
720:
721: /**
722: * @see org.geotools.filter.FilterVisitor#visit(org.geotools.filter.GeometryFilter)
723: */
724: public void visit(GeometryFilter filter) {
725: throw new RuntimeException(
726: "Subclasses must implement this method in order to handle geometries");
727: }
728:
729: /**
730: * Writes the SQL for the Math Expression.
731: *
732: * @param expression the Math phrase to be written.
733: *
734: * @throws RuntimeException for io problems
735: */
736: public void visit(MathExpression expression)
737: throws RuntimeException {
738: LOGGER.finer("exporting Expression Math");
739:
740: String type = (String) expressions.get(new Integer(expression
741: .getType()));
742:
743: try {
744: ((DefaultExpression) expression.getLeftValue())
745: .accept(this );
746: out.write(" " + type + " ");
747: ((DefaultExpression) expression.getRightValue())
748: .accept(this );
749: } catch (java.io.IOException ioe) {
750: throw new RuntimeException(
751: "IO problems writing expression", ioe);
752: }
753: }
754:
755: /**
756: * Writes sql for a function expression. Not currently supported.
757: *
758: * @param expression a function expression
759: *
760: * @throws UnsupportedOperationException every time, this isn't supported.
761: */
762: public void visit(FunctionExpression expression)
763: throws UnsupportedOperationException {
764: String message = "Function expression support not yet added.";
765: throw new UnsupportedOperationException(message);
766: }
767:
768: /**
769: * Sets the SQL name escape string.
770: *
771: * <p>
772: * The value of this string is prefixed and appended to table schema names,
773: * table names and column names in an SQL statement to support mixed-case
774: * and non-English names. Without this, the DBMS may assume a mixed-case
775: * name in the query should be treated as upper-case and an SQLCODE of
776: * -204 or 206 may result if the name is not found.
777: * </p>
778: *
779: * <p>
780: * Typically this is the double-quote character, ", but may not be for all
781: * databases.
782: * </p>
783: *
784: * <p>
785: * For example, consider the following query:
786: * </p>
787: *
788: * <p>
789: * SELECT Geom FROM Spear.ArchSites May be interpreted by the database as:
790: * SELECT GEOM FROM SPEAR.ARCHSITES If the column and table names were
791: * actually created using mixed-case, the query needs to be specified as:
792: * SELECT "Geom" from "Spear"."ArchSites"
793: * </p>
794: *
795: * @param escape the character to be used to escape database names
796: */
797: public void setSqlNameEscape(String escape) {
798: sqlNameEscape = escape;
799: }
800:
801: /**
802: * Sets the escape character for the column name.
803: *
804: * @param escape The character to be used to escape database names.
805: *
806: * @deprecated Use setSqlNameEscape instead, as it is more aptly named.
807: */
808: public void setColnameEscape(String escape) {
809: sqlNameEscape = escape;
810: }
811:
812: /**
813: * Gets the column escape name.
814: *
815: * @return the string to be used to properly escape a db's name.
816: *
817: * @deprecated the escapeName method is preferred over this, it
818: * automatically returns the name properly escaped, since
819: * that's all getColnameEscape was being used for.
820: */
821: protected String getColnameEscape() {
822: return sqlNameEscape;
823: }
824:
825: /**
826: * Surrounds a name with the SQL escape character.
827: *
828: * @param name
829: *
830: * @return DOCUMENT ME!
831: */
832: public String escapeName(String name) {
833: return sqlNameEscape + name + sqlNameEscape;
834: }
835:
836: public void visit(IncludeFilter filter) {
837: try {
838: out.write("TRUE");
839: } catch (java.io.IOException ioe) {
840: throw new RuntimeException(IO_ERROR, ioe);
841: }
842:
843: }
844:
845: public void visit(ExcludeFilter filter) {
846: try {
847: out.write("FALSE");
848: } catch (java.io.IOException ioe) {
849: throw new RuntimeException(IO_ERROR, ioe);
850: }
851: }
852: }
|