0001: package org.geotools.data.jdbc;
0002:
0003: import java.io.IOException;
0004: import java.util.ArrayList;
0005: import java.util.Iterator;
0006: import java.util.List;
0007: import java.util.logging.Level;
0008:
0009: import org.geotools.factory.CommonFactoryFinder;
0010: import org.geotools.feature.AttributeType;
0011: import org.geotools.feature.FeatureType;
0012: import org.geotools.filter.LikeFilterImpl;
0013: import org.geotools.geometry.jts.JTS;
0014: import org.geotools.referencing.CRS;
0015: import org.geotools.util.Converters;
0016: import org.opengis.filter.And;
0017: import org.opengis.filter.BinaryComparisonOperator;
0018: import org.opengis.filter.BinaryLogicOperator;
0019: import org.opengis.filter.ExcludeFilter;
0020: import org.opengis.filter.Filter;
0021: import org.opengis.filter.FilterFactory;
0022: import org.opengis.filter.FilterVisitor;
0023: import org.opengis.filter.Id;
0024: import org.opengis.filter.IncludeFilter;
0025: import org.opengis.filter.Not;
0026: import org.opengis.filter.Or;
0027: import org.opengis.filter.PropertyIsBetween;
0028: import org.opengis.filter.PropertyIsEqualTo;
0029: import org.opengis.filter.PropertyIsGreaterThan;
0030: import org.opengis.filter.PropertyIsGreaterThanOrEqualTo;
0031: import org.opengis.filter.PropertyIsLessThan;
0032: import org.opengis.filter.PropertyIsLessThanOrEqualTo;
0033: import org.opengis.filter.PropertyIsLike;
0034: import org.opengis.filter.PropertyIsNotEqualTo;
0035: import org.opengis.filter.PropertyIsNull;
0036: import org.opengis.filter.expression.Add;
0037: import org.opengis.filter.expression.BinaryExpression;
0038: import org.opengis.filter.expression.Divide;
0039: import org.opengis.filter.expression.ExpressionVisitor;
0040: import org.opengis.filter.expression.Function;
0041: import org.opengis.filter.expression.Literal;
0042: import org.opengis.filter.expression.Multiply;
0043: import org.opengis.filter.expression.NilExpression;
0044: import org.opengis.filter.expression.PropertyName;
0045: import org.opengis.filter.expression.Subtract;
0046: import org.opengis.filter.spatial.BBOX;
0047: import org.opengis.filter.spatial.Beyond;
0048: import org.opengis.filter.spatial.BinarySpatialOperator;
0049: import org.opengis.filter.spatial.Contains;
0050: import org.opengis.filter.spatial.Crosses;
0051: import org.opengis.filter.spatial.DWithin;
0052: import org.opengis.filter.spatial.Disjoint;
0053: import org.opengis.filter.spatial.DistanceBufferOperator;
0054: import org.opengis.filter.spatial.Equals;
0055: import org.opengis.filter.spatial.Intersects;
0056: import org.opengis.filter.spatial.Overlaps;
0057: import org.opengis.filter.spatial.Touches;
0058: import org.opengis.filter.spatial.Within;
0059: import org.opengis.metadata.Identifier;
0060: import org.opengis.referencing.crs.CoordinateReferenceSystem;
0061:
0062: import com.vividsolutions.jts.geom.Envelope;
0063: import com.vividsolutions.jts.geom.Geometry;
0064: import com.vividsolutions.jts.io.WKTWriter;
0065:
0066: /**
0067: * Builds raw sql statements.
0068: * <p>
0069: * This class should be subclasses to accomodate different dialects of sql.
0070: * </p>
0071: * <p>
0072: * This class maintains state and is <b>not thread safe</b>.
0073: * </p>
0074: * @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
0075: *
0076: */
0077: public class SQLBuilder implements ExpressionVisitor, FilterVisitor {
0078: /**
0079: * The data store
0080: */
0081: JDBCDataStore dataStore;
0082:
0083: /**
0084: * Filter factory used to create filters
0085: */
0086: FilterFactory filterFactory = CommonFactoryFinder
0087: .getFilterFactory(null);
0088:
0089: //internal state
0090: /**
0091: * SQL statement buffer
0092: */
0093: StringBuffer sql;
0094: /**
0095: * Feature type being worked on
0096: */
0097: FeatureType featureType;
0098: /**
0099: * Post-processing filter
0100: */
0101: Filter postFilter;
0102:
0103: public SQLBuilder(JDBCDataStore dataStore) {
0104: this .dataStore = dataStore;
0105: }
0106:
0107: public void setFilterFactory(FilterFactory filterFactory) {
0108: this .filterFactory = filterFactory;
0109: }
0110:
0111: public FilterFactory getFilterFactory() {
0112: return filterFactory;
0113: }
0114:
0115: /**
0116: * Initializes the state of the builder.
0117: */
0118: protected void init() {
0119: sql = new StringBuffer();
0120: postFilter = null;
0121: featureType = null;
0122: }
0123:
0124: /**
0125: * Initializes the state of the builder setting the feature type being
0126: * worked on.
0127: *
0128: */
0129: protected void init(FeatureType featureType) {
0130: init();
0131: this .featureType = featureType;
0132: }
0133:
0134: //
0135: // Methods for encoding full statements
0136: /**
0137: * Builds a "CREATE TABLE" statement.
0138: * <p>
0139: *
0140: * </p>
0141: *
0142: * @return A statement of the form "CREATE TABLE ...".
0143: */
0144: public String createTable(FeatureType featureType) {
0145: init(featureType);
0146:
0147: sql.append("CREATE TABLE ");
0148:
0149: //table name
0150: table(featureType.getTypeName());
0151:
0152: //table definition
0153: String[] sqlTypeNames = null;
0154:
0155: try {
0156: sqlTypeNames = JDBCUtils.sqlTypeNames(featureType,
0157: dataStore);
0158: } catch (Exception e) {
0159: throw new RuntimeException(e);
0160: }
0161:
0162: sql.append(" ( ");
0163:
0164: for (int i = 0; i < sqlTypeNames.length; i++) {
0165: AttributeType type = featureType.getAttributeType(i);
0166:
0167: //the column name
0168: name(type.getName());
0169: sql.append(" ");
0170:
0171: //sql type name
0172: sql.append(sqlTypeNames[i]);
0173:
0174: if (i < (sqlTypeNames.length - 1)) {
0175: sql.append(", ");
0176: }
0177: }
0178:
0179: sql.append(" )");
0180: return sql.toString();
0181: }
0182:
0183: /**
0184: * Builds a "DROP TABLE" statement.
0185: *
0186: * @return A statement of the form "DROP TABLE ...".
0187: */
0188: public String dropTable(FeatureType featureType) {
0189: init(featureType);
0190:
0191: sql.append("DROP TABLE ");
0192:
0193: //table name
0194: table(featureType.getTypeName());
0195:
0196: return sql.toString();
0197: }
0198:
0199: /**
0200: * Builds a query which selects the envelope or bounding box of every
0201: * feature / row in a table.
0202: * <p>
0203: * Convenience for <code>bounds(featureType,null)</code>.
0204: * </p>
0205: *
0206: * @return A statement of the form "SELECT envelope(...) FROM ..."
0207: */
0208: public String bounds(FeatureType featureType) {
0209: return bounds(featureType, null);
0210: }
0211:
0212: /**
0213: * Builds a query which selects the envelope or bounding box of a subset of
0214: * features / rows in a table.
0215: * <p>
0216: * The <tt>filter</tt> argument can be used to filter those rows / features
0217: * returned or can be <code>null</code> to return all rows.
0218: * </p>
0219: *
0220: * @return A statement of the form "SELECT envelope(...) FROM ... WHERE ..."
0221: */
0222: public String bounds(FeatureType featureType, Filter filter) {
0223: init(featureType);
0224:
0225: //select
0226: sql.append("SELECT envelope(");
0227: geometry(featureType);
0228: sql.append(")");
0229:
0230: //from
0231: from(featureType);
0232:
0233: //where
0234: if (filter != null && filter != Filter.INCLUDE) {
0235: where(filter);
0236: }
0237:
0238: return sql.toString();
0239: }
0240:
0241: /**
0242: * Builds a query which selects the bound of all rows / features in a table.
0243: * <p>
0244: * Convenience for <code>count(null)</code>
0245: * </p>
0246: * @return A statement of the form "SELECT count(*) FROM ..."
0247: */
0248: public String count(FeatureType featureType) {
0249: return count(featureType, null);
0250: }
0251:
0252: /**
0253: * Builds a query which selects the count of a subset of rows / features in
0254: * a table.
0255: * <p>
0256: * The <tt>filter</tt> argument can be used to filter those rows / features
0257: * included in the count, or <code>null</code> to include all rows.
0258: * </p>
0259: *
0260: * @return A statement of the form "SELECT count(*) FROM ... WHERE ..."
0261: */
0262: public String count(FeatureType featureType, Filter filter) {
0263: init(featureType);
0264:
0265: //select
0266: sql.append("SELECT count(*)");
0267:
0268: //from
0269: from(featureType);
0270:
0271: //where
0272: if (filter != null && filter != Filter.INCLUDE) {
0273: where(filter);
0274: }
0275:
0276: return sql.toString();
0277: }
0278:
0279: /**
0280: * Builds a statement of the form "SELECT att1,att2,...,attN FROM ... WHERE ...";
0281: * <p>
0282: * The <tt>filter</tt> parameter may be null to omit the where clause.
0283: * </p>
0284: *
0285: */
0286: public String select(FeatureType featureType, Filter filter) {
0287: init(featureType);
0288:
0289: PrimaryKey pkey;
0290: try {
0291: pkey = dataStore.getPrimaryKey(featureType);
0292: } catch (IOException e) {
0293: String msg = "Unable to obtain primary key";
0294: throw new RuntimeException(msg, e);
0295: }
0296:
0297: //select
0298: int n = pkey.columns.length;
0299: String[] propertyNames = new String[n
0300: + featureType.getAttributeTypes().length];
0301:
0302: //primary key columns
0303: for (int i = 0; i < n; i++) {
0304: propertyNames[i] = pkey.columns[i].name;
0305: }
0306: //attribute columns
0307: for (int i = n; i < propertyNames.length; i++) {
0308: propertyNames[i] = featureType.getAttributeType(i - n)
0309: .getName();
0310: }
0311: select(featureType, propertyNames);
0312:
0313: //from
0314: from(featureType);
0315:
0316: //where
0317: if (filter != null && filter != Filter.INCLUDE) {
0318: where(filter);
0319: }
0320:
0321: return sql.toString();
0322: }
0323:
0324: /**
0325: * Builds a statement of the form "DELETE FROM ... WHERE ...";
0326: * <p>
0327: * The <tt>filter</tt> parameter may be null to omit the where clause.
0328: * </p>
0329: *
0330: */
0331: public void delete(FeatureType featureType, Filter filter) {
0332: init(featureType);
0333:
0334: //delete
0335: sql.append("DELETE ");
0336:
0337: //from
0338: from(featureType);
0339:
0340: //where
0341: if (filter != null && filter != Filter.INCLUDE) {
0342: where(filter);
0343: }
0344:
0345: }
0346:
0347: // public String select(Query query) {
0348: // init();
0349: //
0350: // //select
0351: // select(query.getPropertyNames());
0352: //
0353: // //from
0354: // from();
0355: //
0356: // //where
0357: // if ((query.getFilter() != null) && (query.getFilter() != Filter.INCLUDE)) {
0358: // where(query.getFilter());
0359: // }
0360: //
0361: // //order by
0362: // if ((query.getSortBy() != null) && (query.getSortBy().length > 0)) {
0363: // sortBy(query.getSortBy());
0364: // }
0365: //
0366: // return sql.toString();
0367: // }
0368: //
0369: //
0370: // Methods for encoding partial statements
0371: //
0372: /**
0373: * Encodes the select clause of a query.
0374: * <p>
0375: * If <param>propertyNames</param> is null or empty then "*" is used.
0376: * </p>
0377: * @param propertyNames The array of properties / columns in the select.
0378: */
0379: protected void select(FeatureType featureType,
0380: String[] propertyNames) {
0381: sql.append("SELECT ");
0382:
0383: if ((propertyNames == null) || (propertyNames.length == 0)) {
0384: sql.append("*");
0385: } else {
0386: for (int i = 0; i < propertyNames.length; i++) {
0387: PropertyName propertyName = filterFactory
0388: .property(propertyNames[i]);
0389:
0390: //get the attribute type
0391: AttributeType attributeType = (AttributeType) propertyName
0392: .evaluate(featureType);
0393:
0394: if (attributeType != null) {
0395: //check for geometry, because it is encided differnently
0396: if (Geometry.class.isAssignableFrom(attributeType
0397: .getType())) {
0398:
0399: }
0400: }
0401:
0402: //encode it
0403: propertyName.accept(this , null);
0404:
0405: if (i < (propertyNames.length - 1)) {
0406: sql.append(", ");
0407: }
0408: }
0409: }
0410: }
0411:
0412: /**
0413: * Encodes "FROM <table>".
0414: *
0415: */
0416: protected void from(FeatureType featureType) {
0417: sql.append(" FROM ");
0418: table(featureType.getTypeName());
0419: }
0420:
0421: /**
0422: * Encodes the table name of a query qualifying it with the database schema
0423: * name if set.
0424: *
0425: * @param name The name of the table.
0426: *
0427: */
0428: protected void table(String name) {
0429: //qualify with schema
0430: if (dataStore.getDatabaseSchema() != null) {
0431: name(dataStore.getDatabaseSchema());
0432: sql.append(".");
0433: }
0434:
0435: //local part
0436: name(name);
0437: }
0438:
0439: /**
0440: * Encodes the WHERE clause of a query.
0441: *
0442: * @param filter The filter defining the where clause, non-null.
0443: *
0444: */
0445: protected void where(Filter filter) {
0446: sql.append(" WHERE ");
0447: filter.accept(this , null);
0448: }
0449:
0450: /**
0451: * Encodes the name of the default geometry in a statement.
0452: * <p>
0453: * </p>
0454: *
0455: */
0456: protected void geometry(FeatureType featureType) {
0457: if (featureType.getDefaultGeometry() == null) {
0458: String msg = "No geometry column to encode";
0459: throw new IllegalStateException(msg);
0460: }
0461:
0462: name(featureType.getDefaultGeometry().getName());
0463: }
0464:
0465: /**
0466: * Encodes a name to be used in an sql statement.
0467: * <p>
0468: * This implementation wraps the name in double quotes.
0469: * <p>
0470: *
0471: * @param raw The raw name.
0472: *
0473: */
0474: protected void name(String raw) {
0475: sql.append("\"" + raw + "\"");
0476: }
0477:
0478: /**
0479: * Encodes a string to be used in an sql statement.
0480: *
0481: * @param raw The raw string.
0482: * @param sql The sql statement buffer
0483: *
0484: */
0485: protected void string(String raw, StringBuffer sql) {
0486: sql.append("'" + raw + "'");
0487: }
0488:
0489: /**
0490: * Encodes a geometry with a known srid to be used in an sql statement.
0491: *
0492: * @param geometry The geometry to encode.
0493: * @param srid The spatial reference id of the geometry
0494: * @param sql HTe sql statement buffer
0495: */
0496: protected void geometry(Geometry geometry, String srid,
0497: StringBuffer sql) {
0498: String wkt = new WKTWriter().write(geometry);
0499: sql.append("GeometryFromText( '" + wkt + "', ");
0500: srid(srid, sql);
0501: sql.append(" )");
0502: }
0503:
0504: /**
0505: * Encodes a srid to be used in an sql statement
0506: *
0507: * @param srid The raw spatial reference id of the geometry
0508: * @param sql HTe sql statement buffer
0509: */
0510: protected void srid(String raw, StringBuffer sql) {
0511: if (raw == null) {
0512: sql.append("0");
0513: } else if (raw.matches("[0-9]+")) {
0514: sql.append(raw);
0515: } else if (raw.matches(".*:[0-9]+")) {
0516: raw = raw.replaceAll(".*:([0-9]+)", "$1");
0517: sql.append(raw);
0518: }
0519: }
0520:
0521: /**
0522: * Encodes <code>nulL</code> to be used in an sql statement.
0523: *
0524: * @param sql The sql statement buffer
0525: */
0526: protected void nil(StringBuffer sql) {
0527: sql.append("NULL");
0528: }
0529:
0530: /**
0531: * @return The current sql buffer.
0532: */
0533: protected StringBuffer getSQL() {
0534: return sql;
0535: }
0536:
0537: //
0538: // ExpressionVisitor methods
0539: //
0540: /**
0541: * Encodes a {@link PropertyName} in an sql statement.
0542: * <p>
0543: * If the AttributeType can be infered from {@link #featureType} then its name
0544: * is used directly.
0545: * </p>
0546: *
0547: * @return The class binding of the attribute type, or <code>null</code> if unknown.
0548: */
0549: public Object visit(PropertyName propertyName, Object data) {
0550: try {
0551: //1. evaluate against the type to get the AttributeType
0552: AttributeType attributeType = (AttributeType) propertyName
0553: .evaluate(featureType);
0554:
0555: if (attributeType != null) {
0556: //encode the name of the attribute, not the property name itself
0557: name(attributeType.getName());
0558:
0559: //return the type as the return
0560: return attributeType.getType();
0561: }
0562:
0563: //2. not in type, could it be a primary key?
0564: PrimaryKey key = dataStore.getPrimaryKey(featureType);
0565:
0566: //serach for the property in the primary key, encode it and return its type
0567: for (int i = 0; i < key.columns.length; i++) {
0568: String name = key.columns[i].name;
0569:
0570: if (propertyName.getPropertyName().equals(name)) {
0571: //encode it
0572: name(propertyName.getPropertyName());
0573:
0574: //return the type
0575: return key.columns[i].type;
0576: }
0577: }
0578:
0579: //3. give up, just encode directly
0580: name(propertyName.getPropertyName());
0581:
0582: //return nothing
0583: return null;
0584: } catch (Exception e) {
0585: throw new RuntimeException(e);
0586: }
0587: }
0588:
0589: //
0590: // Arithmetic operators
0591: //
0592: public Object visit(Add add, Object data) {
0593: return visit(add, data, "+");
0594: }
0595:
0596: public Object visit(Subtract subtract, Object data) {
0597: return visit(subtract, data, "-");
0598: }
0599:
0600: public Object visit(Multiply multiply, Object data) {
0601: return visit(multiply, data, "*");
0602: }
0603:
0604: public Object visit(Divide divide, Object data) {
0605: return visit(divide, data, "/");
0606: }
0607:
0608: protected Object visit(BinaryExpression expression, Object data,
0609: String operator) {
0610: sql.append("( ");
0611: data = expression.getExpression1().accept(this , data);
0612:
0613: sql.append(" ) " + operator + " ( ");
0614: data = expression.getExpression2().accept(this , data);
0615:
0616: sql.append(" )");
0617:
0618: return data;
0619: }
0620:
0621: public Object visit(Literal literal, Object data) {
0622: Object value = null;
0623:
0624: //was the type passed in as a hint?
0625: if (data instanceof Class) {
0626: //special case for numerics, let the database do the conversions
0627: // it needs to
0628: if (Number.class.isAssignableFrom((Class) data)) {
0629: //value = literal.evaluate( null, BigDecimal.class );
0630: value = literal.evaluate(null, Number.class);
0631: } else {
0632: //non numeric, just convert
0633: value = literal.evaluate(null, (Class) data);
0634: }
0635: } else {
0636: //just evaluate
0637: value = literal.evaluate(null);
0638: }
0639:
0640: //check for string
0641: if (value instanceof String) {
0642: string((String) value, sql);
0643:
0644: return data;
0645: }
0646:
0647: //check for geometry
0648: if (value instanceof Geometry) {
0649: //figure out srid
0650: CoordinateReferenceSystem crs = null;
0651:
0652: //passed in?
0653: if (data instanceof CoordinateReferenceSystem) {
0654: crs = (CoordinateReferenceSystem) data;
0655: } else {
0656: //check the feature type
0657: if (featureType.getDefaultGeometry() != null) {
0658: crs = featureType.getDefaultGeometry()
0659: .getCoordinateSystem();
0660: }
0661: }
0662:
0663: //convert to a postgis srid
0664: String srid = null;
0665:
0666: if (crs != null) {
0667: srid = encode(crs);
0668: }
0669:
0670: geometry((Geometry) value, srid, sql);
0671:
0672: return data;
0673: }
0674:
0675: //convert to string
0676: String encoded = (String) Converters.convert(value,
0677: String.class);
0678:
0679: if (encoded == null) {
0680: //fall back to toString()
0681: encoded = value.toString();
0682: }
0683:
0684: sql.append(encoded);
0685:
0686: return data;
0687: }
0688:
0689: public Object visit(NilExpression expression, Object extraData) {
0690: // TODO Auto-generated method stub
0691: return null;
0692: }
0693:
0694: public Object visit(Function expression, Object extraData) {
0695: // TODO Auto-generated method stub
0696: return null;
0697: }
0698:
0699: /**
0700: * Helper method to encode a crs. Can we have this on CRS?
0701: */
0702: String encode(CoordinateReferenceSystem crs) {
0703: if (crs == null) {
0704: return null;
0705: }
0706:
0707: for (Iterator i = crs.getIdentifiers().iterator(); i.hasNext();) {
0708: Identifier id = (Identifier) i.next();
0709:
0710: return id.toString();
0711: }
0712:
0713: return null;
0714: }
0715:
0716: //
0717: // FilterVisitor api
0718: //
0719: public Object visitNullFilter(Object data) {
0720: return null;
0721: }
0722:
0723: public Object visit(ExcludeFilter exclude, Object data) {
0724: sql.append("1 = 0");
0725:
0726: return data;
0727: }
0728:
0729: public Object visit(IncludeFilter include, Object data) {
0730: sql.append("1 = 1");
0731:
0732: return data;
0733: }
0734:
0735: //
0736: // logical operators
0737: //
0738: public Object visit(Not not, Object data) {
0739: sql.append("NOT ( ");
0740: data = not.getFilter().accept(this , data);
0741: sql.append(" )");
0742:
0743: return data;
0744: }
0745:
0746: public Object visit(And and, Object data) {
0747: return visit(and, data, "AND");
0748: }
0749:
0750: public Object visit(Or or, Object data) {
0751: return visit(or, data, "OR");
0752: }
0753:
0754: protected Object visit(BinaryLogicOperator logic, Object data,
0755: String operator) {
0756: boolean parentheses = logic.getChildren().size() > 1;
0757:
0758: //encode each child
0759: for (Iterator f = logic.getChildren().iterator(); f.hasNext();) {
0760: Filter filter = (Filter) f.next();
0761:
0762: if (parentheses) {
0763: sql.append("( ");
0764: }
0765:
0766: data = filter.accept(this , data);
0767:
0768: if (parentheses) {
0769: sql.append(" )");
0770: }
0771:
0772: if (f.hasNext()) {
0773: sql.append(" " + operator + " ");
0774: }
0775: }
0776:
0777: return data;
0778: }
0779:
0780: //
0781: //id filter
0782: //
0783: public Object visit(Id id, Object data) {
0784: try {
0785: //get the primary key
0786: PrimaryKey key = dataStore.getPrimaryKey(featureType);
0787:
0788: //prepare each column as a property name
0789: PropertyName[] columnNames = new PropertyName[key.columns.length];
0790:
0791: for (int i = 0; i < columnNames.length; i++) {
0792: columnNames[i] = filterFactory
0793: .property(key.columns[i].name);
0794: }
0795:
0796: //process each fid, creating an OR filter
0797: List or = new ArrayList();
0798:
0799: for (Iterator f = id.getIDs().iterator(); f.hasNext();) {
0800: String fid = (String) f.next();
0801:
0802: //map to values
0803: Object[] values = key.decode(fid);
0804:
0805: //create an "AND" filter for each property name
0806: List and = new ArrayList();
0807:
0808: for (int i = 0; i < values.length; i++) {
0809: //create an equalTo filter
0810: PropertyName name = columnNames[i];
0811: Literal literal = filterFactory.literal(values[i]);
0812: and.add(filterFactory.equals(name, literal));
0813: }
0814:
0815: //add to or
0816: or.add(filterFactory.and(and));
0817: }
0818:
0819: //encode it
0820: return filterFactory.or(or).accept(this , data);
0821: } catch (Exception e) {
0822: throw new RuntimeException(e);
0823: }
0824: }
0825:
0826: //
0827: // binary comparison operators
0828: //
0829: public Object visit(PropertyIsBetween between, Object data) {
0830: sql.append("( ");
0831:
0832: data = between.getExpression().accept(this , data);
0833: sql.append(" ) BETWEEN ( ");
0834:
0835: data = between.getLowerBoundary().accept(this , data);
0836: sql.append(" ) AND ( ");
0837:
0838: data = between.getUpperBoundary().accept(this , data);
0839: sql.append(" )");
0840:
0841: return data;
0842: }
0843:
0844: public Object visit(PropertyIsEqualTo equalTo, Object data) {
0845: return visit(equalTo, data, "=");
0846: }
0847:
0848: public Object visit(PropertyIsNotEqualTo notEqualTo, Object data) {
0849: return visit(notEqualTo, data, "!=");
0850: }
0851:
0852: public Object visit(PropertyIsGreaterThan greaterThan, Object data) {
0853: return visit(greaterThan, data, ">");
0854: }
0855:
0856: public Object visit(
0857: PropertyIsGreaterThanOrEqualTo greaterThanOrEqualTo,
0858: Object data) {
0859: return visit(greaterThanOrEqualTo, data, ">=");
0860: }
0861:
0862: public Object visit(PropertyIsLessThan lessThan, Object data) {
0863: return visit(lessThan, data, "<");
0864: }
0865:
0866: public Object visit(PropertyIsLessThanOrEqualTo lessThanOrEqualTo,
0867: Object data) {
0868: return visit(lessThanOrEqualTo, data, "<=");
0869: }
0870:
0871: protected Object visit(BinaryComparisonOperator comparison,
0872: Object data, String operator) {
0873: sql.append("( ");
0874:
0875: //left
0876: data = comparison.getExpression1().accept(this , data);
0877:
0878: //operator
0879: sql.append(" ) " + operator + " ( ");
0880:
0881: //right
0882: data = comparison.getExpression2().accept(this , data);
0883:
0884: sql.append(" )");
0885:
0886: return data;
0887: }
0888:
0889: public Object visit(PropertyIsLike like, Object data) {
0890: String pattern = LikeFilterImpl.convertToSQL92(like.getEscape()
0891: .charAt(0), like.getWildCard().charAt(0), like
0892: .getSingleChar().charAt(0), like.getLiteral());
0893:
0894: //evaluate left hande
0895: data = like.getExpression().accept(this , data);
0896:
0897: sql.append(" LIKE ");
0898:
0899: //encode pattern as string
0900: string(pattern, sql);
0901:
0902: return data;
0903: }
0904:
0905: public Object visit(PropertyIsNull isNull, Object data) {
0906: data = isNull.getExpression().accept(this , data);
0907: sql.append(" IS ");
0908: nil(sql);
0909:
0910: return data;
0911: }
0912:
0913: //
0914: // spatial operators
0915: //
0916: public Object visit(BBOX bbox, Object data) {
0917: //create a property name + geometry
0918: PropertyName name = filterFactory.property(bbox
0919: .getPropertyName());
0920: Envelope e = new Envelope(bbox.getMinX(), bbox.getMaxX(), bbox
0921: .getMinY(), bbox.getMaxY());
0922:
0923: if (bbox.getSRS() != null) {
0924: CoordinateReferenceSystem crs;
0925:
0926: try {
0927: crs = CRS.decode(bbox.getSRS());
0928: data = crs;
0929: } catch (Exception ex) {
0930: dataStore.getLogger().log(Level.WARNING,
0931: ex.getLocalizedMessage(), ex);
0932: }
0933: }
0934:
0935: return visit(name, JTS.toGeometry(e), data);
0936: }
0937:
0938: protected Object visit(PropertyName name, Geometry geometry,
0939: Object data) {
0940: //encode the property ( dont carry back data )
0941: name.accept(this , data);
0942:
0943: //bbox operator
0944: sql.append(" && ");
0945:
0946: //encode the geometry
0947: Literal literal = filterFactory.literal(geometry);
0948:
0949: return literal.accept(this , data);
0950: }
0951:
0952: public Object visit(Beyond beyond, Object data) {
0953: return visit(beyond, data, ">", false);
0954: }
0955:
0956: public Object visit(DWithin dwithin, Object data) {
0957: return visit(dwithin, data, "<=", true);
0958: }
0959:
0960: protected Object visit(DistanceBufferOperator distance,
0961: Object data, String operator, boolean index) {
0962: data = visit((BinarySpatialOperator) distance, data,
0963: "distance", index);
0964: sql.append(" " + operator + " " + distance.getDistance());
0965:
0966: return data;
0967: }
0968:
0969: public Object visit(Disjoint disjoint, Object data) {
0970: return visit(disjoint, data, "disjoint", false);
0971: }
0972:
0973: public Object visit(Contains contains, Object data) {
0974: return visit(contains, data, "contains", true);
0975: }
0976:
0977: public Object visit(Crosses crosses, Object data) {
0978: return visit(crosses, data, "crosses", true);
0979: }
0980:
0981: public Object visit(Equals equals, Object data) {
0982: return visit(equals, data, "equals", true);
0983: }
0984:
0985: public Object visit(Intersects intersects, Object data) {
0986: return visit(intersects, data, "intersects", true);
0987: }
0988:
0989: public Object visit(Overlaps overlaps, Object data) {
0990: return visit(overlaps, data, "overlaps", true);
0991: }
0992:
0993: public Object visit(Touches touches, Object data) {
0994: return visit(touches, data, "touches", true);
0995: }
0996:
0997: public Object visit(Within within, Object data) {
0998: return visit(within, data, "within", true);
0999: }
1000:
1001: protected Object visit(BinarySpatialOperator comparison,
1002: Object data, String operator, boolean index) {
1003: //use the index for this comparison?
1004: if (index) {
1005: //figure out the property name, and geometry
1006: PropertyName propertyName = null;
1007: Geometry geometry = null;
1008:
1009: if (comparison.getExpression1() instanceof PropertyName) {
1010: propertyName = (PropertyName) comparison
1011: .getExpression1();
1012: geometry = (Geometry) comparison.getExpression2()
1013: .evaluate(null, Geometry.class);
1014: } else if (comparison.getExpression2() instanceof PropertyName) {
1015: propertyName = (PropertyName) comparison
1016: .getExpression2();
1017: geometry = (Geometry) comparison.getExpression1()
1018: .evaluate(null, Geometry.class);
1019: }
1020:
1021: if ((propertyName != null) && (geometry != null)) {
1022: data = visit(propertyName, geometry, data);
1023: sql.append(" AND ");
1024: }
1025: }
1026:
1027: //operator
1028: sql.append(operator + "( ( ");
1029:
1030: //left
1031: data = comparison.getExpression1().accept(this , data);
1032:
1033: sql.append(" ) , ( ");
1034:
1035: //right
1036: data = comparison.getExpression2().accept(this , data);
1037:
1038: sql.append(" ) )");
1039:
1040: return data;
1041: }
1042:
1043: }
|