001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/io/datastore/sql/AbstractSQLDatastore.java $
002: /*---------------- FILE HEADER ------------------------------------------
003:
004: This file is part of deegree.
005: Copyright (C) 2001-2008 by:
006: EXSE, Department of Geography, University of Bonn
007: http://www.giub.uni-bonn.de/deegree/
008: lat/lon GmbH
009: http://www.lat-lon.de
010:
011: This library is free software; you can redistribute it and/or
012: modify it under the terms of the GNU Lesser General Public
013: License as published by the Free Software Foundation; either
014: version 2.1 of the License, or (at your option) any later version.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: Contact:
026:
027: Andreas Poth
028: lat/lon GmbH
029: Aennchenstraße 19
030: 53177 Bonn
031: Germany
032: E-Mail: poth@lat-lon.de
033:
034: Prof. Dr. Klaus Greve
035: Department of Geography
036: University of Bonn
037: Meckenheimer Allee 166
038: 53115 Bonn
039: Germany
040: E-Mail: greve@giub.uni-bonn.de
041:
042: ---------------------------------------------------------------------------*/
043: package org.deegree.io.datastore.sql;
044:
045: import java.sql.Connection;
046: import java.sql.PreparedStatement;
047: import java.sql.ResultSet;
048: import java.sql.SQLException;
049: import java.sql.Statement;
050: import java.sql.Timestamp;
051: import java.util.Date;
052: import java.util.Iterator;
053: import java.util.List;
054: import java.util.Set;
055:
056: import org.deegree.datatypes.Types;
057: import org.deegree.datatypes.UnknownTypeException;
058: import org.deegree.framework.log.ILogger;
059: import org.deegree.framework.log.LoggerFactory;
060: import org.deegree.framework.util.TimeTools;
061: import org.deegree.i18n.Messages;
062: import org.deegree.io.DBConnectionPool;
063: import org.deegree.io.JDBCConnection;
064: import org.deegree.io.datastore.AnnotationDocument;
065: import org.deegree.io.datastore.Datastore;
066: import org.deegree.io.datastore.DatastoreConfiguration;
067: import org.deegree.io.datastore.DatastoreException;
068: import org.deegree.io.datastore.DatastoreTransaction;
069: import org.deegree.io.datastore.FeatureId;
070: import org.deegree.io.datastore.TransactionException;
071: import org.deegree.io.datastore.idgenerator.IdGenerationException;
072: import org.deegree.io.datastore.schema.MappedFeatureType;
073: import org.deegree.io.datastore.schema.MappedGeometryPropertyType;
074: import org.deegree.io.datastore.schema.content.SQLFunctionCall;
075: import org.deegree.io.datastore.sql.StatementBuffer.StatementArgument;
076: import org.deegree.io.datastore.sql.transaction.SQLTransaction;
077: import org.deegree.io.datastore.sql.wherebuilder.WhereBuilder;
078: import org.deegree.model.crs.CoordinateSystem;
079: import org.deegree.model.crs.UnknownCRSException;
080: import org.deegree.model.feature.FeatureCollection;
081: import org.deegree.model.feature.GMLFeatureDocument;
082: import org.deegree.model.filterencoding.Filter;
083: import org.deegree.model.spatialschema.Geometry;
084: import org.deegree.ogcbase.SortProperty;
085: import org.deegree.ogcwebservices.wfs.operation.Lock;
086: import org.deegree.ogcwebservices.wfs.operation.Query;
087:
088: /**
089: * This abstract class implements the common functionality of a {@link Datastore} that is backed by an SQL database.
090: *
091: * @see QueryHandler
092: *
093: * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
094: * @author last edited by: $Author: apoth $
095: *
096: * @version $Revision: 9342 $, $Date: 2007-12-27 04:32:57 -0800 (Thu, 27 Dec 2007) $
097: */
098: public abstract class AbstractSQLDatastore extends Datastore {
099:
100: protected static final ILogger LOG = LoggerFactory
101: .getLogger(AbstractSQLDatastore.class);
102:
103: // database specific SRS code for unspecified SRS
104: protected static final int SRS_UNDEFINED = -1;
105:
106: protected DBConnectionPool pool;
107:
108: private DatastoreTransaction activeTransaction;
109:
110: private Thread transactionHolder;
111:
112: @Override
113: public AnnotationDocument getAnnotationParser() {
114: return new SQLAnnotationDocument(this .getClass());
115: }
116:
117: @Override
118: public void configure(DatastoreConfiguration datastoreConfiguration)
119: throws DatastoreException {
120: super .configure(datastoreConfiguration);
121: this .pool = DBConnectionPool.getInstance();
122: }
123:
124: @Override
125: public void close() throws DatastoreException {
126: LOG
127: .logInfo("close() does not do nothing for AbstractSQLDatastore because no resources must be released");
128: }
129:
130: /**
131: * Overwrite this to return a database specific (spatial capable) {@link WhereBuilder} implementation.
132: *
133: * @param rootFts
134: * involved (requested) feature types
135: * @param aliases
136: * aliases for the feature types, may be null
137: * @param filter
138: * filter that restricts the matched features
139: * @param sortProperties
140: * sort criteria for the result, may be null or empty
141: * @param aliasGenerator
142: * used to generate unique table aliases
143: * @param vcProvider
144: * @return <code>WhereBuilder</code> implementation suitable for this datastore
145: * @throws DatastoreException
146: */
147: public WhereBuilder getWhereBuilder(MappedFeatureType[] rootFts,
148: String[] aliases, Filter filter,
149: SortProperty[] sortProperties,
150: TableAliasGenerator aliasGenerator,
151: VirtualContentProvider vcProvider)
152: throws DatastoreException {
153: return new WhereBuilder(rootFts, aliases, filter,
154: sortProperties, aliasGenerator, vcProvider);
155: }
156:
157: @Override
158: public FeatureCollection performQuery(Query query,
159: MappedFeatureType[] rootFts) throws DatastoreException,
160: UnknownCRSException {
161:
162: Connection conn = acquireConnection();
163: FeatureCollection result;
164: try {
165: result = performQuery(query, rootFts, conn);
166: } finally {
167: releaseConnection(conn);
168: }
169: return result;
170: }
171:
172: @Override
173: public FeatureCollection performQuery(Query query,
174: MappedFeatureType[] rootFts, DatastoreTransaction context)
175: throws DatastoreException, UnknownCRSException {
176: return performQuery(query, rootFts, ((SQLTransaction) context)
177: .getConnection());
178: }
179:
180: /**
181: * Performs a {@link Query} against the datastore.
182: * <p>
183: * Note that this method is responsible for the coordinate system tranformation of the input {@link Query} and the
184: * output {@link FeatureCollection}.
185: *
186: * @param query
187: * query to be performed
188: * @param rootFts
189: * the root feature types that are queried, more than one type means that the types are joined
190: * @param conn
191: * JDBC connection to use
192: * @return requested feature instances
193: * @throws DatastoreException
194: * @throws UnknownCRSException
195: */
196: protected FeatureCollection performQuery(Query query,
197: MappedFeatureType[] rootFts, Connection conn)
198: throws DatastoreException, UnknownCRSException {
199:
200: query = transformQuery(query);
201:
202: FeatureCollection result = null;
203: try {
204: QueryHandler queryHandler = new QueryHandler(this ,
205: new TableAliasGenerator(), conn, rootFts, query);
206: result = queryHandler.performQuery();
207: } catch (SQLException e) {
208: String msg = "SQL error while performing query: "
209: + e.getMessage();
210: LOG.logError(msg, e);
211: throw new DatastoreException(msg, e);
212: }
213:
214: // transform result to queried srs (only if necessary)
215: String targetSrs = query.getSrsName();
216: if (targetSrs != null && !this .canTransformTo(targetSrs)) {
217: result = transformResult(result, targetSrs);
218: }
219: return result;
220: }
221:
222: /**
223: * Acquires transactional access to the datastore. There's only one active transaction per datastore instance
224: * allowed at a time.
225: *
226: * @return transaction object that allows to perform transaction operations on the datastore
227: * @throws DatastoreException
228: */
229: @Override
230: public synchronized DatastoreTransaction acquireTransaction()
231: throws DatastoreException {
232:
233: while (this .activeTransaction != null) {
234: Thread holder = this .transactionHolder;
235: // check if transaction holder variable has (just) been cleared or if the other thread
236: // has been killed (avoid deadlocks)
237: if (holder == null || !holder.isAlive()) {
238: this .activeTransaction = null;
239: this .transactionHolder = null;
240: break;
241: }
242:
243: try {
244: // wait until the transaction holder wakes us, but not longer than 5000
245: // milliseconds (as the transaction holder may very rarely get killed without
246: // signalling us)
247: wait(5000);
248: } catch (InterruptedException e) {
249: // nothing to do
250: }
251: }
252:
253: this .activeTransaction = createTransaction();
254: this .transactionHolder = Thread.currentThread();
255: return this .activeTransaction;
256: }
257:
258: protected SQLTransaction createTransaction()
259: throws DatastoreException {
260: return new SQLTransaction(this , new TableAliasGenerator(),
261: acquireConnection());
262: }
263:
264: /**
265: * Returns the transaction to the datastore. This makes the transaction available to other clients again (via
266: * <code>acquireTransaction</code>). Underlying resources (such as JDBCConnections are freed).
267: * <p>
268: * The transaction should be terminated, i.e. commit() or rollback() must have been called before.
269: *
270: * @param ta
271: * the DatastoreTransaction to be returned
272: * @throws DatastoreException
273: */
274: @Override
275: public synchronized void releaseTransaction(DatastoreTransaction ta)
276: throws DatastoreException {
277: if (ta.getDatastore() != this ) {
278: String msg = Messages.getMessage("DATASTORE_TA_NOT_OWNER");
279: throw new TransactionException(msg);
280: }
281: if (ta != this .activeTransaction) {
282: String msg = Messages.getMessage("DATASTORE_TA_NOT_ACTIVE");
283: throw new TransactionException(msg);
284: }
285: releaseConnection(((SQLTransaction) ta).getConnection());
286:
287: this .activeTransaction = null;
288: this .transactionHolder = null;
289:
290: notifyAll();
291: }
292:
293: @Override
294: public Set<FeatureId> determineFidsToLock(List<Lock> requestParts)
295: throws DatastoreException {
296:
297: Set<FeatureId> lockedFids = null;
298: Connection conn = acquireConnection();
299: LockHandler handler = new LockHandler(this ,
300: new TableAliasGenerator(), conn, requestParts);
301: try {
302: lockedFids = handler.determineFidsToLock();
303: } finally {
304: releaseConnection(conn);
305: }
306: return lockedFids;
307: }
308:
309: /**
310: * Converts a database specific geometry <code>Object</code> from the <code>ResultSet</code> to a deegree
311: * <code>Geometry</code>.
312: *
313: * @param value
314: * @param targetSRS
315: * @param conn
316: * @return corresponding deegree geometry
317: * @throws SQLException
318: */
319: public abstract Geometry convertDBToDeegreeGeometry(Object value,
320: CoordinateSystem targetSRS, Connection conn)
321: throws SQLException;
322:
323: /**
324: * Converts a deegree <code>Geometry</code> to a database specific geometry <code>Object</code>.
325: *
326: * @param geometry
327: * @param nativeSRSCode
328: * @param conn
329: * @return corresponding database specific geometry object
330: * @throws DatastoreException
331: */
332: public abstract Object convertDeegreeToDBGeometry(
333: Geometry geometry, int nativeSRSCode, Connection conn)
334: throws DatastoreException;
335:
336: /**
337: * Returns the database connection requested for.
338: *
339: * @return Connection
340: * @throws DatastoreException
341: */
342: protected Connection acquireConnection() throws DatastoreException {
343: JDBCConnection jdbcConnection = ((SQLDatastoreConfiguration) this
344: .getConfiguration()).getJDBCConnection();
345: Connection conn = null;
346: try {
347: conn = pool.acquireConnection(jdbcConnection.getDriver(),
348: jdbcConnection.getURL(), jdbcConnection.getUser(),
349: jdbcConnection.getPassword());
350: } catch (Exception e) {
351: String msg = "Cannot acquire database connection: "
352: + e.getMessage();
353: LOG.logError(msg, e);
354: throw new DatastoreException(msg, e);
355: }
356: return conn;
357: }
358:
359: /**
360: * Releases the connection.
361: *
362: * @param conn
363: * connection to be released.
364: * @throws DatastoreException
365: */
366: public void releaseConnection(Connection conn)
367: throws DatastoreException {
368: LOG.logDebug("Releasing JDBCConnection.");
369: JDBCConnection jdbcConnection = ((SQLDatastoreConfiguration) this
370: .getConfiguration()).getJDBCConnection();
371: try {
372: pool.releaseConnection(conn, jdbcConnection.getDriver(),
373: jdbcConnection.getURL(), jdbcConnection.getUser(),
374: jdbcConnection.getPassword());
375: } catch (Exception e) {
376: String msg = "Cannot release database connection: "
377: + e.getMessage();
378: LOG.logError(msg, e);
379: throw new DatastoreException(msg, e);
380: }
381: }
382:
383: /**
384: * Converts the <code>StatementBuffer</code> into a <code>PreparedStatement</code>, which is initialized and
385: * ready to be performed.
386: *
387: * @param conn
388: * connection to be used to create the <code>PreparedStatement</code>
389: * @param statementBuffer
390: * @return the <code>PreparedStatment</code>, ready to be performed
391: * @throws SQLException
392: * if a JDBC related error occurs
393: * @throws DatastoreException
394: */
395: public PreparedStatement prepareStatement(Connection conn,
396: StatementBuffer statementBuffer) throws SQLException,
397: DatastoreException {
398:
399: LOG.logDebug("Preparing statement: "
400: + statementBuffer.getQueryString());
401: PreparedStatement preparedStatement = conn
402: .prepareStatement(statementBuffer.getQueryString());
403:
404: Iterator it = statementBuffer.getArgumentsIterator();
405: int i = 1;
406: while (it.hasNext()) {
407: StatementArgument argument = (StatementArgument) it.next();
408: int targetSqlType = argument.getTypeCode();
409: Object obj = argument.getArgument();
410: Object sqlObject = obj != null ? convertToDBType(obj,
411: targetSqlType) : null;
412:
413: if (LOG.getLevel() == ILogger.LOG_DEBUG) {
414: try {
415: String typeName = Types
416: .getTypeNameForSQLTypeCode(targetSqlType);
417: LOG.logDebug("Setting argument "
418: + i
419: + ": type="
420: + typeName
421: + ", value class="
422: + (sqlObject == null ? "none (null object)"
423: : sqlObject.getClass()));
424: if (sqlObject instanceof String
425: || sqlObject instanceof Number) {
426: LOG.logDebug("Value: '" + sqlObject + "'");
427: }
428: } catch (UnknownTypeException e) {
429: LOG.logError(e.getMessage(), e);
430: throw new SQLException(e.getMessage());
431: }
432: }
433: if (sqlObject == null) {
434: preparedStatement.setNull(i, targetSqlType);
435: } else {
436: preparedStatement
437: .setObject(i, sqlObject, targetSqlType);
438: }
439: i++;
440: }
441: return preparedStatement;
442: }
443:
444: /**
445: * Converts the given object into an object that is suitable for a table column of the specified SQL type.
446: * <p>
447: * The return value is used in a java.sql.PreparedStatement#setObject() call.
448: * <p>
449: * Please note that this implementation is subject to change. There are missing type cases, and it is preferable to
450: * use the original string representation of the input object (except for geometries).
451: * <p>
452: * NOTE: Almost identical functionality exists in {@link GMLFeatureDocument}. This is subject to change.
453: *
454: * @see java.sql.PreparedStatement#setObject(int, Object, int)
455: * @param o
456: * @param sqlTypeCode
457: * @return an object that is suitable for a table column of the specified SQL type
458: * @throws DatastoreException
459: */
460: private Object convertToDBType(Object o, int sqlTypeCode)
461: throws DatastoreException {
462:
463: Object sqlType = null;
464:
465: switch (sqlTypeCode) {
466: case Types.VARCHAR: {
467: sqlType = o.toString();
468: break;
469: }
470: case Types.INTEGER:
471: case Types.SMALLINT: {
472: try {
473: sqlType = new Integer(o.toString().trim());
474: } catch (NumberFormatException e) {
475: throw new DatastoreException("'" + o
476: + "' does not denote a valid Integer value.");
477: }
478: break;
479: }
480: case Types.NUMERIC:
481: case Types.REAL:
482: case Types.DOUBLE: {
483: try {
484: sqlType = new Double(o.toString());
485: } catch (NumberFormatException e) {
486: throw new DatastoreException("'" + o
487: + "' does not denote a valid Double value.");
488: }
489: break;
490: }
491: case Types.DECIMAL:
492: case Types.FLOAT: {
493: try {
494: sqlType = new Float(o.toString());
495: } catch (NumberFormatException e) {
496: throw new DatastoreException("'" + o
497: + "' does not denote a valid Double value.");
498: }
499: break;
500: }
501: case Types.BOOLEAN: {
502: sqlType = new Boolean(o.toString());
503: break;
504: }
505: case Types.DATE: {
506: if (o instanceof Date) {
507: sqlType = new java.sql.Date(((Date) o).getTime());
508: } else {
509: String s = o.toString();
510: int idx = s.indexOf(" "); // Datestring like "2005-04-21 00:00:00"
511: if (-1 != idx)
512: s = s.substring(0, idx);
513: sqlType = new java.sql.Date(TimeTools.createCalendar(s)
514: .getTimeInMillis());
515: }
516: break;
517: }
518: case Types.TIME: {
519: if (o instanceof Date) {
520: sqlType = new java.sql.Time(((Date) o).getTime());
521: } else {
522: sqlType = new java.sql.Time(TimeTools.createCalendar(
523: o.toString()).getTimeInMillis());
524: }
525: break;
526: }
527: case Types.TIMESTAMP: {
528: if (o instanceof Date) {
529: sqlType = new Timestamp(((Date) o).getTime());
530: } else {
531: sqlType = new java.sql.Timestamp(TimeTools
532: .createCalendar(o.toString()).getTimeInMillis());
533: }
534: break;
535: }
536: default: {
537: if (LOG.getLevel() == ILogger.LOG_DEBUG) {
538: String sqlTypeName = "" + sqlTypeCode;
539: try {
540: sqlTypeName = Types
541: .getTypeNameForSQLTypeCode(sqlTypeCode);
542: } catch (UnknownTypeException e) {
543: LOG.logError(e.getMessage(), e);
544: }
545: LOG.logDebug("No type conversion for sql type '"
546: + sqlTypeName
547: + "' defined. Passing argument of type '"
548: + o.getClass().getName() + "'.");
549: }
550: sqlType = o;
551: }
552: }
553: return sqlType;
554: }
555:
556: /**
557: * Converts the given object from a <code>java.sql.ResultSet</code> column to the common type to be used as a
558: * feature property.
559: *
560: * @param rsObject
561: * @param sqlTypeCode
562: * @return an object that is suitable for a table column of the specified SQL type
563: * @throws DatastoreException
564: */
565: @SuppressWarnings("unused")
566: public Object convertFromDBType(Object rsObject, int sqlTypeCode)
567: throws DatastoreException {
568: return rsObject;
569: }
570:
571: /**
572: * Overwrite this to enable the datastore to fetch the next value of a SQL sequence.
573: *
574: * @param conn
575: * JDBC connection to be used.
576: * @param sequence
577: * name of the SQL sequence.
578: * @return next value of the given SQL sequence
579: * @throws DatastoreException
580: * if the value could not be retrieved
581: */
582: public Object getSequenceNextVal(@SuppressWarnings("unused")
583: Connection conn, @SuppressWarnings("unused")
584: String sequence) throws DatastoreException {
585: String msg = Messages.getMessage("DATASTORE_SEQ_NOT_SUPPORTED",
586: this .getClass().getName());
587: throw new DatastoreException(msg);
588: }
589:
590: /**
591: * Returns the maximum (integer) value stored in a certain table column.
592: *
593: * @param conn
594: * JDBC connection to be used
595: * @param tableName
596: * name of the table
597: * @param columnName
598: * name of the column
599: * @return the maximum value
600: * @throws IdGenerationException
601: * if the value could not be retrieved
602: */
603: public int getMaxValue(Connection conn, String tableName,
604: String columnName) throws IdGenerationException {
605:
606: int max = 0;
607: Statement stmt = null;
608: ResultSet rs = null;
609:
610: LOG.logDebug("Retrieving max value in " + tableName + "."
611: + columnName + "...");
612:
613: try {
614: try {
615: stmt = conn.createStatement();
616: rs = stmt.executeQuery("SELECT MAX(" + columnName
617: + ") FROM " + tableName);
618: if (rs.next()) {
619: Object columnMax = rs.getObject(1);
620: if (columnMax != null) {
621: if (columnMax instanceof Integer) {
622: max = ((Integer) columnMax).intValue();
623: } else {
624: max = Integer
625: .parseInt(columnMax.toString());
626: }
627: }
628: }
629: } finally {
630: try {
631: if (rs != null) {
632: rs.close();
633: }
634: } finally {
635: if (stmt != null) {
636: stmt.close();
637: }
638: }
639: }
640: } catch (SQLException e) {
641: String msg = "Could not retrieve max value for table column '"
642: + tableName
643: + "."
644: + columnName
645: + "': "
646: + e.getMessage();
647: LOG.logError(msg, e);
648: throw new IdGenerationException(msg, e);
649: } catch (NumberFormatException e) {
650: String msg = "Could not convert selected value to integer: "
651: + e.getMessage();
652: LOG.logError(msg, e);
653: throw new IdGenerationException(msg, e);
654: }
655: LOG.logDebug("max value: " + max);
656: return max;
657: }
658:
659: /**
660: * Returns an {@link SQLFunctionCall} that refers to the given {@link MappedGeometryPropertyType} in the specified
661: * target SRS using a database specific SQL function.
662: *
663: * @param geoProperty
664: * geometry property
665: * @param targetSRS
666: * target spatial reference system (usually "EPSG:XYZ")
667: * @return an {@link SQLFunctionCall} that refers to the geometry in the specified srs
668: * @throws DatastoreException
669: */
670: public SQLFunctionCall buildSRSTransformCall(
671: @SuppressWarnings("unused")
672: MappedGeometryPropertyType geoProperty,
673: @SuppressWarnings("unused")
674: String targetSRS) throws DatastoreException {
675: String msg = Messages.getMessage(
676: "DATASTORE_SQL_NATIVE_CT_UNSUPPORTED", this .getClass()
677: .getName());
678: throw new DatastoreException(msg);
679: }
680:
681: /**
682: * Builds an SQL fragment that converts the given geometry to the specified SRS.
683: *
684: * @param geomIdentifier
685: * @param nativeSRSCode
686: * @return an SQL fragment that converts the given geometry to the specified SRS
687: * @throws DatastoreException
688: */
689: public String buildSRSTransformCall(@SuppressWarnings("unused")
690: String geomIdentifier, @SuppressWarnings("unused")
691: int nativeSRSCode) throws DatastoreException {
692: String msg = Messages.getMessage(
693: "DATASTORE_SQL_NATIVE_CT_UNSUPPORTED", this .getClass()
694: .getName());
695: throw new DatastoreException(msg);
696: }
697:
698: /**
699: * Returns the database specific code for the given SRS name.
700: *
701: * @param srsName
702: * spatial reference system name (usually "EPSG:XYZ")
703: * @return the database specific SRS code, or -1 if no corresponding native code is known
704: * @throws DatastoreException
705: */
706: public int getNativeSRSCode(@SuppressWarnings("unused")
707: String srsName) throws DatastoreException {
708: String msg = Messages.getMessage(
709: "DATASTORE_SQL_NATIVE_CT_UNSUPPORTED", this .getClass()
710: .getName());
711: throw new DatastoreException(msg);
712: }
713:
714: /**
715: * Checks whether the (native) coordinate transformation of the specified geometry property to the given SRS is
716: * possible (and necessary), i.e.
717: * <ul>
718: * <li>the internal srs of the property is specified (and not -1)
719: * <li>or the requested SRS is null or equal to the property's srs
720: * </ul>
721: * If this is not the case, a {@link DatastoreException} is thrown to indicate the problem.
722: *
723: * @param pt
724: * @param queriedSrs
725: * @return the srs to transform to, or null, if transformation is unnecessary
726: * @throws DatastoreException
727: */
728: String checkTransformation(MappedGeometryPropertyType pt,
729: String queriedSrs) throws DatastoreException {
730:
731: String targetSrs = null;
732: int internalSrs = pt.getMappingField().getSRS();
733: String propertySrs = pt.getCS().getName();
734:
735: if (queriedSrs != null && !propertySrs.equals(queriedSrs)) {
736: if (internalSrs == SRS_UNDEFINED) {
737: String msg = Messages.getMessage(
738: "DATASTORE_SRS_NOT_SPECIFIED", pt.getName(),
739: queriedSrs, propertySrs);
740: throw new DatastoreException(msg);
741: }
742: targetSrs = queriedSrs;
743: }
744: return targetSrs;
745: }
746: }
|