001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/io/datastore/sql/oracle/OracleSpatialWhereBuilder.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003     This file is part of deegree.
004     Copyright (C) 2001-2006 by:
005     Department of Geography, University of Bonn
006     http://www.giub.uni-bonn.de/deegree/
007     lat/lon GmbH
008     http://www.lat-lon.de
009    
010     This library is free software; you can redistribute it and/or
011     modify it under the terms of the GNU Lesser General Public
012     License as published by the Free Software Foundation; either
013     version 2.1 of the License, or (at your option) any later version.
014    
015     This library is distributed in the hope that it will be useful,
016     but WITHOUT ANY WARRANTY; without even the implied warranty of
017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
018     Lesser General Public License for more details.
019    
020     You should have received a copy of the GNU Lesser General Public
021     License along with this library; if not, write to the Free Software
022     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
023    
024     Contact:
025    
026     Andreas Poth
027     lat/lon GmbH
028     Aennchenstraße 19
029     53177 Bonn
030     Germany
031     E-Mail: poth@lat-lon.de
032    
033     Jens Fitzke
034     lat/lon GmbH
035     Aennchenstraße 19
036     53177 Bonn
037     Germany
038     E-Mail: jens.fitzke@uni-bonn.de
039     ---------------------------------------------------------------------------*/
040    package org.deegree.io.datastore.sql.oracle;
041    
042    import java.sql.Types;
043    
044    import oracle.spatial.geometry.JGeometry;
045    
046    import org.deegree.i18n.Messages;
047    import org.deegree.io.datastore.DatastoreException;
048    import org.deegree.io.datastore.schema.MappedFeatureType;
049    import org.deegree.io.datastore.schema.MappedGeometryPropertyType;
050    import org.deegree.io.datastore.sql.StatementBuffer;
051    import org.deegree.io.datastore.sql.TableAliasGenerator;
052    import org.deegree.io.datastore.sql.VirtualContentProvider;
053    import org.deegree.io.datastore.sql.wherebuilder.WhereBuilder;
054    import org.deegree.model.filterencoding.Filter;
055    import org.deegree.model.filterencoding.OperationDefines;
056    import org.deegree.model.filterencoding.SpatialOperation;
057    import org.deegree.model.spatialschema.Geometry;
058    import org.deegree.model.spatialschema.GeometryException;
059    import org.deegree.ogcbase.SortProperty;
060    
061    /**
062     * {@link WhereBuilder} implementation for Oracle Spatial. Supports Oracle Spatial for Oracle
063     * Database 10g.
064     * 
065     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </A>
066     * @author <a href="mailto:tfr@users.sourceforge.net">Torsten Friebe </A>
067     * @author last edited by: $Author: mschneider $
068     * 
069     * @version $Revision: 6633 $, $Date: 2007-04-18 16:32:16 +0200 (Mi, 18 Apr 2007) $
070     */
071    public class OracleSpatialWhereBuilder extends WhereBuilder {
072    
073        private static final int SRS_UNDEFINED = -1;
074    
075        private OracleDatastore ds;
076    
077        /**
078         * Creates a new instance of <code>OracleSpatialWhereBuilder</code> from the given parameters.
079         * 
080         * @param rootFts
081         *            selected feature types, more than one type means that the types are joined
082         * @param aliases
083         *            aliases for the feature types, may be null (must have same length as rootFts
084         *            otherwise)
085         * @param filter
086         *            filter that restricts the matched features
087         * @param sortProperties
088         *            sort criteria for the result, may be null or empty
089         * @param aliasGenerator
090         *            used to generate unique table aliases
091         * @param vcProvider
092         * @throws DatastoreException
093         */
094        public OracleSpatialWhereBuilder( MappedFeatureType[] rootFts, String[] aliases, Filter filter,
095                                          SortProperty[] sortProperties, TableAliasGenerator aliasGenerator,
096                                          VirtualContentProvider vcProvider ) throws DatastoreException {
097            super( rootFts, aliases, filter, sortProperties, aliasGenerator, vcProvider );
098            this.ds = (OracleDatastore) rootFts[0].getGMLSchema().getDatastore();
099        }
100    
101        /**
102         * Generates an SQL-fragment for the given object.
103         * 
104         * @throws DatastoreException
105         */
106        @Override
107        protected void appendSpatialOperationAsSQL( StatementBuffer query, SpatialOperation operation )
108                                throws DatastoreException {
109    
110            try {
111                switch ( operation.getOperatorId() ) {
112                case OperationDefines.BBOX:
113                case OperationDefines.INTERSECTS: {
114                    appendRelateOperationAsSQL( query, operation, "ANYINTERACT" );
115                    break;
116                }
117                case OperationDefines.EQUALS: {
118                    appendRelateOperationAsSQL( query, operation, "EQUAL" );
119                    break;
120                }
121                case OperationDefines.DISJOINT: {
122                    query.append( "NOT " );
123                    appendRelateOperationAsSQL( query, operation, "ANYINTERACT" );
124                    break;
125                }
126                case OperationDefines.TOUCHES: {
127                    appendRelateOperationAsSQL( query, operation, "TOUCH" );
128                    break;
129                }
130                case OperationDefines.WITHIN: {
131                    appendRelateOperationAsSQL( query, operation, "INSIDE+COVEREDBY" );
132                    break;
133                }
134                case OperationDefines.OVERLAPS: {
135                    appendRelateOperationAsSQL( query, operation, "OVERLAPBDYINTERSECT" );
136                    break;
137                }
138                case OperationDefines.CROSSES: {
139                    appendRelateOperationAsSQL( query, operation, "OVERLAPBDYDISJOINT" );
140                    break;
141                }
142                case OperationDefines.CONTAINS: {
143                    appendRelateOperationAsSQL( query, operation, "CONTAINS+COVERS" );
144                    break;
145                }
146                case OperationDefines.DWITHIN: {
147                    appendDWithinOperationAsSQL( query, operation );
148                    break;
149                }
150                case OperationDefines.BEYOND: {
151                    query.append( "NOT " );
152                    appendDWithinOperationAsSQL( query, operation );
153                    break;
154                }
155                default: {
156                    String msg = "Spatial operator" + OperationDefines.getNameById( operation.getOperatorId() )
157                                 + " not supported by '" + this.getClass().toString() + "'.";
158                    throw new DatastoreException( msg );
159                }
160                }
161            } catch ( GeometryException e ) {
162                throw new DatastoreException( e );
163            }
164    
165        }
166    
167        private void appendRelateOperationAsSQL( StatementBuffer query, SpatialOperation operation, String mask )
168                                throws GeometryException, DatastoreException {
169            query.append( "MDSYS.SDO_RELATE(" );
170            appendPropertyNameAsSQL( query, operation.getPropertyName() );
171            query.append( ',' );
172            appendGeometryArgument( query, getGeometryProperty( operation.getPropertyName() ), operation.getGeometry() );
173            query.append( ",'MASK=" + mask + " QUERYTYPE=WINDOW')='TRUE'" );
174        }
175    
176        private void appendDWithinOperationAsSQL( StatementBuffer query, SpatialOperation operation )
177                                throws GeometryException, DatastoreException {
178    
179            query.append( "SDO_WITHIN_DISTANCE(" );
180            appendPropertyNameAsSQL( query, operation.getPropertyName() );
181            query.append( ',' );
182            appendGeometryArgument( query, getGeometryProperty( operation.getPropertyName() ), operation.getGeometry() );
183            query.append( ",'DISTANCE=" + operation.getDistance() + "')='TRUE'" );
184        }
185    
186        /**
187         * Construct and append the geometry argument using the correct internal SRS and perform a
188         * transform call to the internal SRS of the {@link MappedGeometryPropertyType} if necessary.
189         * 
190         * @param query
191         * @param geoProperty
192         * @param geometry
193         * @throws DatastoreException
194         * @throws GeometryException
195         */
196        private void appendGeometryArgument( StatementBuffer query, MappedGeometryPropertyType geoProperty,
197                                             Geometry geometry )
198                                throws DatastoreException, GeometryException {
199    
200            String argumentSRS = null;
201            if ( geometry.getCoordinateSystem() != null ) {
202                argumentSRS = geometry.getCoordinateSystem().getName();
203            }
204            String propertySRS = geoProperty.getCS().getName();
205            int internalSRS = geoProperty.getMappingField().getSRS();
206    
207            int createSRSCode = getArgumentSRSCode( argumentSRS, propertySRS, internalSRS );
208            JGeometry argument = JGeometryAdapter.export( geometry, createSRSCode );
209    
210            int targetSRSCode = getTargetSRSCode( argumentSRS, propertySRS, internalSRS );
211            if ( targetSRSCode != SRS_UNDEFINED ) {
212                query.append( ds.buildSRSTransformCall( "?", targetSRSCode ) );
213            } else {
214                query.append( '?' );
215            }
216            query.addArgument( argument, Types.STRUCT );
217        }
218    
219        /**
220         * Returns the internal SRS code that must be used for the creation of a geometry argument used
221         * in a spatial operator.
222         * 
223         * @param literalSRS
224         * @param propertySRS
225         * @param internalSrs
226         * @return the internal SRS code that must be used for the creation of a geometry argument
227         * @throws DatastoreException
228         */
229        private int getArgumentSRSCode( String argumentSRS, String propertySRS, int internalSrs )
230                                throws DatastoreException {
231            int argumentSRSCode = internalSrs;
232            if ( argumentSRS == null ) {
233                argumentSRSCode = internalSrs;
234            } else if ( !propertySRS.equals( argumentSRS ) ) {
235                argumentSRSCode = this.ds.getNativeSRSCode( argumentSRS );
236                if ( argumentSRSCode == SRS_UNDEFINED ) {
237                    String msg = Messages.getMessage( "DATASTORE_SQL_NATIVE_CT_UNKNOWN_SRS",
238                                                      OracleDatastore.class.getName(), argumentSRS );
239                    throw new DatastoreException( msg );
240                }
241            }
242            return argumentSRSCode;
243        }
244    
245        /**
246         * Returns the internal SRS code that must be used for the transform call for a geometry
247         * argument used in a spatial operator.
248         * 
249         * @param literalSRS
250         * @param propertySRS
251         * @param internalSrs
252         * @return the internal SRS code that must be used for the transform call of a geometry
253         *         argument, or -1 if no transformation is necessary
254         */
255        private int getTargetSRSCode( String argumentSRS, String propertySRS, int internalSrs )
256                                throws DatastoreException {
257            int targetSRS = SRS_UNDEFINED;
258            if ( argumentSRS != null && !argumentSRS.equals( propertySRS ) ) {
259                if ( internalSrs == SRS_UNDEFINED ) {
260                    String msg = Messages.getMessage( "DATASTORE_SRS_NOT_SPECIFIED2", argumentSRS, propertySRS );
261                    throw new DatastoreException( msg );
262                }
263                targetSRS = internalSrs;
264            }
265            return targetSRS;
266        }
267    }