001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/io/datastore/sde/SDEWhereBuilder.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2006 by: M.O.S.S. Computer Grafik Systeme GmbH
006 Hohenbrunner Weg 13
007 D-82024 Taufkirchen
008 http://www.moss.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 ---------------------------------------------------------------------------*/
025 package org.deegree.io.datastore.sde;
026
027 import java.sql.Types;
028 import java.util.ArrayList;
029 import java.util.Iterator;
030 import java.util.List;
031
032 import org.deegree.framework.log.ILogger;
033 import org.deegree.framework.log.LoggerFactory;
034 import org.deegree.io.datastore.DatastoreException;
035 import org.deegree.io.datastore.PropertyPathResolvingException;
036 import org.deegree.io.datastore.schema.MappedFeatureType;
037 import org.deegree.io.datastore.schema.MappedGeometryPropertyType;
038 import org.deegree.io.datastore.schema.MappedPropertyType;
039 import org.deegree.io.datastore.schema.MappedSimplePropertyType;
040 import org.deegree.io.datastore.schema.content.MappingField;
041 import org.deegree.io.datastore.schema.content.MappingGeometryField;
042 import org.deegree.io.datastore.schema.content.SimpleContent;
043 import org.deegree.io.datastore.sql.TableAliasGenerator;
044 import org.deegree.io.datastore.sql.wherebuilder.GeometryPropertyNode;
045 import org.deegree.io.datastore.sql.wherebuilder.PropertyNode;
046 import org.deegree.io.datastore.sql.wherebuilder.QueryTableTree;
047 import org.deegree.io.datastore.sql.wherebuilder.SimplePropertyNode;
048 import org.deegree.io.sdeapi.SDEAdapter;
049 import org.deegree.model.filterencoding.ArithmeticExpression;
050 import org.deegree.model.filterencoding.ComparisonOperation;
051 import org.deegree.model.filterencoding.ComplexFilter;
052 import org.deegree.model.filterencoding.DBFunction;
053 import org.deegree.model.filterencoding.Expression;
054 import org.deegree.model.filterencoding.ExpressionDefines;
055 import org.deegree.model.filterencoding.FeatureFilter;
056 import org.deegree.model.filterencoding.FeatureId;
057 import org.deegree.model.filterencoding.Filter;
058 import org.deegree.model.filterencoding.FilterTools;
059 import org.deegree.model.filterencoding.Function;
060 import org.deegree.model.filterencoding.Literal;
061 import org.deegree.model.filterencoding.LogicalOperation;
062 import org.deegree.model.filterencoding.Operation;
063 import org.deegree.model.filterencoding.OperationDefines;
064 import org.deegree.model.filterencoding.PropertyIsBetweenOperation;
065 import org.deegree.model.filterencoding.PropertyIsCOMPOperation;
066 import org.deegree.model.filterencoding.PropertyIsLikeOperation;
067 import org.deegree.model.filterencoding.PropertyIsNullOperation;
068 import org.deegree.model.filterencoding.PropertyName;
069 import org.deegree.model.filterencoding.SpatialOperation;
070 import org.deegree.ogcbase.PropertyPath;
071
072 import com.esri.sde.sdk.client.SeCoordinateReference;
073 import com.esri.sde.sdk.client.SeFilter;
074 import com.esri.sde.sdk.client.SeLayer;
075 import com.esri.sde.sdk.client.SeShape;
076 import com.esri.sde.sdk.client.SeShapeFilter;
077
078 /**
079 * <code>WhereBuilder</code> implementation for ArcSDE.
080 *
081 * @author <a href="mailto:cpollmann@moss.de">Christoph Pollmann</a>
082 * @author last edited by: $Author: rbezema $
083 *
084 * @version $Revision: 12183 $, $Date: 2008-06-05 11:17:23 +0200 (Do, 05 Jun 2008) $
085 */
086 public class SDEWhereBuilder {
087
088 private static final ILogger LOG = LoggerFactory.getLogger( SDEWhereBuilder.class );
089
090 /**
091 * the root feature type
092 */
093 protected MappedFeatureType rootFeatureType;
094
095 /**
096 * The filter to apply
097 */
098 protected Filter filter;
099
100
101 /**
102 * The query
103 */
104 protected QueryTableTree queryTableTree;
105
106 /**
107 * The filter property paths
108 */
109 protected List<PropertyPath> filterPropertyPaths = new ArrayList<PropertyPath>();
110
111 /**
112 * Creates a new instance of <code>SDEWhereBuilder</code> for the given parameters.
113 *
114 * @param rootFts
115 * selected feature types, more than one type means that the types are joined
116 * @param aliases
117 * aliases for the feature types, may be null (must have same length as rootFts
118 * otherwise)
119 * @param filter
120 * @param aliasGenerator
121 * @throws DatastoreException
122 */
123 public SDEWhereBuilder( MappedFeatureType[] rootFts, String[] aliases, Filter filter,
124 TableAliasGenerator aliasGenerator ) throws DatastoreException {
125
126 this.rootFeatureType = rootFts[0];
127 this.queryTableTree = new QueryTableTree( rootFts, aliases, aliasGenerator );
128 this.filter = filter;
129 if ( filter != null ) {
130 if ( !( filter instanceof ComplexFilter || filter instanceof FeatureFilter ) ) {
131 throw new DatastoreException( "Invalid filter type: '" + filter.getClass()
132 + "'. Filter must be a ComplexFilter or a FeatureFilter." );
133 }
134 buildFilterPropertyNameMap();
135 for ( PropertyPath property : this.filterPropertyPaths ) {
136 this.queryTableTree.addFilterProperty( property );
137 }
138 }
139 }
140
141 /**
142 * @return the alias
143 */
144 public String getRootTableAlias() {
145 return this.queryTableTree.getRootAlias();
146 }
147
148 /**
149 * @return filter
150 */
151 public Filter getFilter() {
152 return this.filter;
153 }
154
155 /**
156 * Returns the internal (database specific) SRS code used in the geometry field of the given
157 * <code>SpatialOperation</code>.
158 *
159 * @param operation
160 * <code>SpatialOperation</code> for which the internal SRS is needed
161 * @return the internal (database specific) SRS code.
162 */
163 protected int getInternalSRS( SpatialOperation operation ) {
164 PropertyPath propertyPath = operation.getPropertyName().getValue();
165 PropertyNode propertyNode = this.queryTableTree.getPropertyNode( propertyPath );
166 if ( propertyNode == null ) {
167 String msg = "Internal error in WhereBuilder: no PropertyNode for path '" + propertyPath
168 + "' in QueryTableTree.";
169 LOG.logError( msg );
170 throw new RuntimeException( msg );
171 } else if ( !( propertyNode instanceof GeometryPropertyNode ) ) {
172 String msg = "Internal error in WhereBuilder: unexpected PropertyNode type: '"
173 + propertyNode.getClass().getName() + "'. Must be a GeometryPropertyNode.";
174 LOG.logError( msg );
175 throw new RuntimeException( msg );
176 }
177 MappedGeometryPropertyType gpc = (MappedGeometryPropertyType) propertyNode.getProperty();
178 MappingGeometryField field = gpc.getMappingField();
179 return field.getSRS();
180 }
181
182 /**
183 * @param propertyName
184 * @return the type of the property name as an int
185 */
186 protected int getPropertyNameSQLType( PropertyName propertyName ) {
187 PropertyPath propertyPath = propertyName.getValue();
188 PropertyNode propertyNode = this.queryTableTree.getPropertyNode( propertyPath );
189 if ( propertyNode == null ) {
190 String msg = "Internal error in WhereBuilder: no PropertyNode for path '" + propertyPath
191 + "' in QueryTableTree.";
192 LOG.logError( msg );
193 throw new RuntimeException( msg );
194 }
195 MappedPropertyType propertyType = propertyNode.getProperty();
196 if ( !( propertyType instanceof MappedSimplePropertyType ) ) {
197 String msg = "Error in WhereBuilder: cannot compare against properties of type '" + propertyType.getClass()
198 + "'.";
199 LOG.logError( msg );
200 throw new RuntimeException( msg );
201 }
202
203 SimpleContent content = ( (MappedSimplePropertyType) propertyType ).getContent();
204 if ( !( content instanceof MappingField ) ) {
205 String msg = "Virtual properties are currently ignored in SDEWhereBuilder#getPropertyNameSQLType(PropertyName).";
206 LOG.logError( msg );
207 return Types.VARCHAR;
208 }
209
210 int targetSqlType = ( (MappingField) content ).getType();
211 return targetSqlType;
212 }
213
214 /**
215 * @throws PropertyPathResolvingException
216 */
217 protected void buildFilterPropertyNameMap()
218 throws PropertyPathResolvingException {
219 if ( this.filter instanceof ComplexFilter ) {
220 buildPropertyNameMapFromOperation( ( (ComplexFilter) this.filter ).getOperation() );
221 } else if ( this.filter instanceof FeatureFilter ) {
222 // FeatureFilter doesn't have real properties, so we don't have to add them here
223 // maybe for join tables and table aliases we need some auxiliary constructions???
224 // throw new PropertyPathResolvingException( "FeatureFilter not implemented yet." );
225 }
226 }
227
228 private void buildPropertyNameMapFromOperation( Operation operation )
229 throws PropertyPathResolvingException {
230 switch ( OperationDefines.getTypeById( operation.getOperatorId() ) ) {
231 case OperationDefines.TYPE_SPATIAL: {
232 registerPropertyName( ( (SpatialOperation) operation ).getPropertyName() );
233 break;
234 }
235 case OperationDefines.TYPE_COMPARISON: {
236 buildPropertyNameMap( (ComparisonOperation) operation );
237 break;
238 }
239 case OperationDefines.TYPE_LOGICAL: {
240 buildPropertyNameMap( (LogicalOperation) operation );
241 break;
242 }
243 default: {
244 break;
245 }
246 }
247 }
248
249 private void buildPropertyNameMap( ComparisonOperation operation )
250 throws PropertyPathResolvingException {
251 switch ( operation.getOperatorId() ) {
252 case OperationDefines.PROPERTYISEQUALTO:
253 case OperationDefines.PROPERTYISLESSTHAN:
254 case OperationDefines.PROPERTYISGREATERTHAN:
255 case OperationDefines.PROPERTYISLESSTHANOREQUALTO:
256 case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: {
257 buildPropertyNameMap( ( (PropertyIsCOMPOperation) operation ).getFirstExpression() );
258 buildPropertyNameMap( ( (PropertyIsCOMPOperation) operation ).getSecondExpression() );
259 break;
260 }
261 case OperationDefines.PROPERTYISLIKE: {
262 registerPropertyName( ( (PropertyIsLikeOperation) operation ).getPropertyName() );
263 break;
264 }
265 case OperationDefines.PROPERTYISNULL: {
266 buildPropertyNameMap( ( (PropertyIsNullOperation) operation ).getPropertyName() );
267 break;
268 }
269 case OperationDefines.PROPERTYISBETWEEN: {
270 buildPropertyNameMap( ( (PropertyIsBetweenOperation) operation ).getLowerBoundary() );
271 buildPropertyNameMap( ( (PropertyIsBetweenOperation) operation ).getUpperBoundary() );
272 registerPropertyName( ( (PropertyIsBetweenOperation) operation ).getPropertyName() );
273 break;
274 }
275 default: {
276 break;
277 }
278 }
279 }
280
281 private void buildPropertyNameMap( LogicalOperation operation )
282 throws PropertyPathResolvingException {
283 List<Operation> operationList = operation.getArguments();
284 Iterator<Operation> it = operationList.iterator();
285 while ( it.hasNext() ) {
286 buildPropertyNameMapFromOperation( it.next() );
287 }
288 }
289
290 private void buildPropertyNameMap( Expression expression )
291 throws PropertyPathResolvingException {
292 switch ( expression.getExpressionId() ) {
293 case ExpressionDefines.PROPERTYNAME: {
294 registerPropertyName( (PropertyName) expression );
295 break;
296 }
297 case ExpressionDefines.ADD:
298 case ExpressionDefines.SUB:
299 case ExpressionDefines.MUL:
300 case ExpressionDefines.DIV: {
301 buildPropertyNameMap( ( (ArithmeticExpression) expression ).getFirstExpression() );
302 buildPropertyNameMap( ( (ArithmeticExpression) expression ).getSecondExpression() );
303 break;
304 }
305 case ExpressionDefines.FUNCTION: {
306 // TODO: What about PropertyNames used here?
307 break;
308 }
309 case ExpressionDefines.EXPRESSION:
310 case ExpressionDefines.LITERAL: {
311 break;
312 }
313 }
314 }
315
316 private void registerPropertyName( PropertyName propertyName ) {
317 this.filterPropertyPaths.add( propertyName.getValue() );
318 }
319
320 /*
321 * appendJoinTableList => String[] der Tabellennamen (mehrfach vorkommende Namen nicht erlaubt)
322 * appendOuterJoins => mit SDE bei versionierten Tabellen realisierbar???
323 */
324
325 /**
326 * Appends the SQL condition from the <code>Filter</code> to the given sql statement.
327 *
328 * @param whereCondition
329 */
330 public final void appendWhereCondition( StringBuffer whereCondition ) {
331 if ( filter instanceof ComplexFilter ) {
332 appendComplexFilterAsSQL( whereCondition, (ComplexFilter) filter );
333 } else if ( filter instanceof FeatureFilter ) {
334 FeatureFilter featureFilter = (FeatureFilter) filter;
335 if ( featureFilter.getFeatureIds().size() > 0 ) {
336 appendFeatureFilterAsSQL( whereCondition, featureFilter );
337 }
338 } else {
339 // assert false : "Unexpected filter type.";
340 }
341 }
342
343 /**
344 * Appends an SQL fragment for the given object.
345 *
346 * @param query
347 * @param filter
348 */
349 protected void appendComplexFilterAsSQL( StringBuffer query, ComplexFilter filter ) {
350 appendOperationAsSQL( query, filter.getOperation() );
351 }
352
353 /**
354 * Appends an SQL fragment for the given object to the given sql statement.
355 *
356 * @param query
357 * @param operation
358 */
359 protected void appendOperationAsSQL( StringBuffer query, Operation operation ) {
360
361 switch ( OperationDefines.getTypeById( operation.getOperatorId() ) ) {
362 case OperationDefines.TYPE_SPATIAL: {
363 // handled seperately with buildSpatialFilter()
364 break;
365 }
366 case OperationDefines.TYPE_COMPARISON: {
367 appendComparisonOperationAsSQL( query, (ComparisonOperation) operation );
368 break;
369 }
370 case OperationDefines.TYPE_LOGICAL: {
371 appendLogicalOperationAsSQL( query, (LogicalOperation) operation );
372 break;
373 }
374 default: {
375 break;
376 }
377 }
378 }
379
380 /**
381 * Appends an SQL fragment for the given object to the given sql statement.
382 *
383 * @param query
384 * @param operation
385 */
386 protected void appendComparisonOperationAsSQL( StringBuffer query, ComparisonOperation operation ) {
387 switch ( operation.getOperatorId() ) {
388 case OperationDefines.PROPERTYISEQUALTO:
389 case OperationDefines.PROPERTYISLESSTHAN:
390 case OperationDefines.PROPERTYISGREATERTHAN:
391 case OperationDefines.PROPERTYISLESSTHANOREQUALTO:
392 case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: {
393 appendPropertyIsCOMPOperationAsSQL( query, (PropertyIsCOMPOperation) operation );
394 break;
395 }
396 case OperationDefines.PROPERTYISLIKE: {
397 appendPropertyIsLikeOperationAsSQL( query, (PropertyIsLikeOperation) operation );
398 break;
399 }
400 case OperationDefines.PROPERTYISNULL: {
401 appendPropertyIsNullOperationAsSQL( query, (PropertyIsNullOperation) operation );
402 break;
403 }
404 case OperationDefines.PROPERTYISBETWEEN: {
405 appendPropertyIsBetweenOperationAsSQL( query, (PropertyIsBetweenOperation) operation );
406 break;
407 }
408 }
409 }
410
411 /**
412 * Appends an SQL fragment for the given object to the given sql statement.
413 *
414 * @param query
415 * @param operation
416 */
417 protected void appendPropertyIsCOMPOperationAsSQL( StringBuffer query, PropertyIsCOMPOperation operation ) {
418 Expression firstExpr = operation.getFirstExpression();
419 if ( !( firstExpr instanceof PropertyName ) ) {
420 throw new IllegalArgumentException( "First expression in a comparison must "
421 + "always be a 'PropertyName' element." );
422 }
423 int targetSqlType = getPropertyNameSQLType( (PropertyName) firstExpr );
424 if ( operation.isMatchCase() ) {
425 appendExpressionAsSQL( query, firstExpr, targetSqlType );
426 } else {
427 List<Expression> list = new ArrayList<Expression>();
428 list.add( firstExpr );
429 Function func = new DBFunction( "LOWER", list );
430 appendFunctionAsSQL( query, func, targetSqlType );
431 }
432 switch ( operation.getOperatorId() ) {
433 case OperationDefines.PROPERTYISEQUALTO: {
434 query.append( " = " );
435 break;
436 }
437 case OperationDefines.PROPERTYISLESSTHAN: {
438 query.append( " < " );
439 break;
440 }
441 case OperationDefines.PROPERTYISGREATERTHAN: {
442 query.append( " > " );
443 break;
444 }
445 case OperationDefines.PROPERTYISLESSTHANOREQUALTO: {
446 query.append( " <= " );
447 break;
448 }
449 case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: {
450 query.append( " >= " );
451 break;
452 }
453 }
454 if ( operation.isMatchCase() ) {
455 appendExpressionAsSQL( query, operation.getSecondExpression(), targetSqlType );
456 } else {
457 List<Expression> list = new ArrayList<Expression>();
458 list.add( operation.getSecondExpression() );
459 Function func = new DBFunction( "LOWER", list );
460 appendFunctionAsSQL( query, func, targetSqlType );
461 }
462 }
463
464 /**
465 * Appends an SQL fragment for the given object to the given sql statement. Replacing and escape
466 * handling is based on a finite automaton with 2 states:
467 * <p>
468 * (escapeMode)
469 * <ul>
470 * <li>' is appended as \', \ is appended as \\</li>
471 * <li>every character (including the escapeChar) is simply appended</li>
472 * <li>- unset escapeMode</li>
473 * (escapeMode is false)
474 * </ul>
475 * <ul>
476 * <li>' is appended as \', \ is appended as \\</li>
477 * <li>escapeChar means: skip char, set escapeMode</li>
478 * <li>wildCard means: append %</li>
479 * <li>singleChar means: append ?</li>
480 * </ul>
481 * </p>
482 *
483 * NOTE: Currently, the method uses a quirk and appends the generated argument inline, i.e. not
484 * using query.addArgument(). This is because of a problem that occurred for example in
485 * Postgresql; the execution of the inline version is *much* faster (at least with version 8.0).
486 *
487 * @param query
488 * @param operation
489 */
490 protected void appendPropertyIsLikeOperationAsSQL( StringBuffer query, PropertyIsLikeOperation operation ) {
491
492 String literal = operation.getLiteral().getValue();
493 char escapeChar = operation.getEscapeChar();
494 char wildCard = operation.getWildCard();
495 char singleChar = operation.getSingleChar();
496 boolean escapeMode = false;
497 int length = literal.length();
498 int targetSqlType = getPropertyNameSQLType( operation.getPropertyName() );
499 if ( operation.isMatchCase() ) {
500 appendPropertyNameAsSQL( query, operation.getPropertyName() );
501 } else {
502 List<PropertyName> list = new ArrayList<PropertyName>();
503 list.add( operation.getPropertyName() );
504 Function func = new DBFunction( "LOWER", list );
505 appendFunctionAsSQL( query, func, targetSqlType );
506 }
507 query.append( " LIKE '" );
508 StringBuffer parameter = new StringBuffer();
509 for ( int i = 0; i < length; i++ ) {
510 char c = literal.charAt( i );
511 if ( escapeMode ) {
512 // ' must (even in escapeMode) be converted to \'
513 if ( c == '\'' )
514 parameter.append( "\'" );
515 // \ must (even in escapeMode) be converted to \\
516 else if ( c == '\\' )
517 parameter.append( "\\\\" );
518 else
519 parameter.append( c );
520 escapeMode = false;
521 } else {
522 // escapeChar means: switch to escapeMode
523 if ( c == escapeChar )
524 escapeMode = true;
525 // wildCard must be converted to %
526 else if ( c == wildCard )
527 parameter.append( '%' );
528 // singleChar must be converted to ?
529 else if ( c == singleChar )
530 parameter.append( '?' );
531 // ' must be converted to \'
532 else if ( c == '\'' )
533 parameter.append( "$'$" );
534 // % must be converted to \'
535 else if ( c == '%' )
536 parameter.append( "$%$" );
537 // ? must be converted to \'
538 // else if (c == '?') sb.append("$?$");
539 // \ must (even in escapeMode) be converted to \\
540 else if ( c == '\\' )
541 parameter.append( "\\\\" );
542 else
543 parameter.append( c );
544 }
545 }
546 if ( operation.isMatchCase() ) {
547 query.append( parameter );
548 } else {
549 query.append( parameter.toString().toLowerCase() );
550 }
551 query.append( '\'' );
552 // query.addArgument( parameter.toString() );
553 }
554
555 /**
556 * Appends an SQL fragment for the given object to the given sql statement.
557 *
558 * @param query
559 * @param operation
560 */
561 protected void appendPropertyIsNullOperationAsSQL( StringBuffer query, PropertyIsNullOperation operation ) {
562 appendPropertyNameAsSQL( query, operation.getPropertyName() );
563 query.append( " IS NULL" );
564 }
565
566 /**
567 * Appends an SQL fragment for the given object to the given sql statement.
568 *
569 * @param query
570 * @param operation
571 */
572 protected void appendPropertyIsBetweenOperationAsSQL( StringBuffer query, PropertyIsBetweenOperation operation ) {
573
574 PropertyName propertyName = operation.getPropertyName();
575 int targetSqlType = getPropertyNameSQLType( propertyName );
576 appendExpressionAsSQL( query, operation.getLowerBoundary(), targetSqlType );
577 query.append( " <= " );
578 appendPropertyNameAsSQL( query, propertyName );
579 query.append( " AND " );
580 appendPropertyNameAsSQL( query, propertyName );
581 query.append( " <= " );
582 appendExpressionAsSQL( query, operation.getUpperBoundary(), targetSqlType );
583 }
584
585 /**
586 * Appends an SQL fragment for the given object to the given sql statement.
587 *
588 * @param query
589 * @param expression
590 * @param targetSqlType
591 * sql type code to be used for literals at the bottom of the expression tree
592 */
593 protected void appendExpressionAsSQL( StringBuffer query, Expression expression, int targetSqlType ) {
594 switch ( expression.getExpressionId() ) {
595 case ExpressionDefines.PROPERTYNAME: {
596 appendPropertyNameAsSQL( query, (PropertyName) expression );
597 break;
598 }
599 case ExpressionDefines.LITERAL: {
600 appendLiteralAsSQL( query, (Literal) expression, targetSqlType );
601 break;
602 }
603 case ExpressionDefines.FUNCTION: {
604 Function function = (Function) expression;
605 appendFunctionAsSQL( query, function, targetSqlType );
606 break;
607 }
608 case ExpressionDefines.ADD:
609 case ExpressionDefines.SUB:
610 case ExpressionDefines.MUL:
611 case ExpressionDefines.DIV: {
612 appendArithmeticExpressionAsSQL( query, (ArithmeticExpression) expression, targetSqlType );
613 break;
614 }
615 case ExpressionDefines.EXPRESSION:
616 default: {
617 throw new IllegalArgumentException( "Unexpected expression type: " + expression.getExpressionName() );
618 }
619 }
620 }
621
622 /**
623 * Appends an SQL fragment for the given object to the given sql statement.
624 *
625 * @param query
626 * @param literal
627 * @param targetSqlType
628 */
629 protected void appendLiteralAsSQL( StringBuffer query, Literal literal, int targetSqlType ) {
630 switch ( targetSqlType ) {
631 case java.sql.Types.DECIMAL:
632 case java.sql.Types.DOUBLE:
633 case java.sql.Types.FLOAT:
634 case java.sql.Types.INTEGER:
635 case java.sql.Types.NUMERIC:
636 case java.sql.Types.REAL:
637 case java.sql.Types.SMALLINT:
638 case java.sql.Types.TINYINT:
639 query.append( literal.getValue() );
640 break;
641 default:
642 query.append( "'" + literal.getValue() + "'" );
643 break;
644 }
645 }
646
647 /**
648 * Appends an SQL fragment for the given object to the given sql statement.
649 *
650 * @param propertyName
651 * @return the field mapped from the propertyname
652 */
653 protected MappingField getPropertyNameMapping( PropertyName propertyName ) {
654
655 PropertyPath propertyPath = propertyName.getValue();
656 LOG.logDebug( "Looking up '" + propertyPath + "' in the query table tree." );
657 MappingField mappingField = null;
658 PropertyNode propertyNode = this.queryTableTree.getPropertyNode( propertyPath );
659 if ( propertyNode == null ) {
660 String msg = "Internal error in WhereBuilder: no PropertyNode for path '" + propertyPath
661 + "' in QueryTableTree.";
662 LOG.logError( msg );
663 throw new RuntimeException( msg );
664 } else if ( propertyNode instanceof SimplePropertyNode ) {
665 SimpleContent content = ( (MappedSimplePropertyType) ( propertyNode.getProperty() ) ).getContent();
666 if ( !( content instanceof MappingField ) ) {
667 String msg = "Virtual properties are currently ignored in WhereBuilder#appendPropertyPathAsSQL(StatementBuffer,PropertyPath).";
668 LOG.logError( msg );
669 throw new RuntimeException( msg );
670 }
671 mappingField = (MappingField) content;
672 } else if ( propertyNode instanceof GeometryPropertyNode ) {
673 mappingField = ( (MappedGeometryPropertyType) propertyNode.getProperty() ).getMappingField();
674 } else {
675 String msg = "Internal error in WhereBuilder: unhandled PropertyNode type: '"
676 + propertyNode.getClass().getName() + "'.";
677 LOG.logError( msg );
678 throw new RuntimeException( msg );
679 }
680 return mappingField;
681 }
682
683 /**
684 * Appends an SQL fragment for the given object to the given sql statement.
685 *
686 * @param query
687 * @param propertyName
688 */
689 protected void appendPropertyNameAsSQL( StringBuffer query, PropertyName propertyName ) {
690
691 MappingField mappingField = getPropertyNameMapping( propertyName );
692 // with ArcSDE because of versioning not applicable
693 // query.append( mappingField.getTable() );
694 // query.append( '.' );
695 query.append( mappingField.getField() );
696 }
697
698 /**
699 * Appends an SQL fragment for the given object to the given sql statement.
700 *
701 * @param query
702 * @param expression
703 * @param targetSqlType
704 */
705 protected void appendArithmeticExpressionAsSQL( StringBuffer query, ArithmeticExpression expression,
706 int targetSqlType ) {
707 query.append( '(' );
708 appendExpressionAsSQL( query, expression.getFirstExpression(), targetSqlType );
709 switch ( expression.getExpressionId() ) {
710 case ExpressionDefines.ADD: {
711 query.append( '+' );
712 break;
713 }
714 case ExpressionDefines.SUB: {
715 query.append( '-' );
716 break;
717 }
718 case ExpressionDefines.MUL: {
719 query.append( '*' );
720 break;
721 }
722 case ExpressionDefines.DIV: {
723 query.append( '/' );
724 break;
725 }
726 }
727 appendExpressionAsSQL( query, expression.getSecondExpression(), targetSqlType );
728 query.append( ')' );
729 }
730
731 /**
732 * Appends an SQL fragment for the given object to the given sql statement.
733 *
734 * @param query
735 * @param function
736 * @param targetSqlType
737 */
738 protected void appendFunctionAsSQL( StringBuffer query, Function function, int targetSqlType ) {
739 query.append( function.getName() );
740 query.append( " (" );
741 List<Expression> list = function.getArguments();
742 for ( int i = 0; i < list.size(); i++ ) {
743 Expression expression = list.get( i );
744 appendExpressionAsSQL( query, expression, targetSqlType );
745 if ( i != list.size() - 1 )
746 query.append( ", " );
747 }
748 query.append( ")" );
749 }
750
751 /**
752 * Appends an SQL fragment for the given object to the given sql statement.
753 *
754 * @param query
755 * @param operation
756 */
757 protected void appendLogicalOperationAsSQL( StringBuffer query, LogicalOperation operation ) {
758 List<Operation> argumentList = operation.getArguments();
759 switch ( operation.getOperatorId() ) {
760 case OperationDefines.AND: {
761 for ( int i = 0; i < argumentList.size(); i++ ) {
762 Operation argument = argumentList.get( i );
763 query.append( '(' );
764 appendOperationAsSQL( query, argument );
765 query.append( ')' );
766 if ( i != argumentList.size() - 1 )
767 query.append( " AND " );
768 }
769 break;
770 }
771 case OperationDefines.OR: {
772 for ( int i = 0; i < argumentList.size(); i++ ) {
773 Operation argument = argumentList.get( i );
774 query.append( '(' );
775 appendOperationAsSQL( query, argument );
776 query.append( ')' );
777 if ( i != argumentList.size() - 1 )
778 query.append( " OR " );
779 }
780 break;
781 }
782 case OperationDefines.NOT: {
783 Operation argument = argumentList.get( 0 );
784 query.append( "NOT (" );
785 appendOperationAsSQL( query, argument );
786 query.append( ')' );
787 break;
788 }
789 }
790 }
791
792 /**
793 * Appends an SQL fragment for the given object to the given sql statement.
794 *
795 * TODO Handle compound primary keys correctly.
796 *
797 * @param query
798 * @param filter
799 */
800 protected void appendFeatureFilterAsSQL( StringBuffer query, FeatureFilter filter ) {
801 ArrayList<FeatureId> list = filter.getFeatureIds();
802 MappingField mapping = rootFeatureType.getGMLId().getIdFields()[0];
803 String quote = "";
804 switch ( mapping.getType() ) {
805 case java.sql.Types.DECIMAL:
806 case java.sql.Types.DOUBLE:
807 case java.sql.Types.FLOAT:
808 case java.sql.Types.INTEGER:
809 case java.sql.Types.NUMERIC:
810 case java.sql.Types.REAL:
811 case java.sql.Types.SMALLINT:
812 case java.sql.Types.TINYINT:
813 break;
814 default:
815 quote = "'";
816 break;
817 }
818 query.append( ' ' );
819 query.append( mapping.getField() );
820 try {
821 for ( int i = 0; i < list.size(); i++ ) {
822 if ( 0 == i )
823 query.append( " IN (" + quote );
824 else
825 query.append( quote + "," + quote );
826 String fid = list.get( i ).getValue();
827 Object fidValue = org.deegree.io.datastore.FeatureId.removeFIDPrefix( fid, rootFeatureType.getGMLId() );
828 query.append( fidValue.toString() );
829 }
830 } catch ( Exception e ) {
831 LOG.logError( "Error converting feature id", e );
832 }
833 query.append( quote + ")" );
834 }
835
836 /**
837 * Generates an SQL-fragment for the given object.
838 *
839 * @param filter
840 * @param layers
841 * @return the complex filters as se filters.
842 *
843 * @throws DatastoreException
844 */
845 protected SeFilter[] buildSpatialFilter( ComplexFilter filter, List<SeLayer> layers )
846 throws DatastoreException {
847
848 SpatialOperation[] spatialOps = FilterTools.extractSpatialFilter( filter );
849 if ( null == spatialOps || 0 == spatialOps.length )
850 return null;
851
852 SeFilter[] spatialFilter = new SeFilter[spatialOps.length];
853
854 for ( int i = 0; i < spatialOps.length; i++ ) {
855 try {
856 MappingField mappingField = getPropertyNameMapping( spatialOps[i].getPropertyName() );
857 String filterTable = mappingField.getTable();
858 String filterColumn = mappingField.getField();
859
860 SeCoordinateReference coordRef = null;
861 String[] splitted = filterTable.toUpperCase().split( "\\." );
862 String tmp = splitted[splitted.length - 1];
863 for ( int k = 0; k < layers.size(); k++ ) {
864 SeLayer layer = layers.get( k );
865 splitted = layer.getName().toUpperCase().split( "\\." );
866 if ( splitted[splitted.length - 1].equals( tmp ) ) {
867 coordRef = layer.getCoordRef();
868 break;
869 }
870 }
871 if ( null == coordRef ) {
872 coordRef = new SeCoordinateReference();
873 }
874
875 int filterMethod = -1;
876 boolean filterTruth = true;
877 switch ( spatialOps[i].getOperatorId() ) {
878 case OperationDefines.CROSSES: {
879 filterMethod = SeFilter.METHOD_LCROSS;
880 break;
881 }
882 case OperationDefines.EQUALS: {
883 filterMethod = SeFilter.METHOD_IDENTICAL;
884 break;
885 }
886 case OperationDefines.WITHIN: {
887 filterMethod = SeFilter.METHOD_SC_NO_ET;
888 break;
889 }
890 case OperationDefines.OVERLAPS: {
891 filterMethod = SeFilter.METHOD_ENVP;
892 break;
893 }
894 case OperationDefines.TOUCHES: {
895 filterMethod = SeFilter.METHOD_ET_OR_AI;
896 break;
897 }
898 case OperationDefines.DISJOINT: {
899 filterMethod = SeFilter.METHOD_SC_NO_ET;
900 filterTruth = false;
901 break;
902 }
903 case OperationDefines.INTERSECTS: {
904 filterMethod = SeFilter.METHOD_AI;
905 break;
906 }
907 case OperationDefines.CONTAINS: {
908 filterMethod = SeFilter.METHOD_AI_OR_ET;
909 break;
910 }
911 case OperationDefines.BBOX: {
912 filterMethod = SeFilter.METHOD_ENVP;
913 break;
914 }
915 case OperationDefines.DWITHIN:
916 case OperationDefines.BEYOND:
917 default: {
918 continue;
919 }
920 }
921 SeShape filterGeom = SDEAdapter.export( spatialOps[i].getGeometry(), coordRef );
922 spatialFilter[i] = new SeShapeFilter( filterTable, filterColumn, filterGeom, filterMethod, filterTruth );
923 } catch ( Exception e ) {
924 e.printStackTrace();
925 throw new DatastoreException( "Error creating spatial filter", e );
926 }
927 }
928 return spatialFilter;
929 }
930 }