001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/io/datastore/sql/oracle/OracleDatastore.java $
002 /*----------------------------------------------------------------------------
003 This file is part of deegree, http://deegree.org/
004 Copyright (C) 2001-2009 by:
005 Department of Geography, University of Bonn
006 and
007 lat/lon GmbH
008
009 This library is free software; you can redistribute it and/or modify it under
010 the terms of the GNU Lesser General Public License as published by the Free
011 Software Foundation; either version 2.1 of the License, or (at your option)
012 any later version.
013 This library is distributed in the hope that it will be useful, but WITHOUT
014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016 details.
017 You should have received a copy of the GNU Lesser General Public License
018 along with this library; if not, write to the Free Software Foundation, Inc.,
019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020
021 Contact information:
022
023 lat/lon GmbH
024 Aennchenstr. 19, 53177 Bonn
025 Germany
026 http://lat-lon.de/
027
028 Department of Geography, University of Bonn
029 Prof. Dr. Klaus Greve
030 Postfach 1147, 53001 Bonn
031 Germany
032 http://www.geographie.uni-bonn.de/deegree/
033
034 e-mail: info@deegree.org
035 ----------------------------------------------------------------------------*/
036
037 package org.deegree.io.datastore.sql.oracle;
038
039 import java.io.IOException;
040 import java.io.InputStream;
041 import java.lang.reflect.Field;
042 import java.sql.Connection;
043 import java.sql.PreparedStatement;
044 import java.sql.ResultSet;
045 import java.sql.SQLException;
046 import java.sql.Statement;
047 import java.util.Date;
048 import java.util.HashMap;
049 import java.util.Iterator;
050 import java.util.Map;
051 import java.util.Properties;
052
053 import oracle.spatial.geometry.JGeometry;
054 import oracle.sql.CLOB;
055 import oracle.sql.STRUCT;
056 import oracle.sql.TIMESTAMP;
057
058 import org.deegree.datatypes.Types;
059 import org.deegree.datatypes.UnknownTypeException;
060 import org.deegree.framework.log.ILogger;
061 import org.deegree.framework.log.LoggerFactory;
062 import org.deegree.framework.util.FileUtils;
063 import org.deegree.framework.util.TimeTools;
064 import org.deegree.i18n.Messages;
065 import org.deegree.io.JDBCConnection;
066 import org.deegree.io.datastore.Datastore;
067 import org.deegree.io.datastore.DatastoreException;
068 import org.deegree.io.datastore.schema.MappedFeatureType;
069 import org.deegree.io.datastore.schema.MappedGeometryPropertyType;
070 import org.deegree.io.datastore.schema.TableRelation;
071 import org.deegree.io.datastore.schema.content.ConstantContent;
072 import org.deegree.io.datastore.schema.content.FieldContent;
073 import org.deegree.io.datastore.schema.content.FunctionParam;
074 import org.deegree.io.datastore.schema.content.MappingGeometryField;
075 import org.deegree.io.datastore.schema.content.SQLFunctionCall;
076 import org.deegree.io.datastore.sql.AbstractSQLDatastore;
077 import org.deegree.io.datastore.sql.SQLDatastoreConfiguration;
078 import org.deegree.io.datastore.sql.StatementBuffer;
079 import org.deegree.io.datastore.sql.TableAliasGenerator;
080 import org.deegree.io.datastore.sql.VirtualContentProvider;
081 import org.deegree.io.datastore.sql.StatementBuffer.StatementArgument;
082 import org.deegree.io.datastore.sql.wherebuilder.WhereBuilder;
083 import org.deegree.model.crs.CoordinateSystem;
084 import org.deegree.model.feature.FeatureCollection;
085 import org.deegree.model.filterencoding.Filter;
086 import org.deegree.model.spatialschema.Geometry;
087 import org.deegree.model.spatialschema.GeometryException;
088 import org.deegree.ogcbase.SortProperty;
089 import org.deegree.ogcwebservices.wfs.operation.Query;
090
091 /**
092 * {@link Datastore} implementation for Oracle Spatial database systems. Supports Oracle Spatial for Oracle 10g.
093 *
094 * TODO Which Oracle spatial versions are supported exactly?
095 *
096 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
097 * @author <a href="mailto:tfr@users.sourceforge.net">Torsten Friebe </a>
098 * @author last edited by: $Author: apoth $
099 *
100 * @version $Revision: 27361 $, $Date: 2010-10-18 20:38:36 +0200 (Mo, 18 Okt 2010) $
101 */
102 public class OracleDatastore extends AbstractSQLDatastore {
103
104 private static final ILogger LOG = LoggerFactory.getLogger( OracleDatastore.class );
105
106 private static final String SRS_CODE_PROP_FILE = "srs_codes_oracle.properties";
107
108 private static Map<String, Integer> nativeSrsCodeMap = new HashMap<String, Integer>();
109
110 // used for identifying the last active connection (if changed, Reijer's JGeometry store workaround patch is
111 // applied)
112 private static Connection lastStoreConnection;
113
114 static {
115 try {
116 initSRSCodeMap();
117 } catch ( IOException e ) {
118 String msg = "Cannot load native srs code file '" + SRS_CODE_PROP_FILE + "'.";
119 LOG.logError( msg, e );
120 }
121 }
122
123 /**
124 * Returns the database connection requested for.
125 *
126 * @return Connection
127 * @throws DatastoreException
128 */
129 protected Connection acquireConnection()
130 throws DatastoreException {
131 JDBCConnection jdbcConnection = ( (SQLDatastoreConfiguration) this.getConfiguration() ).getJDBCConnection();
132 Connection conn = null;
133 try {
134 Properties props = new Properties();
135 props.put( "user", jdbcConnection.getUser() );
136 props.put( "password", jdbcConnection.getPassword() );
137 props.put( "SetBigStringTryClob", "true" );
138 conn = pool.acquireConnection( jdbcConnection.getDriver(), jdbcConnection.getURL(), props );
139 } catch ( Exception e ) {
140 String msg = "Cannot acquire database connection: " + e.getMessage();
141 LOG.logError( msg, e );
142 throw new DatastoreException( msg, e );
143 }
144 return conn;
145 }
146
147 /**
148 * Releases the connection.
149 *
150 * @param conn
151 * connection to be released.
152 * @throws DatastoreException
153 */
154 public void releaseConnection( Connection conn )
155 throws DatastoreException {
156 LOG.logDebug( "Releasing JDBCConnection." );
157 JDBCConnection jdbcConnection = ( (SQLDatastoreConfiguration) this.getConfiguration() ).getJDBCConnection();
158 try {
159 Properties props = new Properties();
160 props.put( "user", jdbcConnection.getUser() );
161 props.put( "password", jdbcConnection.getPassword() );
162 props.put( "SetBigStringTryClob", "true" );
163 pool.releaseConnection( conn, jdbcConnection.getDriver(), jdbcConnection.getURL(), props );
164 } catch ( Exception e ) {
165 String msg = "Cannot release database connection: " + e.getMessage();
166 LOG.logError( msg, e );
167 throw new DatastoreException( msg, e );
168 }
169 }
170
171 /**
172 * @param code
173 * an EPSG code
174 * @return the oracle code as stored in srs_codes_oracle.properties
175 */
176 public static int getOracleSRIDCode( String code ) {
177 Integer res = nativeSrsCodeMap.get( code );
178 if ( res != null ) {
179 return res.intValue();
180 }
181
182 // only in Oracle 10, but what else to do?
183 return Integer.parseInt( code.split( ":" )[1] );
184 }
185
186 /**
187 * @param srid
188 * @return an EPSG code or "-1", if none was found
189 */
190 public static String fromOracleSRIDCode( int srid ) {
191 for ( String k : nativeSrsCodeMap.keySet() ) {
192 if ( nativeSrsCodeMap.get( k ).intValue() == srid ) {
193 return k;
194 }
195 }
196
197 return "-1";
198 }
199
200 /**
201 * Returns a specific {@link WhereBuilder} implementation for Oracle Spatial.
202 *
203 * @param rootFts
204 * involved (requested) feature types
205 * @param aliases
206 * aliases for the feature types, may be null
207 * @param filter
208 * filter that restricts the matched features
209 * @param sortProperties
210 * sort criteria for the result, may be null or empty
211 * @param aliasGenerator
212 * used to generate unique table aliases
213 * @param vcProvider
214 * @return <code>WhereBuilder</code> implementation for Oracle Spatial
215 * @throws DatastoreException
216 */
217 @Override
218 public WhereBuilder getWhereBuilder( MappedFeatureType[] rootFts, String[] aliases, Filter filter,
219 SortProperty[] sortProperties, TableAliasGenerator aliasGenerator,
220 VirtualContentProvider vcProvider )
221 throws DatastoreException {
222 return new OracleSpatialWhereBuilder( rootFts, aliases, filter, sortProperties, aliasGenerator, vcProvider );
223 }
224
225 /**
226 * Converts an Oracle specific geometry <code>Object</code> from the <code>ResultSet</code> to a deegree
227 * <code>Geometry</code>.
228 *
229 * @param value
230 * @param targetCS
231 * @param conn
232 * @return corresponding deegree geometry
233 * @throws SQLException
234 */
235 @Override
236 public Geometry convertDBToDeegreeGeometry( Object value, CoordinateSystem targetCS, Connection conn )
237 throws SQLException {
238 Geometry geometry = null;
239 if ( value != null ) {
240 LOG.logDebug( "Converting STRUCT to JGeometry." );
241 JGeometry jGeometry = JGeometry.load( (STRUCT) value );
242 try {
243 LOG.logDebug( "Converting JGeometry to deegree geometry ('" + targetCS + "')" );
244 geometry = JGeometryAdapter.wrap( jGeometry, targetCS );
245 } catch ( Exception e ) {
246 LOG.logError( "Error while converting STRUCT to Geometry: ", e );
247 throw new SQLException( "Error converting STRUCT to Geometry: " + e.getMessage() );
248 }
249 }
250 return geometry;
251 }
252
253 /**
254 * Converts a deegree <code>Geometry</code> to an Oracle specific geometry object.
255 *
256 * @param geometry
257 * @param nativeSRSCode
258 * @param conn
259 * @return corresponding Oracle specific geometry object
260 * @throws DatastoreException
261 */
262 @Override
263 public STRUCT convertDeegreeToDBGeometry( Geometry geometry, int nativeSRSCode, Connection conn )
264 throws DatastoreException {
265
266 JGeometry jGeometry = null;
267 LOG.logDebug( "Converting deegree geometry to JGeometry." );
268 try {
269 jGeometry = JGeometryAdapter.export( geometry, nativeSRSCode );
270 } catch ( GeometryException e ) {
271 throw new DatastoreException( "Error converting deegree geometry to JGeometry: " + e.getMessage(), e );
272 }
273
274 LOG.logDebug( "Converting JGeometry to STRUCT." );
275 STRUCT struct = null;
276 try {
277 struct = storeGeometryWithMultiConnHack( jGeometry, conn );
278 } catch ( SQLException e ) {
279 throw new DatastoreException( "Error converting JGeometry to STRUCT: " + e.getMessage(), e );
280 }
281 return struct;
282 }
283
284 /**
285 * Workaround for a known Oracle JDBC driver problem.
286 * <p>
287 * JGeometry#store() isn't working when invoked successively using different connections. This method applies a
288 * workaround (based on undocumented behaviour of the Oracle driver) to solve this problem.
289 * http://forums.oracle.com/forums/thread.jspa?messageID=1273670
290 * </p>
291 *
292 * @param geometry
293 * geometry to be stored
294 * @param connection
295 * jdbc connection
296 * @return a {@link STRUCT} to be used as query parameter
297 * @throws SQLException
298 */
299 private STRUCT storeGeometryWithMultiConnHack( JGeometry geometry, Connection connection )
300 throws SQLException {
301 synchronized ( JGeometry.class ) {
302 if ( lastStoreConnection != null && lastStoreConnection != connection ) {
303 LOG.logDebug( "JGeometry#store(...) workaround (lastStoreConnection != connection)" );
304 try {
305 Field geomDesc = JGeometry.class.getDeclaredField( "geomDesc" );
306 geomDesc.setAccessible( true );
307 geomDesc.set( null, null );
308 } catch ( Exception e ) {
309 LOG.logWarning( "Exception caught applying JGeometr#store(...) workaround: " + e.getMessage(), e );
310 }
311 }
312 lastStoreConnection = connection;
313 return JGeometry.store( geometry, connection );
314 }
315 }
316
317 /**
318 * Converts the given object from a <code>java.sql.ResultSet</code> column to the common type to be used as a
319 * feature property.
320 * <p>
321 * NOTE: String- and boolean-valued results have a special conversion handling:
322 * <ul>
323 * <li><code>Strings:</code> because we encountered difficulties when inserting empty strings "" into String-type
324 * columns with NOT NULL constraints (for example in VARCHAR2 fields), "$EMPTY_STRING$" is used to mark them.</li>
325 * <li><code>Boolean:<code>because Oracle has no special boolean type, it is assumed that a CHAR(1) column is used
326 * instead (with values 'Y'=true and 'N'=false)</li>
327 * </ul>
328 *
329 * @param rsObject
330 * @param sqlTypeCode
331 * @return an object that is suitable for a table column of the specified SQL type
332 * @throws DatastoreException
333 */
334 @Override
335 public Object convertFromDBType( Object rsObject, int sqlTypeCode )
336 throws DatastoreException {
337 Object propertyValue = rsObject;
338 try {
339 if ( rsObject instanceof TIMESTAMP ) {
340 propertyValue = ( (TIMESTAMP) rsObject ).timestampValue();
341 } else if ( rsObject instanceof String ) {
342 if ( rsObject.equals( "$EMPTY_STRING$" ) ) {
343 propertyValue = "";
344 }
345 if ( sqlTypeCode == Types.BOOLEAN ) {
346 String val = rsObject.toString();
347
348 if ( val.length() == 1 && val.charAt( 0 ) == 'Y' ) {
349 propertyValue = Boolean.TRUE;
350 }
351 if ( val.length() == 1 && val.charAt( 0 ) == 'N' ) {
352 propertyValue = Boolean.FALSE;
353 }
354 }
355 } else if ( rsObject instanceof oracle.sql.CLOB ) {
356 try {
357 propertyValue = FileUtils.readTextFile( ( (CLOB) rsObject ).getCharacterStream() ).toString();
358 } catch ( IOException e ) {
359 // TODO Auto-generated catch block
360 e.printStackTrace();
361 }
362 }
363 } catch ( SQLException e ) {
364 throw new DatastoreException( e.getMessage(), e );
365 }
366 return propertyValue;
367 }
368
369 /**
370 * Returns the next value of the given SQL sequence.
371 *
372 * @param conn
373 * JDBC connection to be used.
374 * @param sequence
375 * name of the SQL sequence
376 * @return next value of the given SQL sequence
377 * @throws DatastoreException
378 * if the value could not be retrieved
379 */
380 @Override
381 public Object getSequenceNextVal( Connection conn, String sequence )
382 throws DatastoreException {
383
384 Object nextVal = null;
385 Statement stmt = null;
386 ResultSet rs = null;
387
388 try {
389 try {
390 stmt = conn.createStatement();
391 rs = stmt.executeQuery( "SELECT " + sequence + ".nextval FROM dual" );
392 if ( rs.next() ) {
393 nextVal = rs.getObject( 1 );
394 }
395 } finally {
396 try {
397 if ( rs != null ) {
398 rs.close();
399 }
400 } finally {
401 if ( stmt != null ) {
402 stmt.close();
403 }
404 }
405 }
406 } catch ( SQLException e ) {
407 String msg = "Could not retrieve value for sequence '" + sequence + "': " + e.getMessage();
408 throw new DatastoreException( msg, e );
409 }
410 return nextVal;
411 }
412
413 /**
414 * Returns the current value (plus an offset) of the given SQL sequence.
415 *
416 * @param conn
417 * JDBC connection to be used.
418 * @param sequence
419 * name of the SQL sequence
420 * @param offset
421 * offset added to the sequence value
422 * @return current value (plus offset) of the given SQL sequence
423 * @throws DatastoreException
424 * if the value could not be retrieved
425 */
426 @Override
427 public Object getSequenceCurrValPlusOffset( Connection conn, String sequence, int offset )
428 throws DatastoreException {
429
430 Object nextVal = null;
431 Statement stmt = null;
432 ResultSet rs = null;
433
434 try {
435 try {
436 stmt = conn.createStatement();
437 rs = stmt.executeQuery( "SELECT " + sequence + ".currval + " + offset + " FROM dual" );
438 if ( rs.next() ) {
439 nextVal = rs.getObject( 1 );
440 }
441 } finally {
442 try {
443 if ( rs != null ) {
444 rs.close();
445 }
446 } finally {
447 if ( stmt != null ) {
448 stmt.close();
449 }
450 }
451 }
452 } catch ( SQLException e ) {
453 String msg = "Could not retrieve value for sequence '" + sequence + "': " + e.getMessage();
454 throw new DatastoreException( msg, e );
455 }
456 return nextVal;
457 }
458
459 /**
460 * Converts the {@link StatementBuffer} into a {@link PreparedStatement}, which is initialized and ready to be
461 * performed.
462 *
463 * TODO remove this method (use super class method instead), change handling of JGeometry NOTE: String- and
464 * boolean-valued results have a special conversion handling:
465 * <ul>
466 * <li><code>Strings:</code> because we encountered difficulties when inserting empty strings "" into String-type
467 * columns with NOT NULL constraints (for example in VARCHAR2 fields), "$EMPTY_STRING$" is used to mark them.</li>
468 * <li><code>Boolean:<code>because Oracle has no special boolean type, it is assumed that a CHAR(1) column is used
469 * instead (with values 'Y'=true and 'N'=false)</li>
470 * </ul>
471 *
472 * @param conn
473 * connection to be used to create the <code>PreparedStatement</code>
474 * @param statementBuffer
475 * @return the <code>PreparedStatment</code>, ready to be performed
476 * @throws SQLException
477 * if a JDBC related error occurs
478 */
479 @Override
480 public PreparedStatement prepareStatement( Connection conn, StatementBuffer statementBuffer )
481 throws SQLException {
482 LOG.logDebug( "Preparing statement: " + statementBuffer.getQueryString() );
483
484 PreparedStatement preparedStatement = conn.prepareStatement( statementBuffer.getQueryString() );
485
486 Iterator<StatementArgument> it = statementBuffer.getArgumentsIterator();
487 int i = 1;
488 while ( it.hasNext() ) {
489 StatementArgument argument = it.next();
490 Object parameter = argument.getArgument();
491 int targetSqlType = argument.getTypeCode();
492 if ( parameter != null ) {
493 if ( targetSqlType == Types.DATE ) {
494 if ( parameter instanceof String ) {
495 parameter = TimeTools.createCalendar( (String) parameter ).getTime();
496 }
497 parameter = new java.sql.Date( ( (Date) parameter ).getTime() );
498 } else if ( targetSqlType == Types.TIMESTAMP ) {
499 if ( parameter instanceof String ) {
500 parameter = TimeTools.createCalendar( (String) parameter ).getTime();
501 }
502 parameter = new java.sql.Timestamp( ( (Date) parameter ).getTime() );
503 } else if ( parameter != null && parameter instanceof JGeometry ) {
504 parameter = storeGeometryWithMultiConnHack( (JGeometry) parameter, conn );
505 } else if ( targetSqlType == Types.INTEGER || targetSqlType == Types.SMALLINT
506 || targetSqlType == Types.TINYINT ) {
507 parameter = Integer.parseInt( parameter.toString() );
508 } else if ( targetSqlType == Types.DECIMAL || targetSqlType == Types.DOUBLE
509 || targetSqlType == Types.REAL || targetSqlType == Types.FLOAT ) {
510 parameter = Double.parseDouble( parameter.toString() );
511 } else if ( targetSqlType == Types.NUMERIC ) {
512 try {
513 parameter = Integer.parseInt( parameter.toString() );
514 } catch ( Exception e ) {
515 parameter = Double.parseDouble( parameter.toString() );
516 }
517 } else if ( targetSqlType == Types.BOOLEAN ) {
518 // Oracle does not have a BOOLEAN datatype
519 // default maping to column of type CHAR(1)
520 // http://thinkoracle.blogspot.com/2005/07/oracle-boolean.html
521 targetSqlType = Types.CHAR;
522 if ( Boolean.parseBoolean( parameter.toString() ) ) {
523 parameter = "Y";
524 } else {
525 parameter = "N";
526 }
527 } else if ( parameter instanceof String ) {
528 // Using the empty string ("") for NOT NULL columns fails
529 // (at least using PreparedStatements)
530 // TODO implement a proper solution
531 if ( ( (String) parameter ).length() == 0 ) {
532 parameter = "$EMPTY_STRING$";
533 }
534 }
535 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
536 try {
537 String typeName = Types.getTypeNameForSQLTypeCode( targetSqlType );
538 LOG.logDebug( "Setting argument " + i + ": type=" + typeName + ", value class="
539 + parameter.getClass() );
540 if ( parameter instanceof String || parameter instanceof Number
541 || parameter instanceof java.sql.Date ) {
542 LOG.logDebug( "Value: '" + parameter + "'" );
543 }
544 } catch ( UnknownTypeException e ) {
545 throw new SQLException( e.getMessage() );
546 }
547 }
548 preparedStatement.setObject( i, parameter, targetSqlType );
549 } else {
550 setNullValue( preparedStatement, i, targetSqlType );
551 }
552 i++;
553 }
554 return preparedStatement;
555 }
556
557 /**
558 * Transforms the incoming {@link Query} so that the {@link CoordinateSystem} of all spatial arguments (BBOX, etc.)
559 * in the {@link Filter} match the SRS of the targeted {@link MappingGeometryField}s.
560 * <p>
561 * NOTE: If this transformation can be performed by the backend (e.g. by Oracle Spatial), this method should be
562 * overwritten to return the original input {@link Query}.
563 *
564 * @param query
565 * query to be transformed
566 * @return query with spatial arguments transformed to target SRS
567 */
568 @Override
569 protected Query transformQuery( Query query ) {
570 return query;
571 }
572
573 /**
574 * Transforms the {@link FeatureCollection} so that the geometries of all contained geometry properties use the
575 * requested SRS.
576 *
577 * @param fc
578 * feature collection to be transformed
579 * @param targetSRS
580 * requested SRS
581 * @return transformed FeatureCollection
582 */
583 @Override
584 protected FeatureCollection transformResult( FeatureCollection fc, String targetSRS ) {
585 return fc;
586 }
587
588 /**
589 * Returns whether the datastore is capable of performing a native coordinate transformation (using an SQL function
590 * call for example) into the given SRS.
591 *
592 * @param targetSRS
593 * target spatial reference system (usually "EPSG:XYZ")
594 * @return true, if the datastore can perform the coordinate transformation, false otherwise
595 */
596 @Override
597 protected boolean canTransformTo( String targetSRS ) {
598 return getNativeSRSCode( targetSRS ) != SRS_UNDEFINED;
599 }
600
601 /**
602 * Returns an {@link SQLFunctionCall} that refers to the given {@link MappingGeometryField} in the specified target
603 * SRS using a database specific SQL function.
604 *
605 * @param geoProperty
606 * geometry property
607 * @param targetSRS
608 * target spatial reference system (usually "EPSG:XYZ")
609 * @return an {@link SQLFunctionCall} that refers to the geometry in the specified srs
610 * @throws DatastoreException
611 */
612 @Override
613 public SQLFunctionCall buildSRSTransformCall( MappedGeometryPropertyType geoProperty, String targetSRS )
614 throws DatastoreException {
615
616 int nativeSRSCode = getNativeSRSCode( targetSRS );
617 if ( nativeSRSCode == SRS_UNDEFINED ) {
618 String msg = Messages.getMessage( "DATASTORE_SQL_NATIVE_CT_UNKNOWN_SRS", this.getClass().getName(),
619 targetSRS );
620 throw new DatastoreException( msg );
621 }
622
623 MappingGeometryField field = geoProperty.getMappingField();
624 FunctionParam param1 = new FieldContent( field, new TableRelation[0] );
625 FunctionParam param2 = new ConstantContent( "" + nativeSRSCode );
626
627 SQLFunctionCall transformCall = new SQLFunctionCall( "SDO_CS.TRANSFORM($1,$2)", field.getType(), param1, param2 );
628 return transformCall;
629 }
630
631 @Override
632 public String buildSRSTransformCall( String geomIdentifier, int nativeSRSCode )
633 throws DatastoreException {
634 String call = "SDO_CS.TRANSFORM(" + geomIdentifier + "," + nativeSRSCode + ")";
635 return call;
636 }
637
638 @Override
639 public int getNativeSRSCode( String srsName ) {
640 Integer nativeSRSCode = nativeSrsCodeMap.get( srsName );
641 if ( nativeSRSCode == null ) {
642 return SRS_UNDEFINED;
643 }
644 return nativeSRSCode;
645 }
646
647 private void setNullValue( PreparedStatement preparedStatement, int i, int targetSqlType )
648 throws SQLException {
649 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
650 try {
651 String typeName = Types.getTypeNameForSQLTypeCode( targetSqlType );
652 LOG.logDebug( "Setting argument " + i + ": type=" + typeName );
653 LOG.logDebug( "Value: null" );
654 } catch ( UnknownTypeException e ) {
655 throw new SQLException( e.getMessage() );
656 }
657 }
658 preparedStatement.setNull( i, targetSqlType );
659 }
660
661 private static void initSRSCodeMap()
662 throws IOException {
663 InputStream is = OracleDatastore.class.getResourceAsStream( SRS_CODE_PROP_FILE );
664 Properties props = new Properties();
665 props.load( is );
666 for ( Object key : props.keySet() ) {
667 String nativeCodeStr = props.getProperty( (String) key ).trim();
668 try {
669 int nativeCode = Integer.parseInt( nativeCodeStr );
670 nativeSrsCodeMap.put( (String) key, nativeCode );
671 } catch ( NumberFormatException e ) {
672 String msg = Messages.getMessage( "DATASTORE_SRS_CODE_INVALID", SRS_CODE_PROP_FILE, nativeCodeStr, key );
673 throw new IOException( msg );
674 }
675 }
676 }
677 }