001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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    }