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