/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2004-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.data.postgis;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import junit.framework.AssertionFailedError;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.DefaultQuery;
import org.geotools.data.FeatureReader;
import org.geotools.data.Query;
import org.geotools.data.Transaction;
import org.geotools.filter.FilterFactory;
import org.geotools.filter.RegfuncFilterFactoryImpl;
import org.geotools.filter.RegisteredFunction;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.expression.Expression;
/**
* Full test of registered function support, using filter queries to call a function on the database
* side.
*
* <p>
*
* See {@link AbstractRegfuncPostgisOnlineTestCase} for a description of this test fixture.
*
* @author Ben Caradoc-Davies, CSIRO Exploration and Mining
* @version $Id$
* @source $URL$
* @since 2.4
*/
public class RegFuncPostgisDataStoreOnlineTest extends AbstractRegfuncPostgisOnlineTestCase {
/**
* Test that using no filter results in features for all four rows being returned.
*
* @throws Exception
*/
public void testNoFilter() throws Exception {
runFilterTest(Filter.INCLUDE, new int[] { 3, 27, 41, 93 });
}
/**
* Test that a filter for descriptions containing "basalt" results in features for the two rows
* whose descriptions contain "basalt". Note that this is a case-insensitive condition.
*
* @throws Exception
*/
public void testBasaltFilter() throws Exception {
runFilterTest(buildFilter("basalt"), new int[] { 3, 27 });
}
/**
* Test that a filter for descriptions containing "GRAVEL" results in a feature for the one row
* whose description contains "GRAVEL" (case-insensitive).
*
* @throws Exception
*/
public void testGravelFilter() throws Exception {
runFilterTest(buildFilter("GRAVEL"), new int[] { 93 });
}
/**
* Test that a filter for descriptions containing "chocolate" returns no features.
*
* @throws Exception
*/
public void testChocolateFilter() throws Exception {
runFilterTest(buildFilter("chocolate"), new int[] {});
}
/**
* Test that the test is working by ensuring that specifying the wrong features causes a test
* failure.
*
* @throws Exception
*/
public void testBasaltFilterWrongFeatures() throws Exception {
// true if test unexpectedly passes
boolean failed = false;
try {
// should expect ids 3 and 27
runFilterTest(buildFilter("basalt"), new int[] { 27 });
// cannot fail() here as would be caught
failed = true;
} catch (AssertionFailedError e) {
// success
}
if (failed) {
fail();
}
}
/**
* Test that a filter query issued.
*
* @param filter
* filter to be passed in the query to determine the subset of features to be
* returned, or null for all features
* @param expectedFeatureIds
* integer id for returned features, matching the expected row ids
* @throws Exception
*/
private void runFilterTest(Filter filter, int[] expectedFeatureIds) throws Exception {
DataStore datastore = null;
try {
datastore = DataStoreFinder.getDataStore(getParams());
assertNotNull(datastore);
Query query = new DefaultQuery(TEST_TABLE_NAME, filter);
FeatureReader<SimpleFeatureType, SimpleFeature> reader = null;
try {
/*
* List of all the integer feature ids seen in the features returned for this query.
*/
List<Integer> featureIds = new ArrayList<Integer>();
reader = datastore.getFeatureReader(query, Transaction.AUTO_COMMIT);
for (SimpleFeature feature = null; reader.hasNext();) {
feature = reader.next();
/*
* Convert long feature id of the form test_table_name.1234 to the numeric id
* used when creating the row. This relies on the behaviour of the postgis fid
* mapper.
*/
Integer id = Integer
.valueOf(feature.getID().replace(TEST_TABLE_NAME + ".", ""));
featureIds.add(id);
}
/*
* The query succeeded as expected if and only if the sorted lists of returned and
* expected ids are equal. The expected ids are converted to a List of Integers to
* leverage Collections comparison.
*/
assertEquals(sortedList(expectedFeatureIds), sorted(featureIds));
} finally {
if (reader != null) {
reader.close();
}
}
} finally {
if (datastore != null) {
datastore.dispose();
}
}
}
/**
* Create a {@link Filter} that selects features from the test table whose description contains
* the text specified in the argument (case-insensitive).
*
* @param descriptionContainsText
* text that description column must contain
* @return filter to select features containing text in their description
*/
private Filter buildFilter(String descriptionContainsText) {
FilterFactory filterFactory = new RegfuncFilterFactoryImpl();
// Programmatically build a filter like this fragment from an OGC WFS 1.1.0 Query:
//
// <ogc:Filter>
// <ogc:PropertyIsEqualTo>
// <ogc:Function name="contains_text">
// <ogc:PropertyName>
// gsml:specification/gsml:GeologicUnit/gml:description
// </ogc:PropertyName>
// <ogc:Literal>basalt</ogc:Literal>
// </ogc:Function>
// <ogc:Literal>1</ogc:Literal>
// </ogc:PropertyIsEqualTo>
// </ogc:Filter>
//
// Now step by step:
//
// Build <ogc:PropertyName>
Expression property = filterFactory.property(DESCRIPTION_COLUMN_NAME);
// Build <ogc:Literal>basalt</ogc:Literal> equivalent
Expression string = filterFactory.createLiteralExpression(descriptionContainsText);
// Build <ogc:Function name="contains_text"> equivalent and set its arguments
Expression function = filterFactory.function(TEST_FUNCTION_NAME, property, string);
// Sanity check
assertEquals(RegisteredFunction.class, function.getClass());
// Build <ogc:Literal>1</ogc:Literal>
Expression one = filterFactory.createLiteralExpression(TEST_FUNCTION_RETURN_TRUE);
// Build <ogc:PropertyIsEqualTo>, set its arguments, and return it
return filterFactory.equals(function, one);
}
/**
* Return a sorted copy of a list.
*
* @param a
* list to be sorted
* @return a sorted copy of the input list
*/
private List sorted(List a) {
List b = new ArrayList(a);
Collections.sort(b);
return b;
}
/**
* Convert an int array into a sorted list of Integer.
*
* @param a
* array of int to be sorted
* @return sorted list of Integer, copied from input
*/
private List/* <Integer> */sortedList(int[] a) {
List b = new ArrayList(a.length);
for (int i = 0; i < a.length; i++) {
b.add(new Integer(a[i]));
}
return sorted(b);
}
}