//$Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/io/datastore/sde/SDEWhereBuilder.java,v 1.12 2006/11/29 16:59:54 mschneider Exp $
/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2006 by: M.O.S.S. Computer Grafik Systeme GmbH
Hohenbrunner Weg 13
D-82024 Taufkirchen
http://www.moss.de/
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
---------------------------------------------------------------------------*/
package org.deegree.io.datastore.sde;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.io.datastore.DatastoreException;
import org.deegree.io.datastore.PropertyPathResolvingException;
import org.deegree.io.datastore.schema.MappedFeatureType;
import org.deegree.io.datastore.schema.MappedGeometryPropertyType;
import org.deegree.io.datastore.schema.MappedPropertyType;
import org.deegree.io.datastore.schema.MappedSimplePropertyType;
import org.deegree.io.datastore.schema.content.MappingField;
import org.deegree.io.datastore.schema.content.MappingGeometryField;
import org.deegree.io.datastore.schema.content.SimpleContent;
import org.deegree.io.datastore.sql.TableAliasGenerator;
import org.deegree.io.datastore.sql.wherebuilder.GeometryPropertyNode;
import org.deegree.io.datastore.sql.wherebuilder.PropertyNode;
import org.deegree.io.datastore.sql.wherebuilder.QueryTableTree;
import org.deegree.io.datastore.sql.wherebuilder.SimplePropertyNode;
import org.deegree.io.sdeapi.SDEAdapter;
import org.deegree.model.filterencoding.ArithmeticExpression;
import org.deegree.model.filterencoding.ComparisonOperation;
import org.deegree.model.filterencoding.ComplexFilter;
import org.deegree.model.filterencoding.Expression;
import org.deegree.model.filterencoding.ExpressionDefines;
import org.deegree.model.filterencoding.FeatureFilter;
import org.deegree.model.filterencoding.Filter;
import org.deegree.model.filterencoding.FilterTools;
import org.deegree.model.filterencoding.Function;
import org.deegree.model.filterencoding.Literal;
import org.deegree.model.filterencoding.LogicalOperation;
import org.deegree.model.filterencoding.Operation;
import org.deegree.model.filterencoding.OperationDefines;
import org.deegree.model.filterencoding.PropertyIsBetweenOperation;
import org.deegree.model.filterencoding.PropertyIsCOMPOperation;
import org.deegree.model.filterencoding.PropertyIsLikeOperation;
import org.deegree.model.filterencoding.PropertyIsNullOperation;
import org.deegree.model.filterencoding.PropertyName;
import org.deegree.model.filterencoding.SpatialOperation;
import org.deegree.ogcbase.PropertyPath;
import com.esri.sde.sdk.client.SeCoordinateReference;
import com.esri.sde.sdk.client.SeFilter;
import com.esri.sde.sdk.client.SeLayer;
import com.esri.sde.sdk.client.SeShape;
import com.esri.sde.sdk.client.SeShapeFilter;
/**
* <code>WhereBuilder</code> implementation for ArcSDE.
*
* @author <a href="mailto:cpollmann@moss.de">Christoph Pollmann</a>
*
* @author last edited by: $Author: mschneider $
*
* @version 2.0, $Revision: 1.12 $
*
* @since 2.0
*/
public class SDEWhereBuilder {
protected static final ILogger LOG = LoggerFactory.getLogger( SDEWhereBuilder.class );
protected MappedFeatureType rootFeatureType;
protected Filter filter;
protected QueryTableTree queryTableTree;
protected List<PropertyPath> filterPropertyPaths = new ArrayList<PropertyPath>();
/**
* Creates a new instance of <code>SDEWhereBuilder</code> for the given <code>Query</code>.
*
* @param rootFeatureType
* @param filter
* @param aliasGenerator
* @throws DatastoreException
*/
public SDEWhereBuilder( MappedFeatureType rootFeatureType, Filter filter,
TableAliasGenerator aliasGenerator ) throws DatastoreException {
this.rootFeatureType = rootFeatureType;
this.queryTableTree = new QueryTableTree( rootFeatureType, aliasGenerator );
this.filter = filter;
if ( filter != null ) {
if ( !( filter instanceof ComplexFilter || filter instanceof FeatureFilter ) ) {
throw new DatastoreException(
"Invalid filter type: '"
+ filter.getClass()
+ "'. Filter must be a ComplexFilter or a FeatureFilter." );
}
buildFilterPropertyNameMap();
for ( PropertyPath property : this.filterPropertyPaths ) {
this.queryTableTree.addFilterProperty( property );
}
}
}
public String getRootTableAlias() {
return this.queryTableTree.getRootAlias();
}
public Filter getFilter() {
return this.filter;
}
/**
* Returns the internal (database specific) SRS code used in the geometry field of the given
* <code>SpatialOperation</code>.
*
* @param operation
* <code>SpatialOperation</code> for which the internal SRS is needed
* @return the internal (database specific) SRS code.
*/
protected int getInternalSRS( SpatialOperation operation ) {
PropertyPath propertyPath = operation.getPropertyName().getValue();
PropertyNode propertyNode = this.queryTableTree.getPropertyNode( propertyPath );
if ( propertyNode == null ) {
String msg = "Internal error in WhereBuilder: no PropertyNode for path '"
+ propertyPath + "' in QueryTableTree.";
LOG.logError( msg );
throw new RuntimeException( msg );
} else if ( !( propertyNode instanceof GeometryPropertyNode ) ) {
String msg = "Internal error in WhereBuilder: unexpected PropertyNode type: '"
+ propertyNode.getClass().getName() + "'. Must be a GeometryPropertyNode.";
LOG.logError( msg );
throw new RuntimeException( msg );
}
MappedGeometryPropertyType gpc = (MappedGeometryPropertyType) propertyNode.getProperty();
MappingGeometryField field = gpc.getMappingField();
return field.getSRS();
}
protected int getPropertyNameSQLType( PropertyName propertyName ) {
PropertyPath propertyPath = propertyName.getValue();
PropertyNode propertyNode = this.queryTableTree.getPropertyNode( propertyPath );
if ( propertyNode == null ) {
String msg = "Internal error in WhereBuilder: no PropertyNode for path '"
+ propertyPath + "' in QueryTableTree.";
LOG.logError( msg );
throw new RuntimeException( msg );
}
MappedPropertyType propertyType = propertyNode.getProperty();
if ( !( propertyType instanceof MappedSimplePropertyType ) ) {
String msg = "Error in WhereBuilder: cannot compare against properties of type '"
+ propertyType.getClass() + "'.";
LOG.logError( msg );
throw new RuntimeException( msg );
}
SimpleContent content = ( (MappedSimplePropertyType) propertyType ).getContent();
if ( !( content instanceof MappingField ) ) {
String msg = "Virtual properties are currently ignored in SDEWhereBuilder#getPropertyNameSQLType(PropertyName).";
LOG.logError( msg );
return Types.VARCHAR;
}
int targetSqlType = ( (MappingField) content ).getType();
return targetSqlType;
}
protected void buildFilterPropertyNameMap()
throws PropertyPathResolvingException {
if ( this.filter instanceof ComplexFilter ) {
buildPropertyNameMapFromOperation( ( (ComplexFilter) this.filter ).getOperation() );
} else if ( this.filter instanceof FeatureFilter ) {
// FeatureFilter doesn't have real properties, so we don't have to add them here
// maybe for join tables and table aliases we need some auxiliary constructions???
//throw new PropertyPathResolvingException( "FeatureFilter not implemented yet." );
}
}
private void buildPropertyNameMapFromOperation( Operation operation )
throws PropertyPathResolvingException {
switch ( OperationDefines.getTypeById( operation.getOperatorId() ) ) {
case OperationDefines.TYPE_SPATIAL: {
registerPropertyName( ( (SpatialOperation) operation ).getPropertyName() );
break;
}
case OperationDefines.TYPE_COMPARISON: {
buildPropertyNameMap( (ComparisonOperation) operation );
break;
}
case OperationDefines.TYPE_LOGICAL: {
buildPropertyNameMap( (LogicalOperation) operation );
break;
}
default: {
break;
}
}
}
private void buildPropertyNameMap( ComparisonOperation operation )
throws PropertyPathResolvingException {
switch ( operation.getOperatorId() ) {
case OperationDefines.PROPERTYISEQUALTO:
case OperationDefines.PROPERTYISLESSTHAN:
case OperationDefines.PROPERTYISGREATERTHAN:
case OperationDefines.PROPERTYISLESSTHANOREQUALTO:
case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: {
buildPropertyNameMap( ( (PropertyIsCOMPOperation) operation ).getFirstExpression() );
buildPropertyNameMap( ( (PropertyIsCOMPOperation) operation ).getSecondExpression() );
break;
}
case OperationDefines.PROPERTYISLIKE: {
registerPropertyName( ( (PropertyIsLikeOperation) operation ).getPropertyName() );
break;
}
case OperationDefines.PROPERTYISNULL: {
buildPropertyNameMap( ( (PropertyIsNullOperation) operation ).getPropertyName() );
break;
}
case OperationDefines.PROPERTYISBETWEEN: {
buildPropertyNameMap( ( (PropertyIsBetweenOperation) operation ).getLowerBoundary() );
buildPropertyNameMap( ( (PropertyIsBetweenOperation) operation ).getUpperBoundary() );
registerPropertyName( ( (PropertyIsBetweenOperation) operation ).getPropertyName() );
break;
}
default: {
break;
}
}
}
private void buildPropertyNameMap( LogicalOperation operation )
throws PropertyPathResolvingException {
List operationList = operation.getArguments();
Iterator it = operationList.iterator();
while ( it.hasNext() ) {
buildPropertyNameMapFromOperation( (Operation) it.next() );
}
}
private void buildPropertyNameMap( Expression expression )
throws PropertyPathResolvingException {
switch ( expression.getExpressionId() ) {
case ExpressionDefines.PROPERTYNAME: {
registerPropertyName( (PropertyName) expression );
break;
}
case ExpressionDefines.ADD:
case ExpressionDefines.SUB:
case ExpressionDefines.MUL:
case ExpressionDefines.DIV: {
buildPropertyNameMap( ( (ArithmeticExpression) expression ).getFirstExpression() );
buildPropertyNameMap( ( (ArithmeticExpression) expression ).getSecondExpression() );
break;
}
case ExpressionDefines.FUNCTION: {
// TODO: What about PropertyNames used here?
break;
}
case ExpressionDefines.EXPRESSION:
case ExpressionDefines.LITERAL: {
break;
}
}
}
private void registerPropertyName( PropertyName propertyName ) {
this.filterPropertyPaths.add( propertyName.getValue() );
}
/*
appendJoinTableList => String[] der Tabellennamen (mehrfach vorkommende Namen nicht erlaubt)
appendOuterJoins => mit SDE bei versionierten Tabellen realisierbar???
*/
/**
* Appends the SQL condition from the <code>Filter</code> to the given sql statement.
*
* @param query
*/
public final void appendWhereCondition( StringBuffer whereCondition ) {
if ( filter instanceof ComplexFilter ) {
appendComplexFilterAsSQL( whereCondition, (ComplexFilter) filter );
} else if ( filter instanceof FeatureFilter ) {
FeatureFilter featureFilter = (FeatureFilter) filter;
if ( featureFilter.getFeatureIds().size() > 0 ) {
appendFeatureFilterAsSQL( whereCondition, featureFilter );
}
} else {
// assert false : "Unexpected filter type.";
}
}
/**
* Appends an SQL fragment for the given object.
*
* @param query
* @param filter
*/
protected void appendComplexFilterAsSQL( StringBuffer query, ComplexFilter filter ) {
appendOperationAsSQL( query, filter.getOperation() );
}
/**
* Appends an SQL fragment for the given object to the given sql statement.
*/
protected void appendOperationAsSQL( StringBuffer query, Operation operation ) {
switch ( OperationDefines.getTypeById( operation.getOperatorId() ) ) {
case OperationDefines.TYPE_SPATIAL: {
// handled seperately with buildSpatialFilter()
break;
}
case OperationDefines.TYPE_COMPARISON: {
appendComparisonOperationAsSQL( query, (ComparisonOperation) operation );
break;
}
case OperationDefines.TYPE_LOGICAL: {
appendLogicalOperationAsSQL( query, (LogicalOperation) operation );
break;
}
default: {
break;
}
}
}
/**
* Appends an SQL fragment for the given object to the given sql statement.
*
* @param query
* @param operation
*/
protected void appendComparisonOperationAsSQL( StringBuffer query, ComparisonOperation operation ) {
switch ( operation.getOperatorId() ) {
case OperationDefines.PROPERTYISEQUALTO:
case OperationDefines.PROPERTYISLESSTHAN:
case OperationDefines.PROPERTYISGREATERTHAN:
case OperationDefines.PROPERTYISLESSTHANOREQUALTO:
case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: {
appendPropertyIsCOMPOperationAsSQL( query, (PropertyIsCOMPOperation) operation );
break;
}
case OperationDefines.PROPERTYISLIKE: {
appendPropertyIsLikeOperationAsSQL( query, (PropertyIsLikeOperation) operation );
break;
}
case OperationDefines.PROPERTYISNULL: {
appendPropertyIsNullOperationAsSQL( query, (PropertyIsNullOperation) operation );
break;
}
case OperationDefines.PROPERTYISBETWEEN: {
appendPropertyIsBetweenOperationAsSQL( query, (PropertyIsBetweenOperation) operation );
break;
}
}
}
/**
* Appends an SQL fragment for the given object to the given sql statement.
*/
protected void appendPropertyIsCOMPOperationAsSQL( StringBuffer query,
PropertyIsCOMPOperation operation ) {
Expression firstExpr = operation.getFirstExpression();
if ( !( firstExpr instanceof PropertyName ) ) {
throw new IllegalArgumentException( "First expression in a comparison must "
+ "always be a 'PropertyName' element." );
}
int targetSqlType = getPropertyNameSQLType( (PropertyName) firstExpr );
if ( operation.isMatchCase() ) {
appendExpressionAsSQL( query, firstExpr, targetSqlType );
} else {
List list = new ArrayList();
list.add( firstExpr );
Function func = new Function( "LOWER", list );
appendFunctionAsSQL( query, func, targetSqlType );
}
switch ( operation.getOperatorId() ) {
case OperationDefines.PROPERTYISEQUALTO: {
query.append( " = " );
break;
}
case OperationDefines.PROPERTYISLESSTHAN: {
query.append( " < " );
break;
}
case OperationDefines.PROPERTYISGREATERTHAN: {
query.append( " > " );
break;
}
case OperationDefines.PROPERTYISLESSTHANOREQUALTO: {
query.append( " <= " );
break;
}
case OperationDefines.PROPERTYISGREATERTHANOREQUALTO: {
query.append( " >= " );
break;
}
}
if ( operation.isMatchCase() ) {
appendExpressionAsSQL( query, operation.getSecondExpression(), targetSqlType );
} else {
List list = new ArrayList();
list.add( operation.getSecondExpression() );
Function func = new Function( "LOWER", list );
appendFunctionAsSQL( query, func, targetSqlType );
}
}
/**
* Appends an SQL fragment for the given object to the given sql statement. Replacing and escape
* handling is based on a finite automaton with 2 states:
* <p>
* (escapeMode)
* <ul>
* <li>' is appended as \', \ is appended as \\</li>
* <li>every character (including the escapeChar) is simply appended</li>
* <li>- unset escapeMode</li>
* (escapeMode is false)
* </ul>
* <ul>
* <li>' is appended as \', \ is appended as \\</li>
* <li>escapeChar means: skip char, set escapeMode</li>
* <li>wildCard means: append %</li>
* <li>singleChar means: append ?</li>
* </ul>
* </p>
*
* NOTE: Currently, the method uses a quirk and appends the generated argument inline, i.e. not
* using query.addArgument(). This is because of a problem that occurred for example in
* Postgresql; the execution of the inline version is *much* faster (at least with version 8.0).
*/
protected void appendPropertyIsLikeOperationAsSQL( StringBuffer query,
PropertyIsLikeOperation operation ) {
String literal = operation.getLiteral().getValue();
char escapeChar = operation.getEscapeChar();
char wildCard = operation.getWildCard();
char singleChar = operation.getSingleChar();
boolean escapeMode = false;
int length = literal.length();
int targetSqlType = getPropertyNameSQLType( operation.getPropertyName() );
if ( operation.isMatchCase() ) {
appendPropertyNameAsSQL( query, operation.getPropertyName() );
} else {
List list = new ArrayList();
list.add( operation.getPropertyName() );
Function func = new Function( "LOWER", list );
appendFunctionAsSQL( query, func, targetSqlType );
}
query.append( " LIKE '" );
StringBuffer parameter = new StringBuffer();
for ( int i = 0; i < length; i++ ) {
char c = literal.charAt( i );
if ( escapeMode ) {
// ' must (even in escapeMode) be converted to \'
if ( c == '\'' )
parameter.append( "\'" );
// \ must (even in escapeMode) be converted to \\
else if ( c == '\\' )
parameter.append( "\\\\" );
else
parameter.append( c );
escapeMode = false;
} else {
// escapeChar means: switch to escapeMode
if ( c == escapeChar )
escapeMode = true;
// wildCard must be converted to %
else if ( c == wildCard )
parameter.append( '%' );
// singleChar must be converted to ?
else if ( c == singleChar )
parameter.append( '?' );
// ' must be converted to \'
else if ( c == '\'' )
parameter.append( "$'$" );
// % must be converted to \'
else if ( c == '%' )
parameter.append( "$%$" );
// ? must be converted to \'
// else if (c == '?') sb.append("$?$");
// \ must (even in escapeMode) be converted to \\
else if ( c == '\\' )
parameter.append( "\\\\" );
else
parameter.append( c );
}
}
if ( operation.isMatchCase() ) {
query.append( parameter );
} else {
query.append( parameter.toString().toLowerCase() );
}
query.append( '\'' );
// query.addArgument( parameter.toString() );
}
/**
* Appends an SQL fragment for the given object to the given sql statement.
*/
protected void appendPropertyIsNullOperationAsSQL( StringBuffer query,
PropertyIsNullOperation operation ) {
appendPropertyNameAsSQL( query, operation.getPropertyName() );
query.append( " IS NULL" );
}
/**
* Appends an SQL fragment for the given object to the given sql statement.
*/
protected void appendPropertyIsBetweenOperationAsSQL( StringBuffer query,
PropertyIsBetweenOperation operation ) {
PropertyName propertyName = operation.getPropertyName();
int targetSqlType = getPropertyNameSQLType( propertyName );
appendExpressionAsSQL( query, operation.getLowerBoundary(), targetSqlType );
query.append( " <= " );
appendPropertyNameAsSQL( query, propertyName );
query.append( " AND " );
appendPropertyNameAsSQL( query, propertyName );
query.append( " <= " );
appendExpressionAsSQL( query, operation.getUpperBoundary(), targetSqlType );
}
/**
* Appends an SQL fragment for the given object to the given sql statement.
*
* @param query
* @param expression
* @param targetSqlType
* sql type code to be used for literals at the bottom of the expression tree
*/
protected void appendExpressionAsSQL( StringBuffer query, Expression expression,
int targetSqlType ) {
switch ( expression.getExpressionId() ) {
case ExpressionDefines.PROPERTYNAME: {
appendPropertyNameAsSQL( query, (PropertyName) expression );
break;
}
case ExpressionDefines.LITERAL: {
appendLiteralAsSQL( query, (Literal) expression, targetSqlType );
break;
}
case ExpressionDefines.FUNCTION: {
Function function = (Function) expression;
appendFunctionAsSQL( query, function, targetSqlType );
break;
}
case ExpressionDefines.ADD:
case ExpressionDefines.SUB:
case ExpressionDefines.MUL:
case ExpressionDefines.DIV: {
appendArithmeticExpressionAsSQL( query, (ArithmeticExpression) expression,
targetSqlType );
break;
}
case ExpressionDefines.EXPRESSION:
default: {
throw new IllegalArgumentException( "Unexpected expression type: "
+ expression.getExpressionName() );
}
}
}
/**
* Appends an SQL fragment for the given object to the given sql statement.
*
* @param query
* @param literal
* @param targetSqlType
*/
protected void appendLiteralAsSQL( StringBuffer query, Literal literal, int targetSqlType ) {
switch ( targetSqlType ) {
case java.sql.Types.DECIMAL:
case java.sql.Types.DOUBLE:
case java.sql.Types.FLOAT:
case java.sql.Types.INTEGER:
case java.sql.Types.NUMERIC:
case java.sql.Types.REAL:
case java.sql.Types.SMALLINT:
case java.sql.Types.TINYINT:
query.append( literal.getValue() );
break;
default:
query.append( "'" + literal.getValue() + "'" );
break;
}
}
/**
* Appends an SQL fragment for the given object to the given sql statement.
*/
protected MappingField getPropertyNameMapping( PropertyName propertyName ) {
PropertyPath propertyPath = propertyName.getValue();
LOG.logDebug( "Looking up '" + propertyPath + "' in the query table tree." );
MappingField mappingField = null;
PropertyNode propertyNode = this.queryTableTree.getPropertyNode( propertyPath );
if ( propertyNode == null ) {
String msg = "Internal error in WhereBuilder: no PropertyNode for path '"
+ propertyPath + "' in QueryTableTree.";
LOG.logError( msg );
throw new RuntimeException( msg );
} else if ( propertyNode instanceof SimplePropertyNode ) {
SimpleContent content = ( (MappedSimplePropertyType) ( propertyNode.getProperty() ) ).getContent();
if (!(content instanceof MappingField)) {
String msg = "Virtual properties are currently ignored in WhereBuilder#appendPropertyPathAsSQL(StatementBuffer,PropertyPath).";
LOG.logError (msg);
throw new RuntimeException (msg);
}
mappingField = (MappingField) content;
} else if ( propertyNode instanceof GeometryPropertyNode ) {
mappingField = ( (MappedGeometryPropertyType) propertyNode.getProperty()).getMappingField();
} else {
String msg = "Internal error in WhereBuilder: unhandled PropertyNode type: '"
+ propertyNode.getClass().getName() + "'.";
LOG.logError( msg );
throw new RuntimeException( msg );
}
return mappingField;
}
/**
* Appends an SQL fragment for the given object to the given sql statement.
*/
protected void appendPropertyNameAsSQL( StringBuffer query, PropertyName propertyName ) {
MappingField mappingField = getPropertyNameMapping( propertyName );
//with ArcSDE because of versioning not applicable
//query.append( mappingField.getTable() );
//query.append( '.' );
query.append( mappingField.getField() );
}
/**
* Appends an SQL fragment for the given object to the given sql statement.
*/
protected void appendArithmeticExpressionAsSQL( StringBuffer query,
ArithmeticExpression expression,
int targetSqlType ) {
query.append( '(' );
appendExpressionAsSQL( query, expression.getFirstExpression(), targetSqlType );
switch ( expression.getExpressionId() ) {
case ExpressionDefines.ADD: {
query.append( '+' );
break;
}
case ExpressionDefines.SUB: {
query.append( '-' );
break;
}
case ExpressionDefines.MUL: {
query.append( '*' );
break;
}
case ExpressionDefines.DIV: {
query.append( '/' );
break;
}
}
appendExpressionAsSQL( query, expression.getSecondExpression(), targetSqlType );
query.append( ')' );
}
/**
* Appends an SQL fragment for the given object to the given sql statement.
*/
protected void appendFunctionAsSQL( StringBuffer query, Function function, int targetSqlType ) {
query.append( function.getName() );
query.append( " (" );
List list = function.getArguments();
for ( int i = 0; i < list.size(); i++ ) {
Expression expression = (Expression) list.get( i );
appendExpressionAsSQL( query, expression, targetSqlType );
if ( i != list.size() - 1 )
query.append( ", " );
}
query.append( ")" );
}
/**
* Appends an SQL fragment for the given object to the given sql statement.
*/
protected void appendLogicalOperationAsSQL( StringBuffer query, LogicalOperation operation ) {
List argumentList = operation.getArguments();
switch ( operation.getOperatorId() ) {
case OperationDefines.AND: {
for ( int i = 0; i < argumentList.size(); i++ ) {
Operation argument = (Operation) argumentList.get( i );
query.append( '(' );
appendOperationAsSQL( query, argument );
query.append( ')' );
if ( i != argumentList.size() - 1 )
query.append( " AND " );
}
break;
}
case OperationDefines.OR: {
for ( int i = 0; i < argumentList.size(); i++ ) {
Operation argument = (Operation) argumentList.get( i );
query.append( '(' );
appendOperationAsSQL( query, argument );
query.append( ')' );
if ( i != argumentList.size() - 1 )
query.append( " OR " );
}
break;
}
case OperationDefines.NOT: {
Operation argument = (Operation) argumentList.get( 0 );
query.append( "NOT (" );
appendOperationAsSQL( query, argument );
query.append( ')' );
break;
}
}
}
/**
* Appends an SQL fragment for the given object to the given sql statement.
*
* TODO Handle compound primary keys correctly.
*/
protected void appendFeatureFilterAsSQL( StringBuffer query, FeatureFilter filter ) {
ArrayList list = filter.getFeatureIds();
MappingField mapping = rootFeatureType.getGMLId().getIdFields()[0];
String quote = "";
switch ( mapping.getType() ) {
case java.sql.Types.DECIMAL:
case java.sql.Types.DOUBLE:
case java.sql.Types.FLOAT:
case java.sql.Types.INTEGER:
case java.sql.Types.NUMERIC:
case java.sql.Types.REAL:
case java.sql.Types.SMALLINT:
case java.sql.Types.TINYINT:
break;
default:
quote = "'";
break;
}
query.append( ' ' );
query.append( mapping.getField() );
try {
for ( int i = 0; i < list.size(); i++ ) {
if ( 0 == i )
query.append( " IN (" + quote );
else
query.append( quote + "," + quote );
String fid = ( (org.deegree.model.filterencoding.FeatureId) list.get( i ) ).getValue();
Object fidValue = org.deegree.io.datastore.FeatureId.removeFIDPrefix(
fid,
rootFeatureType.getGMLId() );
query.append( fidValue.toString() );
}
} catch ( Exception e ) {
LOG.logError( "Error converting feature id", e );
}
query.append( quote + ")" );
}
/**
* Generates an SQL-fragment for the given object.
*
* @throws DatastoreException
*/
protected SeFilter[] buildSpatialFilter( ComplexFilter filter, List layers )
throws DatastoreException {
SpatialOperation[] spatialOps = FilterTools.extractSpatialFilter( filter );
if ( null == spatialOps || 0 == spatialOps.length )
return null;
SeFilter[] spatialFilter = new SeFilter[spatialOps.length];
for ( int i = 0; i < spatialOps.length; i++ ) {
try {
MappingField mappingField = getPropertyNameMapping( spatialOps[i].getPropertyName() );
String filterTable = mappingField.getTable();
String filterColumn = mappingField.getField();
SeCoordinateReference coordRef = null;
String[] splitted = filterTable.toUpperCase().split( "\\." );
String tmp = splitted[splitted.length - 1];
for ( int k = 0; k < layers.size(); k++ ) {
SeLayer layer = (SeLayer) layers.get( k );
splitted = layer.getName().toUpperCase().split( "\\." );
if ( splitted[splitted.length - 1].equals( tmp ) ) {
coordRef = layer.getCoordRef();
break;
}
}
if ( null == coordRef ) {
coordRef = new SeCoordinateReference();
}
int filterMethod = -1;
boolean filterTruth = true;
switch ( spatialOps[i].getOperatorId() ) {
case OperationDefines.CROSSES: {
filterMethod = SeFilter.METHOD_LCROSS;
break;
}
case OperationDefines.EQUALS: {
filterMethod = SeFilter.METHOD_IDENTICAL;
break;
}
case OperationDefines.WITHIN: {
filterMethod = SeFilter.METHOD_SC_NO_ET;
break;
}
case OperationDefines.OVERLAPS: {
filterMethod = SeFilter.METHOD_ENVP;
break;
}
case OperationDefines.TOUCHES: {
filterMethod = SeFilter.METHOD_ET_OR_AI;
break;
}
case OperationDefines.DISJOINT: {
filterMethod = SeFilter.METHOD_SC_NO_ET;
filterTruth = false;
break;
}
case OperationDefines.INTERSECTS: {
filterMethod = SeFilter.METHOD_AI;
break;
}
case OperationDefines.CONTAINS: {
filterMethod = SeFilter.METHOD_AI_OR_ET;
break;
}
case OperationDefines.BBOX: {
filterMethod = SeFilter.METHOD_ENVP;
break;
}
case OperationDefines.DWITHIN:
case OperationDefines.BEYOND:
default: {
continue;
}
}
SeShape filterGeom = SDEAdapter.export( spatialOps[i].getGeometry(), coordRef );
spatialFilter[i] = new SeShapeFilter( filterTable, filterColumn, filterGeom,
filterMethod, filterTruth );
} catch ( Exception e ) {
e.printStackTrace();
throw new DatastoreException( "Error creating spatial filter", e );
}
}
return spatialFilter;
}
}
/***************************************************************************************************
* Changes to this class. What the people have been up to:
* $Log: SDEWhereBuilder.java,v $
* Revision 1.12 2006/11/29 16:59:54 mschneider
* Improved handling of native coordinate transformation.
*
* Revision 1.11 2006/09/05 14:44:54 mschneider
* Adapted to changes in QueryTableTree.
*
* Revision 1.10 2006/09/04 14:16:46 mschneider
* Adapted due to changes in QueryTableTree.
*
* Revision 1.9 2006/08/23 16:34:06 mschneider
* Added handling of virtual properties. Needs testing.
*
* Revision 1.8 2006/08/22 18:14:42 mschneider
* Refactored due to cleanup of org.deegree.io.datastore.schema package.
*
* Revision 1.7 2006/08/21 16:42:36 mschneider
* Refactored due to cleanup (and splitting) of org.deegree.io.datastore.schema package.
*
* Revision 1.6 2006/08/21 15:44:38 mschneider
* Refactored due to cleanup (and splitting) of org.deegree.io.datastore.schema package.
*
* Revision 1.5 2006/08/15 17:39:34 mschneider
* Removed (unnecessary) #getFilterPropertyPaths().
*
* Revision 1.4 2006/08/06 20:38:51 poth
* never thrown exceptions and never read variables removed
*
* Revision 1.2 2006/06/25 08:00:00 poth
* bug fix - using uppercase characters for case insensitive searches caused problems with '�' so now lower case characters are used
*
* Revision 1.1 2006/05/21 19:06:21 poth
* initial load up
*
* Revision 1.1 2006/05/09 14:51:52 polli
* no message
*
**************************************************************************************************/