/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.spatial.testing;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import org.jboss.logging.Logger;
import org.hibernate.spatial.HSMessageLogger;
/**
* An <code>AbstractExpectationsFactory</code> provides the expected
* values to be used in the integration tests of the spatial functions
* provided by specific providers.
* <p/>
* The expected values are returned as a map of (identifier, expected value) pairs.
*
* @author Karel Maesen, Geovise BVBA
*/
public abstract class AbstractExpectationsFactory {
private static final HSMessageLogger LOG = Logger.getMessageLogger(
HSMessageLogger.class,
AbstractExpectationsFactory.class.getName()
);
public final static String TEST_POLYGON_WKT = "POLYGON((0 0, 50 0, 100 100, 0 100, 0 0))";
public final static String TEST_POINT_WKT = "POINT(0 0)";
public final static int INTEGER = 1;
public final static int DOUBLE = 2;
public final static int GEOMETRY = 3;
public final static int STRING = 4;
public final static int BOOLEAN = 5;
public final static int OBJECT = -1;
private final static int TEST_SRID = 4326;
private final DataSourceUtils dataSourceUtils;
private static final int MAX_BYTE_LEN = 1024;
public AbstractExpectationsFactory(DataSourceUtils dataSourceUtils) {
this.dataSourceUtils = dataSourceUtils;
}
protected DataSourceUtils getDataSourceUtils() {
return this.dataSourceUtils;
}
/**
* Returns the SRID in which all tests are conducted. This is for now 4326;
*
* @return
*/
public int getTestSrid() {
return TEST_SRID;
}
/**
* Returns the expected dimensions of all testsuite-suite geometries.
*
* @return map of identifier, dimension
*
* @throws SQLException
*/
public Map<Integer, Integer> getDimension() throws SQLException {
return retrieveExpected( createNativeDimensionSQL(), INTEGER );
}
/**
* Returns the expected WKT of all testsuite-suite geometries.
*
* @return map of identifier, WKT-string
*
* @throws SQLException
*/
public Map<Integer, String> getAsText() throws SQLException {
return retrieveExpected( createNativeAsTextStatement(), STRING );
}
/**
* Returns the expected WKB representations of all testsuite-suite geometries
*
* @return map of identifier, WKB representation
*
* @throws SQLException
*/
public Map<Integer, byte[]> getAsBinary() throws SQLException {
return retrieveExpected( createNativeAsBinaryStatement(), OBJECT );
}
/**
* Returns the expected type names of all testsuite-suite geometries
*
* @return map of identifier, type name
*
* @throws SQLException
*/
public Map<Integer, String> getGeometryType() throws SQLException {
return retrieveExpected( createNativeGeometryTypeStatement(), STRING );
}
/**
* Returns the expected SRID codes of all testsuite-suite geometries
*
* @return map of identifier, SRID
*
* @throws SQLException
*/
public Map<Integer, Integer> getSrid() throws SQLException {
return retrieveExpected( createNativeSridStatement(), INTEGER );
}
/**
* Returns whether the testsuite-suite geometries are simple
*
* @return map of identifier and whether testsuite-suite geometry is simple
*
* @throws SQLException
*/
public Map<Integer, Boolean> getIsSimple() throws SQLException {
return retrieveExpected( createNativeIsSimpleStatement(), BOOLEAN );
}
/**
* Returns whether the testsuite-suite geometries are empty
*
* @return map of identifier and whether testsuite-suite geometry is empty
*
* @throws SQLException
*/
public Map<Integer, Boolean> getIsEmpty() throws SQLException {
return retrieveExpected( createNativeIsEmptyStatement(), BOOLEAN );
}
/**
* Returns whether the testsuite-suite geometries are empty
*
* @return map of identifier and whether testsuite-suite geometry is empty
*
* @throws SQLException
*/
public Map<Integer, Boolean> getIsNotEmpty() throws SQLException {
return retrieveExpected( createNativeIsNotEmptyStatement(), BOOLEAN );
}
/**
* Returns the expected boundaries of all testsuite-suite geometries
*
* @return map of identifier and boundary geometry
*
* @throws SQLException
*/
public Map<Integer, Geometry> getBoundary() throws SQLException {
return retrieveExpected( createNativeBoundaryStatement(), GEOMETRY );
}
/**
* Returns the expected envelopes of all testsuite-suite geometries
*
* @return map of identifier and envelope
*
* @throws SQLException
*/
public Map<Integer, Geometry> getEnvelope() throws SQLException {
return retrieveExpected( createNativeEnvelopeStatement(), GEOMETRY );
}
/**
* Returns the expected results of the within operator
*
* @param geom testsuite-suite geometry
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Boolean> getWithin(Geometry geom) throws SQLException {
return retrieveExpected( createNativeWithinStatement( geom ), BOOLEAN );
}
/**
* Returns the expected results of the equals operator
*
* @param geom
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Boolean> getEquals(Geometry geom) throws SQLException {
return retrieveExpected( createNativeEqualsStatement( geom ), BOOLEAN );
}
/**
* Returns the expected results of the crosses operator
*
* @param geom
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Boolean> getCrosses(Geometry geom) throws SQLException {
return retrieveExpected( createNativeCrossesStatement( geom ), BOOLEAN );
}
/**
* Returns the expected results of the contains operator
*/
public Map<Integer, Boolean> getContains(Geometry geom) throws SQLException {
return retrieveExpected( createNativeContainsStatement( geom ), BOOLEAN );
}
/**
* Returns the expected results of the disjoint operator
*
* @param geom
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Boolean> getDisjoint(Geometry geom) throws SQLException {
return retrieveExpected( createNativeDisjointStatement( geom ), BOOLEAN );
}
/**
* Returns the expected results of the intersects operator
*
* @param geom
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Boolean> getIntersects(Geometry geom) throws SQLException {
return retrieveExpected( createNativeIntersectsStatement( geom ), BOOLEAN );
}
/**
* Returns the expected results of the touches operator
*
* @param geom
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Boolean> getTouches(Geometry geom) throws SQLException {
return retrieveExpected( createNativeTouchesStatement( geom ), BOOLEAN );
}
/**
* Returns the expected results of the overlaps operator
*
* @param geom
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Boolean> getOverlaps(Geometry geom) throws SQLException {
return retrieveExpected( createNativeOverlapsStatement( geom ), BOOLEAN );
}
/**
* Returns the expected results of the DWithin operator
*
* @param geom
* @param distance
*
* @return
*/
public Map<Integer, Boolean> getDwithin(Point geom, double distance) throws SQLException {
return retrieveExpected( createNativeDwithinStatement( geom, distance ), BOOLEAN );
}
/**
* Returns the expected result of the havingSRID operator
*
* @param srid the SRID (EPSG code)
*
* @return
*/
public Map<Integer, Boolean> havingSRID(int srid) throws SQLException {
return retrieveExpected( createNativeHavingSRIDStatement( srid ), BOOLEAN );
}
/**
* Returns the expected results of the relate operator
*
* @param geom
* @param matrix
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Boolean> getRelate(Geometry geom, String matrix) throws SQLException {
return retrieveExpected( createNativeRelateStatement( geom, matrix ), BOOLEAN );
}
/**
* Returns the expected results for the geometry filter
*
* @param geom filter Geometry
*
* @return
*/
public Map<Integer, Boolean> getFilter(Geometry geom) throws SQLException {
return retrieveExpected( createNativeFilterStatement( geom ), BOOLEAN );
}
/**
* Returns the expected results of the distance function
*
* @param geom geometry parameter to distance function
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Double> getDistance(Geometry geom) throws SQLException {
return retrieveExpected( createNativeDistanceStatement( geom ), DOUBLE );
}
/**
* Returns the expected results of the buffering function
*
* @param distance distance parameter to the buffer function
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Geometry> getBuffer(Double distance) throws SQLException {
return retrieveExpected( createNativeBufferStatement( distance ), GEOMETRY );
}
/**
* Returns the expected results of the convexhull function
*
* @param geom geometry with which each testsuite-suite geometry is unioned before convexhull calculation
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Geometry> getConvexHull(Geometry geom) throws SQLException {
return retrieveExpected( createNativeConvexHullStatement( geom ), GEOMETRY );
}
/**
* Returns the expected results of the intersection function
*
* @param geom parameter to the intersection function
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Geometry> getIntersection(Geometry geom) throws SQLException {
return retrieveExpected( createNativeIntersectionStatement( geom ), GEOMETRY );
}
/**
* Returns the expected results of the difference function
*
* @param geom parameter to the difference function
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Geometry> getDifference(Geometry geom) throws SQLException {
return retrieveExpected( createNativeDifferenceStatement( geom ), GEOMETRY );
}
/**
* Returns the expected results of the symdifference function
*
* @param geom parameter to the symdifference function
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Geometry> getSymDifference(Geometry geom) throws SQLException {
return retrieveExpected( createNativeSymDifferenceStatement( geom ), GEOMETRY );
}
/**
* Returns the expected results of the geomunion function
*
* @param geom parameter to the geomunion function
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Geometry> getGeomUnion(Geometry geom) throws SQLException {
return retrieveExpected( createNativeGeomUnionStatement( geom ), GEOMETRY );
}
/**
* Returns the expected result of the transform function
*
* @param epsg
*
* @return
*
* @throws SQLException
*/
public Map<Integer, Geometry> getTransform(int epsg) throws SQLException {
return retrieveExpected( createNativeTransformStatement( epsg ), GEOMETRY );
}
/**
* Returns a statement corresponding to the HQL statement:
* "SELECT id, touches(geom, :filter) from GeomEntity where touches(geom, :filter) = true and srid(geom) = 4326"
*
* @param geom the geometry corresponding to the ':filter' query parameter
*
* @return
*/
protected abstract NativeSQLStatement createNativeTouchesStatement(Geometry geom);
/**
* Returns a statement corresponding to the HQL statement:
* "SELECT id, overlaps(geom, :filter) from GeomEntity where overlaps(geom, :filter) = true and srid(geom) = 4326"
*
* @param geom the geometry corresponding to the ':filter' query parameter
*
* @return
*/
protected abstract NativeSQLStatement createNativeOverlapsStatement(Geometry geom);
/**
* Returns a statement corresponding to the HQL statement:
* "SELECT id, relate(geom, :filter, :matrix) from GeomEntity where relate(geom, :filter, :matrix) = true and srid(geom) = 4326"
*
* @param geom the geometry corresponding to the ':filter' query parameter
* @param matrix the string corresponding to the ':matrix' query parameter
*
* @return
*/
protected abstract NativeSQLStatement createNativeRelateStatement(Geometry geom, String matrix);
/**
* Returns a statement corresponding to the HQL statement:
* "SELECT id, dwithin(geom, :filter, :distance) from GeomEntity where dwithin(geom, :filter, :distance) = true and srid(geom) = 4326"
*
* @param geom the geometry corresponding to the ':filter' query parameter
* @param distance the string corresponding to the ':distance' query parameter
*
* @return
*/
protected abstract NativeSQLStatement createNativeDwithinStatement(Point geom, double distance);
/**
* Returns a statement corresponding to the HQL statement:
* "SELECT id, intersects(geom, :filter) from GeomEntity where intersects(geom, :filter) = true and srid(geom) = 4326"
*
* @param geom the geometry corresponding to the ':filter' query parameter
*
* @return
*/
protected abstract NativeSQLStatement createNativeIntersectsStatement(Geometry geom);
/**
* Returns the statement corresponding to the SpatialRestrictions.filter() method.
*
* @param geom filter geometry
*
* @return
*/
protected abstract NativeSQLStatement createNativeFilterStatement(Geometry geom);
/**
* Returns a statement corresponding to the HQL statement:
* "SELECT id, distance(geom, :filter) from GeomEntity where srid(geom) = 4326"
*
* @param geom
*
* @return
*/
protected abstract NativeSQLStatement createNativeDistanceStatement(Geometry geom);
/**
* Returns a statement corresponding to the HQL statement:
* "select id, dimension(geom) from GeomEntity".
*
* @return the SQL String
*/
protected abstract NativeSQLStatement createNativeDimensionSQL();
/**
* Returns a statement corresponding to the HQL statement:
* "SELECT id, buffer(geom, :distance) from GeomEntity where srid(geom) = 4326"
*
* @param distance parameter corresponding to the ':distance' query parameter
*
* @return the native SQL Statement
*/
protected abstract NativeSQLStatement createNativeBufferStatement(Double distance);
/**
* Returns a statement corresponding to the HQL statement:
* "SELECT id, convexhull(geomunion(geom, :polygon)) from GeomEntity where srid(geom) = 4326"
*
* @param geom parameter corresponding to the ':polygon' query parameter
*
* @return
*/
protected abstract NativeSQLStatement createNativeConvexHullStatement(Geometry geom);
/**
* Returns a statement corresponding to the HQL statement:
* "SELECT id, intersection(geom, :polygon) from GeomEntity where srid(geom) = 4326"
*
* @param geom parameter corresponding to the ':polygon' query parameter
*
* @return
*/
protected abstract NativeSQLStatement createNativeIntersectionStatement(Geometry geom);
/**
* Returns a statement corresponding to the HQL statement:
* "SELECT id, difference(geom, :polygon) from GeomEntity where srid(geom) = 4326"26"
*
* @param geom parameter corresponding to the ':polygon' query parameter
*
* @return
*/
protected abstract NativeSQLStatement createNativeDifferenceStatement(Geometry geom);
/**
* Returns a statement corresponding to the HQL statement:
* "SELECT id, symdifference(geom, :polygon) from GeomEntity where srid(geom) = 4326"26"
*
* @param geom parameter corresponding to the ':polygon' query parameter
*
* @return
*/
protected abstract NativeSQLStatement createNativeSymDifferenceStatement(Geometry geom);
/**
* Returns a statement corresponding to the HQL statement:
* "SELECT id, geomunion(geom, :polygon) from GeomEntity where srid(geom) = 4326"26"
*
* @param geom parameter corresponding to the ':polygon' query parameter
*
* @return
*/
protected abstract NativeSQLStatement createNativeGeomUnionStatement(Geometry geom);
/**
* Returns a statement corresponding to the HQL statement:
* "select id, astext(geom) from GeomEntity".
*
* @return the native SQL Statement
*/
protected abstract NativeSQLStatement createNativeAsTextStatement();
/**
* Returns a statement corresponding to the HQL statement:
* "select id, srid(geom) from GeomEntity".
*
* @return the native SQL Statement
*/
protected abstract NativeSQLStatement createNativeSridStatement();
/**
* Returns a statement corresponding to the HQL statement:
* "select id, issimple(geom) from GeomEntity".
*
* @return the native SQL Statement
*/
protected abstract NativeSQLStatement createNativeIsSimpleStatement();
/**
* Returns a statement corresponding to the HQL statement:
* "select id, isempty(geom) from GeomEntity".
*
* @return the native SQL Statement
*/
protected abstract NativeSQLStatement createNativeIsEmptyStatement();
/**
* Returns a statement corresponding to the HQL statement:
* "select id,not isempty(geom) from GeomEntity".
*
* @return
*/
protected abstract NativeSQLStatement createNativeIsNotEmptyStatement();
/**
* Returns a statement corresponding to the HQL statement:
* "select id, boundary(geom) from GeomEntity".
*
* @return the native SQL Statement
*/
protected abstract NativeSQLStatement createNativeBoundaryStatement();
/**
* Returns a statement corresponding to the HQL statement:
* "select id, envelope(geom) from GeomEntity".
*
* @return the native SQL Statement
*/
protected abstract NativeSQLStatement createNativeEnvelopeStatement();
/**
* Returns a statement corresponding to the HQL statement:
* "select id, asbinary(geom) from GeomEntity".
*
* @return the native SQL Statement
*/
protected abstract NativeSQLStatement createNativeAsBinaryStatement();
/**
* Returns a statement corresponding to the HQL statement:
* "select id, geometrytype(geom) from GeomEntity".
*
* @return the SQL String
*/
protected abstract NativeSQLStatement createNativeGeometryTypeStatement();
/**
* Returns a statement corresponding to the HQL statement
* "SELECT id, within(geom, :filter) from GeomEntity where within(geom, :filter) = true and srid(geom) = 4326"
*
* @param testPolygon the geometry corresponding to the ':filter' query parameter
*
* @return
*/
protected abstract NativeSQLStatement createNativeWithinStatement(Geometry testPolygon);
/**
* Returns a statement corresponding to the HQL statement
* "SELECT id, equals(geom, :filter) from GeomEntity where equals(geom, :filter) = true and srid(geom) = 4326"
*
* @param geom the geometry corresponding to the ':filter' query parameter
*
* @return
*/
protected abstract NativeSQLStatement createNativeEqualsStatement(Geometry geom);
/**
* Returns a statement corresponding to the HQL statement
* "SELECT id, crosses(geom, :filter) from GeomEntity where crosses(geom, :filter) = true and srid(geom) = 4326"
*
* @param geom the geometry corresponding to the ':filter' query parameter
*
* @return
*/
protected abstract NativeSQLStatement createNativeCrossesStatement(Geometry geom);
/**
* Returns a statement corresponding to the HQL statement:
* "SELECT id, contains(geom, :filter) from GeomEntity where contains(geom, :geom) = true and srid(geom) = 4326";
*
* @param geom
*
* @return
*/
protected abstract NativeSQLStatement createNativeContainsStatement(Geometry geom);
/**
* Returns a statement corresponding to the HQL statement
* "SELECT id, disjoint(geom, :filter) from GeomEntity where disjoint(geom, :filter) = true and srid(geom) = 4326"
*
* @param geom the geometry corresponding to the ':filter' query parameter
*
* @return
*/
protected abstract NativeSQLStatement createNativeDisjointStatement(Geometry geom);
/**
* Returns a statement corresponding to the HQL statement
* "SELECT id, transform(geom, :epsg) from GeomEntity where srid(geom) = 4326"
*
* @param epsg - the EPSG code of the target projection system.
*
* @return
*/
protected abstract NativeSQLStatement createNativeTransformStatement(int epsg);
/**
* Returns the statement corresponding to the HQL statement
* "select id, (srid(geom) = :epsg) from GeomEntity where srid(geom) = :epsg ";
*
* @param srid
*
* @return
*/
protected abstract NativeSQLStatement createNativeHavingSRIDStatement(int srid);
/**
* Creates a connection to the database
*
* @return a Connection
*
* @throws SQLException
*/
protected Connection createConnection() throws SQLException {
return this.dataSourceUtils.getConnection();
}
/**
* Decodes a native database object to a JTS <code>Geometry</code> instance
*
* @param o native database object
*
* @return decoded geometry
*/
protected abstract Geometry decode(Object o);
/**
* Return a testsuite-suite polygon (filter, ...)
*
* @return a testsuite-suite polygon
*/
public Polygon getTestPolygon() {
WKTReader reader = new WKTReader();
try {
Polygon polygon = (Polygon) reader.read( TEST_POLYGON_WKT );
polygon.setSRID( getTestSrid() );
return polygon;
}
catch (ParseException e) {
throw new RuntimeException( e );
}
}
/**
* Return a testsuite-suite point (filter, ...)
*
* @return a testsuite-suite point
*/
public Point getTestPoint() {
WKTReader reader = new WKTReader();
try {
Point point = (Point) reader.read( TEST_POINT_WKT );
point.setSRID( getTestSrid() );
return point;
}
catch (ParseException e) {
throw new RuntimeException( e );
}
}
protected <T> Map<Integer, T> retrieveExpected(NativeSQLStatement nativeSQLStatement, int type)
throws SQLException {
PreparedStatement preparedStatement = null;
ResultSet results = null;
Connection cn = null;
Map<Integer, T> expected = new HashMap<Integer, T>();
try {
cn = createConnection();
preparedStatement = nativeSQLStatement.prepare( cn );
LOG.info( "Native SQL is: " + preparedStatement.toString() );
results = preparedStatement.executeQuery();
while ( results.next() ) {
int id = results.getInt( 1 );
switch ( type ) {
case GEOMETRY:
expected.put( id, (T) decode( results.getObject( 2 ) ) );
break;
case STRING:
expected.put( id, (T) results.getString( 2 ) );
break;
case INTEGER:
expected.put( id, (T) Long.valueOf( results.getLong( 2 ) ) );
break;
case DOUBLE:
Double value = Double.valueOf( results.getDouble( 2 ) );
if ( results.wasNull() ) {
value = null; //this is required because SQL Server converts automatically null to 0.0
}
expected.put( id, (T) value );
break;
case BOOLEAN:
expected.put( id, (T) Boolean.valueOf( results.getBoolean( 2 ) ) );
break;
default:
T val = (T) results.getObject( 2 );
//this code is a hack to deal with Oracle Spatial that returns Blob's for asWKB() function
//TODO -- clean up
if ( val instanceof Blob ) {
val = (T) ((Blob) val).getBytes( 1, MAX_BYTE_LEN );
}
expected.put( id, val );
}
}
return expected;
}
finally {
if ( results != null ) {
try {
results.close();
}
catch (SQLException e) {
}
}
if ( preparedStatement != null ) {
try {
preparedStatement.close();
}
catch (SQLException e) {
}
}
if ( cn != null ) {
try {
cn.close();
}
catch (SQLException e) {
}
}
}
}
protected NativeSQLStatement createNativeSQLStatement(final String sql) {
return new NativeSQLStatement() {
public PreparedStatement prepare(Connection connection) throws SQLException {
return connection.prepareStatement( sql );
}
};
}
protected NativeSQLStatement createNativeSQLStatementAllWKTParams(final String sql, final String wkt) {
return new NativeSQLStatement() {
public PreparedStatement prepare(Connection connection) throws SQLException {
PreparedStatement pstmt = connection.prepareStatement( sql );
for ( int i = 1; i <= numPlaceHoldersInSQL( sql ); i++ ) {
pstmt.setString( i, wkt );
}
return pstmt;
}
};
}
protected NativeSQLStatement createNativeSQLStatement(final String sql, final Object[] params) {
return new NativeSQLStatement() {
public PreparedStatement prepare(Connection connection) throws SQLException {
PreparedStatement pstmt = connection.prepareStatement( sql );
int i = 1;
for ( Object param : params ) {
pstmt.setObject( i++, param );
}
return pstmt;
}
};
}
protected int numPlaceHoldersInSQL(String sql) {
return sql.replaceAll( "[^?]", "" ).length();
}
}