/*
 * 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.integration;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.spatial.HSMessageLogger;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.dialect.h2geodb.GeoDBDialect;
import org.hibernate.spatial.dialect.hana.HANASpatialDialect;
import org.hibernate.spatial.dialect.oracle.OracleSpatial10gDialect;
import org.hibernate.spatial.testing.SpatialDialectMatcher;
import org.hibernate.spatial.testing.SpatialFunctionalTestCase;

import org.hibernate.testing.Skip;
import org.hibernate.testing.SkipForDialect;
import org.junit.Test;

import org.jboss.logging.Logger;

import org.locationtech.jts.geom.Geometry;

import static java.lang.String.format;

/**
 * @author Karel Maesen, Geovise BVBA
 */
@Skip(condition = SpatialDialectMatcher.class, message = "No Spatial Dialect")
public class TestSpatialFunctions extends SpatialFunctionalTestCase {

	private static final HSMessageLogger LOG = Logger.getMessageLogger(
			HSMessageLogger.class,
			TestSpatialFunctions.class.getName()
	);

	protected HSMessageLogger getLogger() {
		return LOG;
	}

	@Test
	public void test_dimension_on_jts() throws SQLException {
		dimension( JTS );
	}

	@Test
	public void test_dimension_on_geolatte() throws SQLException {
		dimension( GEOLATTE );
	}

	public void dimension(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.dimension ) ) {
			return;
		}
		Map<Integer, Integer> dbexpected = expectationsFactory.getDimension();
		String hql = format( "SELECT id, dimension(geom) FROM %s", entityName( pckg ) );
		retrieveHQLResultsAndCompare( dbexpected, hql, pckg );
	}

	@Test
	public void test_astext_on_jts() throws SQLException {
		astext( JTS );
	}

	@Test
	public void test_astext_on_geolatte() throws SQLException {
		astext( GEOLATTE );
	}

	public void astext(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.astext ) ) {
			return;
		}
		Map<Integer, String> dbexpected = expectationsFactory.getAsText();
		String hql = format( "SELECT id, astext(geom) from %s", entityName( pckg ) );
		retrieveHQLResultsAndCompare( dbexpected, hql, pckg );
	}

	@Test
	public void test_asbinary_on_jts() throws SQLException {
		asbinary( JTS );
	}

	@Test
	public void test_asbinary_on_geolatte() throws SQLException {
		asbinary( GEOLATTE );
	}


	public void asbinary(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.asbinary ) ) {
			return;
		}
		Map<Integer, byte[]> dbexpected = expectationsFactory.getAsBinary();
		String hql = format( "SELECT id, asbinary(geom) from %s", entityName( pckg ) );
		retrieveHQLResultsAndCompare( dbexpected, hql, pckg );
	}

	@Test
	public void test_geometrytype_on_jts() throws SQLException {
		geometrytype( JTS );
	}

	@Test
	public void test_geometrytype_on_geolatte() throws SQLException {
		geometrytype( GEOLATTE );
	}

	public void geometrytype(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.geometrytype ) ) {
			return;
		}
		Map<Integer, String> dbexpected = expectationsFactory.getGeometryType();
		String hql = format(
				"SELECT id, geometrytype(geom) from %s",
				entityName( pckg )
		);
		retrieveHQLResultsAndCompare( dbexpected, hql, entityName( pckg ) );
	}

	@Test
	public void test_srid_on_jts() throws SQLException {
		srid( JTS );
	}

	@Test
	public void test_srid_on_geolatte() throws SQLException {
		srid( GEOLATTE );
	}

	public void srid(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.srid ) ) {
			return;
		}
		Map<Integer, Integer> dbexpected = expectationsFactory.getSrid();
		String hql = format( "SELECT id, srid(geom) from %s", entityName( pckg ) );
		retrieveHQLResultsAndCompare( dbexpected, hql, pckg );
	}

	@Test
	public void test_issimple_on_jts() throws SQLException {
		issimple( JTS );
	}

	@Test
	public void test_issimple_on_geolatte() throws SQLException {
		issimple( GEOLATTE );
	}

	public void issimple(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.issimple ) ) {
			return;
		}
		Map<Integer, Boolean> dbexpected = expectationsFactory.getIsSimple();
		String hql = format( "SELECT id, issimple(geom) from %s", entityName( pckg ) );
		retrieveHQLResultsAndCompare( dbexpected, hql, pckg );
	}

	@Test
	public void test_isempty_on_jts() throws SQLException {
		isempty( JTS );
	}

	@Test
	public void test_isempty_on_geolatte() throws SQLException {
		isempty( GEOLATTE );
	}

	public void isempty(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.isempty ) ) {
			return;
		}
		Map<Integer, Boolean> dbexpected = expectationsFactory.getIsEmpty();
		String hql = format( "SELECT id, isEmpty(geom) from %s", entityName( pckg ) );
		retrieveHQLResultsAndCompare( dbexpected, hql, pckg );
	}

	@Test
	public void test_boundary_on_jts() throws SQLException {
		boundary( JTS );
	}

	@Test
	public void test_boundary_on_geolatte() throws SQLException {
		boundary( GEOLATTE );
	}

	public void boundary(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.boundary ) ) {
			return;
		}
		Map<Integer, Geometry> dbexpected = expectationsFactory.getBoundary();
		String hql = format( "SELECT id, boundary(geom) from %s", entityName( pckg ) );
		retrieveHQLResultsAndCompare( dbexpected, hql, pckg );
	}

	@Test
	public void test_envelope_on_jts() throws SQLException {
		envelope( JTS );
	}

	@Test
	public void test_envelope_on_geolatte() throws SQLException {
		envelope( GEOLATTE );
	}

	public void envelope(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.envelope ) ) {
			return;
		}
		Map<Integer, Geometry> dbexpected = expectationsFactory.getEnvelope();
		String hql = format( "SELECT id, envelope(geom) from %s", entityName( pckg ) );
		retrieveHQLResultsAndCompare( dbexpected, hql, pckg );
	}

	@Test
	public void test_within_on_jts() throws SQLException {
		within( JTS );
	}

	@Test
	public void test_within_on_geolatte() throws SQLException {
		within( GEOLATTE );
	}

	public void within(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.within ) ) {
			return;
		}
		Map<Integer, Boolean> dbexpected = expectationsFactory.getWithin( expectationsFactory.getTestPolygon() );
		String hql = format(
				"SELECT id, within(geom, :filter) from %s " +
						"where within(geom, :filter) = true and srid(geom) = %d",
				entityName( pckg ),
				expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "filter", expectationsFactory.getTestPolygon() );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );
	}

	@Test
	public void test_equals_on_jts() throws SQLException {
		equals( JTS );
	}

	@Test
	public void test_equals_on_geolatte() throws SQLException {
		equals( GEOLATTE );
	}

	public void equals(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.equals ) ) {
			return;
		}
		Map<Integer, Boolean> dbexpected = expectationsFactory.getEquals( expectationsFactory.getTestPolygon() );
		String hql = format(
				"SELECT id, equals(geom, :filter) from %s " +
						"where equals(geom, :filter) = true and srid(geom) = %d",
				entityName( pckg ),
				expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "filter", expectationsFactory.getTestPolygon() );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );
	}

	@Test
	public void test_crosses_on_jts() throws SQLException {
		crosses( JTS );
	}

	@Test
	public void test_crosses_on_geolatte() throws SQLException {
		crosses( GEOLATTE );
	}

	public void crosses(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.crosses ) ) {
			return;
		}
		Map<Integer, Boolean> dbexpected = expectationsFactory.getCrosses( expectationsFactory.getTestPolygon() );
		String hql = format(
				"SELECT id, crosses(geom, :filter) from %s " +
						"where crosses(geom, :filter) = true and srid(geom) = %d",
				entityName( pckg ),
				expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "filter", expectationsFactory.getTestPolygon() );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );

	}

	@Test
	public void test_contains_on_jts() throws SQLException {
		contains( JTS );
	}

	@Test
	public void test_contains_on_geolatte() throws SQLException {
		contains( GEOLATTE );
	}

	public void contains(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.contains ) ) {
			return;
		}
		Map<Integer, Boolean> dbexpected = expectationsFactory.getContains( expectationsFactory.getTestPolygon() );
		String hql = format(
				"SELECT id, contains(geom, :filter) from %s " +
						"where contains(geom, :filter) = true and srid(geom) = %d",
				entityName( pckg ),
				expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "filter", expectationsFactory.getTestPolygon() );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );
	}

	@Test
	public void test_disjoint_on_jts() throws SQLException {
		disjoint( JTS );
	}

	@Test
	public void test_disjoint_on_geolatte() throws SQLException {
		disjoint( GEOLATTE );
	}

	public void disjoint(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.disjoint ) ) {
			return;
		}
		Map<Integer, Boolean> dbexpected = expectationsFactory.getDisjoint( expectationsFactory.getTestPolygon() );
		String hql = format(
				"SELECT id, disjoint(geom, :filter) from %s " +
						"where disjoint(geom, :filter) = true and srid(geom) = %d",
				entityName( pckg ),
				expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "filter", expectationsFactory.getTestPolygon() );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );
	}

	@Test
	public void test_intersects_on_jts() throws SQLException {
		intersects( JTS );
	}

	@Test
	public void test_intersects_on_geolatte() throws SQLException {
		intersects( GEOLATTE );
	}

	public void intersects(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.intersects ) ) {
			return;
		}
		Map<Integer, Boolean> dbexpected = expectationsFactory.getIntersects( expectationsFactory.getTestPolygon() );
		String hql = format(
				"SELECT id, intersects(geom, :filter) from %s " +
						"where intersects(geom, :filter) = true and srid(geom) = %d",
				entityName( pckg ),
				expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "filter", expectationsFactory.getTestPolygon() );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );
	}

	@Test
	public void test_overlaps_on_jts() throws SQLException {
		overlaps( JTS );
	}

	@Test
	public void test_overlaps_on_geolatte() throws SQLException {
		overlaps( GEOLATTE );
	}

	public void overlaps(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.overlaps ) ) {
			return;
		}
		Map<Integer, Boolean> dbexpected = expectationsFactory.getOverlaps( expectationsFactory.getTestPolygon() );
		String hql = format(
				"SELECT id, overlaps(geom, :filter) from %s where overlaps(geom, :filter) = true and srid(geom) = %d",
				entityName( pckg ),
				expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "filter", expectationsFactory.getTestPolygon() );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );
	}

	@Test
	public void test_touches_on_jts() throws SQLException {
		touches( JTS );
	}

	@Test
	public void test_touches_on_geolatte() throws SQLException {
		touches( GEOLATTE );
	}

	public void touches(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.touches ) ) {
			return;
		}
		String hql = format(
				"SELECT id, touches(geom, :filter) from %s where touches(geom, :filter) = true and srid(geom) = %d",
				entityName( pckg ),
				expectationsFactory.getTestSrid()
		);
		Map<Integer, Boolean> dbexpected = expectationsFactory.getTouches( expectationsFactory.getTestPolygon() );
		Map<String, Object> params = createQueryParams( "filter", expectationsFactory.getTestPolygon() );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );
	}

	@Test
	public void test_relate_on_jts() throws SQLException {
		relate( JTS );
	}

	@Test
	public void test_relate_on_geolatte() throws SQLException {
		relate( GEOLATTE );
	}

	public void relate(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.relate ) ) {
			return;
		}
		String matrix = "T*T***T**";
		Map<Integer, Boolean> dbexpected = expectationsFactory.getRelate(
				expectationsFactory.getTestPolygon(),
				matrix
		);
		String hql = format(
				"SELECT id, relate(geom, :filter, :matrix) from %s " +
						"where relate(geom, :filter, :matrix) = true and srid(geom) = %d",
				entityName( pckg ),
				expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "filter", expectationsFactory.getTestPolygon() );
		params.put( "matrix", matrix );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );

		matrix = "FF*FF****";
		dbexpected = expectationsFactory.getRelate( expectationsFactory.getTestPolygon(), matrix );
		params.put( "matrix", matrix );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );

	}

	@Test
	public void test_distance_on_jts() throws SQLException {
		distance( JTS );
	}

	@Test
	public void test_distance_on_geolatte() throws SQLException {
		distance( GEOLATTE );
	}

	public void distance(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.distance ) ) {
			return;
		}
		Map<Integer, Double> dbexpected = expectationsFactory.getDistance( expectationsFactory.getTestPolygon() );
		String hql = format(
				"SELECT id, distance(geom, :filter) from %s where srid(geom) = %d", entityName( pckg ), expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "filter", expectationsFactory.getTestPolygon() );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );
	}

	@Test
	public void test_buffer_on_jts() throws SQLException {
		buffer( JTS );
	}

	@Test
	public void test_buffer_on_geolatte() throws SQLException {
		buffer( GEOLATTE );
	}

	public void buffer(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.buffer ) ) {
			return;
		}
		Map<Integer, Geometry> dbexpected = expectationsFactory.getBuffer( Double.valueOf( 1.0 ) );
		String hql = format(
				"SELECT id, buffer(geom, :distance) from %s where srid(geom) = %d",
				entityName( pckg ), expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "distance", Double.valueOf( 1.0 ) );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );

	}

	// The convexhull tests are skipped for Oracle because of error:
	// ORA-13276: internal error [Geodetic Geometry too big for Local Transform] in coordinate transformation

	@Test
	@SkipForDialect(value = OracleSpatial10gDialect.class)
	public void test_convexhull_on_jts() throws SQLException {
		convexhull( JTS );
	}


	@Test
	@SkipForDialect(value = OracleSpatial10gDialect.class)
	public void test_convexhull_on_geolatte() throws SQLException {
		convexhull( GEOLATTE );
	}

	public void convexhull(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.convexhull ) ) {
			return;
		}
		Map<Integer, Geometry> dbexpected = expectationsFactory.getConvexHull( expectationsFactory.getTestPolygon() );
		String hql = format(
				"SELECT id, convexhull(geomunion(geom, :polygon)) from %s where srid(geom) = %d",
				entityName( pckg ), expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "polygon", expectationsFactory.getTestPolygon() );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );

	}

	@Test
	@SkipForDialect(value = GeoDBDialect.class)
	public void test_intersection_on_jts() throws SQLException {
		intersection( JTS );
	}

	@Test
	@SkipForDialect(value = GeoDBDialect.class)
	public void test_intersection_on_geolatte() throws SQLException {
		intersection( GEOLATTE );
	}

	//skipped for GeoDBDialect because GeoDB throws exceptions in case the intersection is empty.
	// (Error message is "Empty Points cannot be represented in WKB")
	public void intersection(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.intersection ) ) {
			return;
		}
		Map<Integer, Geometry> dbexpected = expectationsFactory.getIntersection( expectationsFactory.getTestPolygon() );
		String hql = format(
				"SELECT id, intersection(geom, :polygon) from %s where srid(geom) = %d",
				entityName( pckg ), expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "polygon", expectationsFactory.getTestPolygon() );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );
	}

	@Test
	public void test_difference_on_jts() throws SQLException {
		difference( JTS );
	}

	@Test
	public void test_difference_on_geolatte() throws SQLException {
		difference( GEOLATTE );
	}

	public void difference(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.difference ) ) {
			return;
		}
		Map<Integer, Geometry> dbexpected = expectationsFactory.getDifference( expectationsFactory.getTestPolygon() );
		String hql = format(
				"SELECT id, difference(geom, :polygon) from %s where srid(geom) = %d",
				entityName( pckg ), expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "polygon", expectationsFactory.getTestPolygon() );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );
	}

	@Test
	public void test_symdifference_on_jts() throws SQLException {
		symdifference( JTS );
	}

	@Test
	public void test_symdifference_on_geolatte() throws SQLException {
		symdifference( GEOLATTE );
	}

	public void symdifference(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.symdifference ) ) {
			return;
		}
		Map<Integer, Geometry> dbexpected = expectationsFactory.getSymDifference( expectationsFactory.getTestPolygon() );
		String hql = format(
				"SELECT id, symdifference(geom, :polygon) from %s where srid(geom) = %d",
				entityName( pckg ),
				expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "polygon", expectationsFactory.getTestPolygon() );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );
	}

	@Test
	public void test_geomunion_on_jts() throws SQLException {
		geomunion( JTS );
	}

	@Test
	public void test_geomunion_on_geolatte() throws SQLException {
		geomunion( GEOLATTE );
	}

	public void geomunion(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.geomunion ) ) {
			return;
		}
		Map<Integer, Geometry> dbexpected = expectationsFactory.getGeomUnion( expectationsFactory.getTestPolygon() );
		String hql = format(
				"SELECT id, geomunion(geom, :polygon) from %s " +
						"where srid(geom) = %d", entityName( pckg ), expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "polygon", expectationsFactory.getTestPolygon() );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );
	}

	@Test
	public void test_dwithin_on_jts() throws SQLException {
		dwithin( JTS );
	}

	@Test
	public void test_dwithin_on_geolatte() throws SQLException {
		dwithin( GEOLATTE );
	}

	public void dwithin(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.dwithin ) ) {
			return;
		}
		double distance = 30.0;
		Map<Integer, Boolean> dbexpected = expectationsFactory.getDwithin(
				expectationsFactory.getTestPoint(),
				distance
		);
		String hql = format(
				"SELECT id, dwithin(geom, :filter, :distance) from " +
						"%s where dwithin(geom, :filter, :distance) = true " +
						"and srid(geom) = %d", entityName( pckg ), expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "filter", expectationsFactory.getTestPoint() );
		if ( getDialect() instanceof OracleSpatial10gDialect ) {
			//because this uses the weird syntax and conventions of SDO_WITHIN_DISTANCE which returns a string (really)
			// we use a different boolean expression guaranteed to be true, and we set the third parameter to key/value string
			hql = "SELECT id, issimple(geom) from org.hibernate.spatial.integration.GeomEntity where dwithin(geom, :filter, :distance) = true and srid(geom) = " + expectationsFactory
					.getTestSrid();
			params.put( "distance", "distance = 30" );
		}
		else {
			params.put( "distance", 30.0 );
		}
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );
	}

	// The transform tests are skipped for HANA because there is no transform definition for SRID 0

	@Test
	@SkipForDialect(value = HANASpatialDialect.class)
	public void test_transform_on_jts() throws SQLException {
		transform( JTS );
	}

	@Test
	@SkipForDialect(value = HANASpatialDialect.class)
	public void test_transform_on_geolatte() throws SQLException {
		transform( GEOLATTE );
	}

	public void transform(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.transform ) ) {
			return;
		}
		int epsg = 4324;
		Map<Integer, Geometry> dbexpected = expectationsFactory.getTransform( epsg );
		String hql = format(
				"SELECT id, transform(geom, :epsg) from %s " +
						"where srid(geom) = %d", entityName( pckg ), expectationsFactory.getTestSrid()
		);
		Map<String, Object> params = createQueryParams( "epsg", Integer.valueOf( epsg ) );
		retrieveHQLResultsAndCompare( dbexpected, hql, params, pckg );

	}

	@Test
	public void test_extent_on_jts() throws SQLException {
		extent( JTS );
	}

	@Test
	public void test_extent_on_geolatte() throws SQLException {
		extent( GEOLATTE );
	}

	public void extent(String pckg) throws SQLException {
		if ( !isSupportedByDialect( SpatialFunction.extent ) ) {
			return;
		}
		// here we just check if we get a result, and can read it
		String hql = format(
				"SELECT id, extent(geom) from %s group by id", entityName( pckg )
		);
		Map<Integer, Object> hsreceived = new HashMap<Integer, Object>();
		doInSession( hql, hsreceived, new HashMap<String, Object>() );
	}

	public <T> void retrieveHQLResultsAndCompare(Map<Integer, T> dbexpected, String hql, String geometryType) {
		retrieveHQLResultsAndCompare( dbexpected, hql, null, geometryType );
	}

	protected <T> void retrieveHQLResultsAndCompare(
			Map<Integer, T> dbexpected,
			String hql,
			Map<String, Object> params,
			String geometryType) {
		Map<Integer, T> hsreceived = new HashMap<Integer, T>();
		doInSession( hql, hsreceived, params );
		compare( dbexpected, hsreceived, geometryType );
	}

	private Map<String, Object> createQueryParams(String filterParamName, Object value) {
		Map<String, Object> params = new HashMap<String, Object>();
		params.put( filterParamName, value );
		return params;
	}

	private <T> void doInSession(String hql, Map<Integer, T> result, Map<String, Object> params) {
		Session session = null;
		Transaction tx = null;
		try {
			session = openSession();
			tx = session.beginTransaction();
			Query query = session.createQuery( hql );
			setParameters( params, query );
			addQueryResults( result, query );
		}
		finally {
			if ( tx != null ) {
				tx.rollback();
			}
			if ( session != null ) {
				session.close();
			}
		}
	}

	private void setParameters(Map<String, Object> params, Query query) {
		if ( params == null ) {
			return;
		}
		for ( Map.Entry<String, Object> entry : params.entrySet() ) {
			query.setParameter( entry.getKey(), entry.getValue() );
		}
	}

}
