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.data.postgis;
017:
018: import org.geotools.data.jdbc.DefaultSQLBuilder;
019: import org.geotools.data.jdbc.JDBCDataStoreConfig;
020: import org.geotools.data.jdbc.fidmapper.FIDMapper;
021: import org.geotools.feature.AttributeType;
022: import org.geotools.feature.FeatureType;
023: import org.geotools.feature.GeometryAttributeType;
024: import org.geotools.filter.Filter;
025: import org.geotools.filter.SQLEncoder;
026: import org.geotools.filter.SQLEncoderException;
027: import org.geotools.filter.SQLEncoderPostgis;
028: import org.opengis.referencing.crs.CoordinateReferenceSystem;
029:
030: /**
031: * Builds sql for postgis.
032: *
033: * @author Chris Holmes
034: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/plugin/postgis/src/main/java/org/geotools/data/postgis/PostgisSQLBuilder.java $
035: */
036: public class PostgisSQLBuilder extends DefaultSQLBuilder {
037:
038: /** If true, WKB format is used instead of WKT */
039: protected boolean WKBEnabled = false;
040:
041: /** If true, ByteA function is used to transfer WKB data*/
042: protected boolean byteaEnabled = false;
043:
044: /** If true, tables are qualified with a schema **/
045: protected boolean schemaEnabled = true;
046:
047: /** The DataStore we are building SQL for **/
048: protected JDBCDataStoreConfig config;
049:
050: /**
051: * Strategy used to encode column information into SQL.
052: * <p>
053: * Different stratagies are defined according to:
054: * <ul>
055: * <li>how they handle CoordinateReferenceSystem information with 3D data
056: * <li>if the geometry information is requested using WKB
057: * <li>if "Byte A" needs to be enabled
058: * </ul>
059: */
060: private class ColumnStratagy {
061: /**
062: * The base Column Stratagy does nothing special.
063: *
064: * @param sql
065: * @param attribute
066: */
067: public StringBuffer sqlColumn(StringBuffer sql,
068: AttributeType attribute) {
069: sql.append("\"");
070: sql.append(attribute.getLocalName());
071: sql.append("\"");
072: return sql;
073: }
074: }
075:
076: /** Used when "plain" WKB is enabled */
077: private class GeometryStratagy extends ColumnStratagy {
078: public StringBuffer sqlColumn(StringBuffer sql,
079: AttributeType attribute) {
080: GeometryAttributeType geometryType = (GeometryAttributeType) attribute;
081: CoordinateReferenceSystem crs = geometryType
082: .getCoordinateSystem();
083: final int D = crs == null ? 2 : crs.getCoordinateSystem()
084: .getDimension();
085: if (D == 3 && !isForce2D()) {
086: sql.append("asEWKT(");
087: } else {
088: sql.append("asText(");
089: }
090: sqlGeometry(sql, geometryType);
091: sql.append(")");
092: return sql;
093: }
094:
095: /**
096: * Request the Geometry (with the correct number of dimensions based
097: * on CoordinateReferenceSystem).
098: * <p>
099: * We may need to shut this functionality off (and force2d all the time
100: * when rendering a 2D map - why request Z values if we are going to
101: * throw them away?).
102: *
103: * @param sql
104: * @param attribute
105: * @param geometryType
106: */
107: protected void sqlGeometry(StringBuffer sql,
108: GeometryAttributeType geometryType) {
109: CoordinateReferenceSystem crs = geometryType
110: .getCoordinateSystem();
111: final int D = crs == null ? 2 : crs.getCoordinateSystem()
112: .getDimension();
113:
114: String geomName = geometryType.getLocalName();
115:
116: if (D == 3 && !isForce2D()) {
117: sql.append("force_3d(\"" + geomName + "\")");
118: } else {
119: sql.append("force_2d(\"" + geomName + "\")");
120: }
121: }
122: }
123:
124: /** Used when "plain" WKB is enabled */
125: private class WKBGeometryStratagy extends GeometryStratagy {
126: public StringBuffer sqlColumn(StringBuffer sql,
127: AttributeType attribute) {
128: GeometryAttributeType geometryType = (GeometryAttributeType) attribute;
129: CoordinateReferenceSystem crs = geometryType
130: .getCoordinateSystem();
131: final int D = crs == null ? 2 : crs.getCoordinateSystem()
132: .getDimension();
133:
134: if (D == 3 && !isForce2D()) {
135: sql.append("asEWKB(");
136: } else {
137: sql.append("AsBinary(");
138: }
139: sqlGeometry(sql, geometryType);
140: sql.append(",'XDR')");
141: return sql;
142: }
143: }
144:
145: /** Used when "ByteA" WKB is enabled */
146: private class ByteaWKBGeometryStratagy extends GeometryStratagy {
147: public StringBuffer sqlColumn(StringBuffer sql,
148: AttributeType attribute) {
149: GeometryAttributeType geometryType = (GeometryAttributeType) attribute;
150: CoordinateReferenceSystem crs = geometryType
151: .getCoordinateSystem();
152: final int D = crs == null ? 2 : crs.getCoordinateSystem()
153: .getDimension();
154:
155: sql.append("encode(");
156: if (D == 3) {
157: sql.append("asEWKB(");
158: } else {
159: sql.append("asBinary(");
160: }
161: sqlGeometry(sql, geometryType);
162: sql.append(",'XDR'),'base64')");
163: return sql;
164: }
165: }
166:
167: public ColumnStratagy getStratagy(AttributeType attribute) {
168: if (attribute instanceof GeometryAttributeType) {
169: if (WKBEnabled) {
170: if (byteaEnabled) {
171: return new ByteaWKBGeometryStratagy();
172: } else {
173: return new WKBGeometryStratagy();
174: }
175: } else {
176: return new GeometryStratagy();
177: }
178: } else {
179: return new ColumnStratagy();
180: }
181: }
182:
183: /**
184: *
185: */
186: public PostgisSQLBuilder(int srid, JDBCDataStoreConfig config) {
187: this ((SQLEncoder) new SQLEncoderPostgis(srid), config);
188: }
189:
190: /**
191: * Constructor with encoder. Use PostgisSQLBuilder(encoder, config, ft) if possible.
192: *
193: * @param encoder
194: */
195: public PostgisSQLBuilder(SQLEncoder encoder,
196: JDBCDataStoreConfig config) {
197: super (encoder);
198: this .config = config;
199: }
200:
201: public PostgisSQLBuilder(SQLEncoder encoder,
202: JDBCDataStoreConfig config, FeatureType ft) {
203: super (encoder);
204: this .config = config;
205: this .ft = ft;
206: encoder.setFeatureType(ft);
207: }
208:
209: /**
210: * Produces the select information required.
211: *
212: * <p>
213: * The featureType, if known, is always requested.
214: * </p>
215: *
216: * <p>
217: * sql: <code>featureID (,attributeColumn)</code>
218: * </p>
219: *
220: * <p>
221: * We may need to provide AttributeReaders with a hook so they can request
222: * a wrapper function.
223: * </p>
224: *
225: * @param sql
226: * @param mapper
227: * @param attributes
228: */
229: public void sqlColumns(StringBuffer sql, FIDMapper mapper,
230: AttributeType[] attributes) {
231:
232: for (int i = 0; i < mapper.getColumnCount(); i++) {
233: sql.append("\"" + mapper.getColumnName(i) + "\"");
234: //DJB: add quotes in. NOTE: if FID mapper isnt oid (ie. PK - Primary Key),
235: // you could be requesting PK columns multiple times
236: if ((attributes.length > 0)
237: || (i < (mapper.getColumnCount() - 1))) {
238: sql.append(", ");
239: }
240: }
241:
242: for (int i = 0; i < attributes.length; i++) {
243: AttributeType attributeType = attributes[i];
244:
245: ColumnStratagy stratagy = getStratagy(attributeType);
246: stratagy.sqlColumn(sql, attributeType);
247:
248: if (i < (attributes.length - 1)) {
249: sql.append(", ");
250: }
251: }
252: System.out.println(sql);
253: }
254:
255: /**
256: * Consutrcts FROM clause for featureType
257: *
258: * <p>
259: * sql: <code>FROM typeName</code>
260: * </p>
261: *
262: * @param sql
263: * @param typeName
264: */
265: public void sqlFrom(StringBuffer sql, String typeName) {
266: sql.append(" FROM ");
267: sql.append(encodeTableName(typeName));
268: }
269:
270: /**
271: * Constructs WHERE clause, if needed, for FILTER.
272: *
273: * <p>
274: * sql: <code>WHERE filter encoding</code>
275: * </p>
276: *
277: * @param sql DOCUMENT ME!
278: * @param preFilter DOCUMENT ME!
279: *
280: * @throws SQLEncoderException DOCUMENT ME!
281: */
282: public void sqlWhere(StringBuffer sql, Filter preFilter)
283: throws SQLEncoderException {
284: if ((preFilter != null)
285: || (preFilter == org.geotools.filter.Filter.NONE)) {
286: String where = encoder.encode(preFilter);
287: sql.append(" ");
288: sql.append(where);
289: }
290: }
291:
292: /**
293: * Returns true if the WKB format is used to transfer geometries, false
294: * otherwise
295: *
296: */
297: public boolean isWKBEnabled() {
298: return WKBEnabled;
299: }
300:
301: /**
302: * If turned on, WKB will be used to transfer geometry data instead of WKT
303: *
304: * @param enabled
305: */
306: public void setWKBEnabled(boolean enabled) {
307: WKBEnabled = enabled;
308: }
309:
310: /**
311: * Enables the use of the bytea function to transfer faster WKB geometries
312: */
313: public boolean isByteaEnabled() {
314: return byteaEnabled;
315: }
316:
317: /**
318: * Enables/disables the use of the bytea function
319: * @param byteaEnable
320: */
321: public void setByteaEnabled(boolean byteaEnable) {
322: byteaEnabled = byteaEnable;
323: }
324:
325: /**
326: * Enables/disables schema name qualification.
327: */
328: public void setSchemaEnabled(boolean schemaEnabled) {
329: this .schemaEnabled = schemaEnabled;
330: }
331:
332: /**
333: * @return true if table names are prefixed with the containing schema.
334: */
335: public boolean isSchemaEnabled() {
336: return schemaEnabled;
337: }
338:
339: /**
340: * Encode the table name (if schemaEnabled is true the schema name
341: * will be prepended).
342: *
343: * @param tableName
344: * @return "tableName" or "schema"."tableName"
345: */
346: public String encodeTableName(String tableName) {
347: return schemaEnabled ? "\"" + config.getDatabaseSchemaName()
348: + "\".\"" + tableName + "\"" : "\"" + tableName + "\"";
349: }
350:
351: /**
352: * Surround columnName with quotes.
353: * @param columnName
354: * @return "columnName"
355: */
356: public String encodeColumnName(String columnName) {
357: return "\"" + columnName + "\"";
358: }
359:
360: }
|