    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/io/datastore/sql/oracle/OracleSpatialWhereBuilder.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    package org.deegree.io.datastore.sql.oracle;
    037
    038    import java.sql.Types;
    039
    040    import oracle.spatial.geometry.JGeometry;
    041
    042    import org.deegree.i18n.Messages;
    043    import org.deegree.io.datastore.DatastoreException;
    044    import org.deegree.io.datastore.schema.MappedFeatureType;
    045    import org.deegree.io.datastore.schema.MappedGeometryPropertyType;
    046    import org.deegree.io.datastore.sql.StatementBuffer;
    047    import org.deegree.io.datastore.sql.TableAliasGenerator;
    048    import org.deegree.io.datastore.sql.VirtualContentProvider;
    049    import org.deegree.io.datastore.sql.wherebuilder.WhereBuilder;
    050    import org.deegree.model.filterencoding.Filter;
    051    import org.deegree.model.filterencoding.OperationDefines;
    052    import org.deegree.model.filterencoding.SpatialOperation;
    053    import org.deegree.model.spatialschema.Geometry;
    054    import org.deegree.model.spatialschema.GeometryException;
    055    import org.deegree.ogcbase.SortProperty;
    056
    057    /**
    058     * {@link WhereBuilder} implementation for Oracle Spatial. Supports Oracle Spatial for Oracle
    059     * Database 10g.
    060     *
    061     * TODO Which Oracle spatial versions are supported exactly?
    062     *
    063     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </A>
    064     * @author <a href="mailto:tfr@users.sourceforge.net">Torsten Friebe </A>
    065     * @author last edited by: $Author: mschneider $
    066     *
    067     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
    068     */
    069    public class OracleSpatialWhereBuilder extends WhereBuilder {
    070
    071        private static final int SRS_UNDEFINED = -1;
    072
    073        private OracleDatastore ds;
    074
    075        /**
    076         * Creates a new instance of <code>OracleSpatialWhereBuilder</code> from the given parameters.
    077         *
    078         * @param rootFts
    079         *            selected feature types, more than one type means that the types are joined
    080         * @param aliases
    081         *            aliases for the feature types, may be null (must have same length as rootFts
    082         *            otherwise)
    083         * @param filter
    084         *            filter that restricts the matched features
    085         * @param sortProperties
    086         *            sort criteria for the result, may be null or empty
    087         * @param aliasGenerator
    088         *            used to generate unique table aliases
    089         * @param vcProvider
    090         * @throws DatastoreException
    091         */
    092        public OracleSpatialWhereBuilder( MappedFeatureType[] rootFts, String[] aliases, Filter filter,
    093                                          SortProperty[] sortProperties, TableAliasGenerator aliasGenerator,
    094                                          VirtualContentProvider vcProvider ) throws DatastoreException {
    095            super( rootFts, aliases, filter, sortProperties, aliasGenerator, vcProvider );
    096            this.ds = (OracleDatastore) rootFts[0].getGMLSchema().getDatastore();
    097        }
    098
    099        /**
    100         * Generates an SQL-fragment for the given object.
    101         *
    102         * @throws DatastoreException
    103         */
    104        @Override
    105        protected void appendSpatialOperationAsSQL( StatementBuffer query, SpatialOperation operation )
    106                                throws DatastoreException {
    107
    108            try {
    109                switch ( operation.getOperatorId() ) {
    110                case OperationDefines.BBOX:
    111                case OperationDefines.INTERSECTS: {
    112                    appendRelateOperationAsSQL( query, operation, "ANYINTERACT" );
    113                    break;
    114                }
    115                case OperationDefines.EQUALS: {
    116                    appendRelateOperationAsSQL( query, operation, "EQUAL" );
    117                    break;
    118                }
    119                case OperationDefines.DISJOINT: {
    120                    query.append( "NOT " );
    121                    appendRelateOperationAsSQL( query, operation, "ANYINTERACT" );
    122                    break;
    123                }
    124                case OperationDefines.TOUCHES: {
    125                    appendRelateOperationAsSQL( query, operation, "TOUCH" );
    126                    break;
    127                }
    128                case OperationDefines.WITHIN: {
    129                    appendRelateOperationAsSQL( query, operation, "INSIDE+COVEREDBY" );
    130                    break;
    131                }
    132                case OperationDefines.OVERLAPS: {
    133                    appendRelateOperationAsSQL( query, operation, "OVERLAPBDYINTERSECT" );
    134                    break;
    135                }
    136                case OperationDefines.CROSSES: {
    137                    appendRelateOperationAsSQL( query, operation, "OVERLAPBDYDISJOINT" );
    138                    break;
    139                }
    140                case OperationDefines.CONTAINS: {
    141                    appendRelateOperationAsSQL( query, operation, "CONTAINS+COVERS" );
    142                    break;
    143                }
    144                case OperationDefines.DWITHIN: {
    145                    appendDWithinOperationAsSQL( query, operation );
    146                    break;
    147                }
    148                case OperationDefines.BEYOND: {
    149                    query.append( "NOT " );
    150                    appendDWithinOperationAsSQL( query, operation );
    151                    break;
    152                }
    153                default: {
    154                    String msg = "Spatial operator" + OperationDefines.getNameById( operation.getOperatorId() )
    155                                 + " not supported by '" + this.getClass().toString() + "'.";
    156                    throw new DatastoreException( msg );
    157                }
    158                }
    159            } catch ( GeometryException e ) {
    160                throw new DatastoreException( e );
    161            }
    162
    163        }
    164
    165        private void appendRelateOperationAsSQL( StatementBuffer query, SpatialOperation operation, String mask )
    166                                throws GeometryException, DatastoreException {
    167            query.append( "MDSYS.SDO_RELATE(" );
    168            appendPropertyNameAsSQL( query, operation.getPropertyName() );
    169            query.append( ',' );
    170            appendGeometryArgument( query, getGeometryProperty( operation.getPropertyName() ), operation.getGeometry() );
    171            query.append( ",'MASK=" + mask + " QUERYTYPE=WINDOW')='TRUE'" );
    172        }
    173
    174        private void appendDWithinOperationAsSQL( StatementBuffer query, SpatialOperation operation )
    175                                throws GeometryException, DatastoreException {
    176
    177            query.append( "SDO_WITHIN_DISTANCE(" );
    178            appendPropertyNameAsSQL( query, operation.getPropertyName() );
    179            query.append( ',' );
    180            appendGeometryArgument( query, getGeometryProperty( operation.getPropertyName() ), operation.getGeometry() );
    181            query.append( ",'DISTANCE=" + operation.getDistance() + "')='TRUE'" );
    182        }
    183
    184        /**
    185         * Construct and append the geometry argument using the correct internal SRS and perform a
    186         * transform call to the internal SRS of the {@link MappedGeometryPropertyType} if necessary.
    187         *
    188         * @param query
    189         * @param geoProperty
    190         * @param geometry
    191         * @throws DatastoreException
    192         * @throws GeometryException
    193         */
    194        private void appendGeometryArgument( StatementBuffer query, MappedGeometryPropertyType geoProperty,
    195                                             Geometry geometry )
    196                                throws DatastoreException, GeometryException {
    197
    198            String argumentSRS = null;
    199            if ( geometry.getCoordinateSystem() != null ) {
    200                argumentSRS = geometry.getCoordinateSystem().getIdentifier();
    201            }
    202            String propertySRS = geoProperty.getCS().getIdentifier();
    203            int internalSRS = geoProperty.getMappingField().getSRS();
    204
    205            int createSRSCode = getArgumentSRSCode( argumentSRS, propertySRS, internalSRS );
    206            JGeometry argument = JGeometryAdapter.export( geometry, createSRSCode );
    207
    208            int targetSRSCode = getTargetSRSCode( argumentSRS, propertySRS, internalSRS );
    209            if ( targetSRSCode != SRS_UNDEFINED ) {
    210                query.append( ds.buildSRSTransformCall( "?", targetSRSCode ) );
    211            } else {
    212                query.append( '?' );
    213            }
    214            query.addArgument( argument, Types.STRUCT );
    215        }
    216
    217        /**
    218         * Returns the internal SRS code that must be used for the creation of a geometry argument used
    219         * in a spatial operator.
    220         *
    221         * @param argumentSRS
    222         * @param propertySRS
    223         * @param internalSrs
    224         * @return the internal SRS code that must be used for the creation of a geometry argument
    225         * @throws DatastoreException
    226         */
    227        private int getArgumentSRSCode( String argumentSRS, String propertySRS, int internalSrs )
    228                                throws DatastoreException {
    229            int argumentSRSCode = internalSrs;
    230            if ( argumentSRS == null ) {
    231                argumentSRSCode = internalSrs;
    232            } else if ( !propertySRS.equals( argumentSRS ) ) {
    233                argumentSRSCode = this.ds.getNativeSRSCode( argumentSRS );
    234                if ( argumentSRSCode == SRS_UNDEFINED ) {
    235                    String msg = Messages.getMessage( "DATASTORE_SQL_NATIVE_CT_UNKNOWN_SRS",
    236                                                      OracleDatastore.class.getName(), argumentSRS );
    237                    throw new DatastoreException( msg );
    238                }
    239            }
    240            return argumentSRSCode;
    241        }
    242
    243        /**
    244         * Returns the internal SRS code that must be used for the transform call for a geometry
    245         * argument used in a spatial operator.
    246         *
    247         * @param argumentSRS
    248         * @param propertySRS
    249         * @param internalSrs
    250         * @return the internal SRS code that must be used for the transform call of a geometry
    251         *         argument, or -1 if no transformation is necessary
    252         */
    253        private int getTargetSRSCode( String argumentSRS, String propertySRS, int internalSrs )
    254                                throws DatastoreException {
    255            int targetSRS = SRS_UNDEFINED;
    256            if ( argumentSRS != null && !argumentSRS.equals( propertySRS ) ) {
    257                if ( internalSrs == SRS_UNDEFINED ) {
    258                    String msg = Messages.getMessage( "DATASTORE_SRS_NOT_SPECIFIED2", argumentSRS, propertySRS );
    259                    throw new DatastoreException( msg );
    260                }
    261                targetSRS = internalSrs;
    262            }
    263            return targetSRS;
    264        }
    265    }