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 }