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