/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotools.jdbc;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.geotools.data.jdbc.FilterToSQL;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.filter.BinaryComparisonOperator;
import org.opengis.filter.Id;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.identity.Identifier;
import org.opengis.filter.spatial.BinarySpatialOperator;
import com.vividsolutions.jts.geom.Geometry;
/**
* Extension of FilterToSQL intended for use with prepared statements.
* <p>
* Each time a {@link Literal} is visited, a '?' is encoded, and the
* value and type of the literal are stored, available after the fact
* via {@link #getLiteralValues()} and {@link #getLiteralTypes()}.
*
* </p>
* @author Justin Deoliveira, OpenGEO
* @author Andrea Aime, OpenGEO
*
*
* @source $URL$
*/
public class PreparedFilterToSQL extends FilterToSQL {
/**
* ordered list of literal values encountered and their types
*/
protected List<Object> literalValues = new ArrayList<Object>();
protected List<Class> literalTypes = new ArrayList<Class>();
protected List<Integer> SRIDs = new ArrayList<Integer>();
protected PreparedStatementSQLDialect dialect;
boolean prepareEnabled = true;
/**
* Default constructor
* @deprecated Use {@link PreparedFilterToSQL(PreparedStatementSQLDialect)} instead
*/
public PreparedFilterToSQL() {
this.dialect = null;
}
/**
* Contructor taking a reference to the SQL dialect, will use it to
* encode geometry placeholders
* @param dialect
*/
public PreparedFilterToSQL(PreparedStatementSQLDialect dialect) {
this.dialect = dialect;
}
/**
* If true (default) a sql statement with literal placemarks is created, otherwise
* a normal statement is created
* @return
*/
public boolean isPrepareEnabled() {
return prepareEnabled;
}
public void setPrepareEnabled(boolean prepareEnabled) {
this.prepareEnabled = prepareEnabled;
}
public PreparedFilterToSQL(Writer out) {
super(out);
}
public Object visit(Literal expression, Object context)
throws RuntimeException {
if(!prepareEnabled)
return super.visit(expression, context);
// evaluate the literal and store it for later
Object literalValue = evaluateLiteral( expression, (context instanceof Class ? (Class) context : null) );
literalValues.add(literalValue);
SRIDs.add(currentSRID);
Class clazz = null;
if(context instanceof Class)
clazz = (Class) context;
else if(literalValue != null)
clazz = literalValue.getClass();
literalTypes.add( clazz );
try {
if ( literalValue == null || dialect == null ) {
out.write( "?" );
}
else {
StringBuffer sb = new StringBuffer();
if ( Geometry.class.isAssignableFrom(literalValue.getClass()) ) {
int srid = currentSRID != null ? currentSRID : -1;
dialect.prepareGeometryValue((Geometry) literalValue, srid, Geometry.class, sb);
}
else if ( encodingFunction ) {
dialect.prepareFunctionArgument(clazz,sb);
}
else {
sb.append("?");
}
out.write( sb.toString() );
}
}
catch (IOException e) {
throw new RuntimeException( e );
}
return context;
}
/**
* Encodes an Id filter
*
* @param filter the
*
* @throws RuntimeException If there's a problem writing output
*
*/
public Object visit(Id filter, Object extraData) {
if (mapper == null) {
throw new RuntimeException(
"Must set a fid mapper before trying to encode FIDFilters");
}
Set ids = filter.getIdentifiers();
// prepare column name array
String[] colNames = new String[mapper.getColumnCount()];
for (int i = 0; i < colNames.length; i++) {
colNames[i] = mapper.getColumnName(i);
}
for (Iterator i = ids.iterator(); i.hasNext(); ) {
try {
Identifier id = (Identifier) i.next();
Object[] attValues = mapper.getPKAttributes(id.toString());
out.write("(");
for (int j = 0; j < attValues.length; j++) {
out.write( escapeName(colNames[j]) );
out.write(" = ");
out.write('?');
// store the value for later usage
literalValues.add(attValues[j]);
// no srid, pk are not formed with geometry values
SRIDs.add(-1);
// if it's not null, we can also infer the type
literalTypes.add(attValues[j] != null ? attValues[j].getClass() : null);
if (j < (attValues.length - 1)) {
out.write(" AND ");
}
}
out.write(")");
if (i.hasNext()) {
out.write(" OR ");
}
} catch (java.io.IOException e) {
throw new RuntimeException(IO_ERROR, e);
}
}
return extraData;
}
public List<Object> getLiteralValues() {
return literalValues;
}
public List<Class> getLiteralTypes() {
return literalTypes;
}
/**
* Returns the list of native SRID for each literal that happens to be a geometry, or null otherwise
* @return
*/
public List<Integer> getSRIDs() {
return SRIDs;
}
}