/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2005-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.filter;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;
import junit.framework.TestCase;
import org.opengis.filter.expression.Add;
import org.opengis.filter.expression.Divide;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.ExpressionVisitor;
import org.opengis.filter.expression.Function;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.expression.Multiply;
import org.opengis.filter.expression.NilExpression;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.expression.Subtract;
/**
*
* @author Gabriel Roldan, Axios Engineering
*
* @source $URL$
* @version $Id$
*/
public class FunctionExpressionImplTest extends TestCase {
private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger(FunctionExpressionImplTest.class.getPackage().getName());
FunctionExpressionImpl function;
TestExpressionVisitor testVisitor;
public void setUp() throws Exception {
super.setUp();
function = new FunctionExpressionImpl("testFunction") {
public int getArgCount() {
return 1;
}
};
testVisitor = new TestExpressionVisitor();
}
public void tearDown() throws Exception {
super.tearDown();
function = null;
testVisitor = null;
}
public void testAcceptExpressionVisitor() {
Object extraData = new Object();
function.accept(testVisitor, extraData);
final Object[] expected = { Boolean.TRUE, extraData };
final Object[] actual = testVisitor.functionVisited;
assertEquals(expected[0], actual[0]);
assertEquals(expected[1], actual[1]);
}
public void testGetType() {
assertEquals(ExpressionType.FUNCTION, function.getType());
}
public void testGetName() {
function.setName("testFunction");
assertEquals("testFunction", function.getName());
}
public void testSetName() {
function.setName("testFunction");
// do not try this at home
assertEquals("testFunction", function.name);
}
public void testGetParameters() {
final List expected = Collections
.singletonList(new LiteralExpressionImpl(10d));
// do not try this at home
function.params = expected;
assertEquals(expected, function.getParameters());
}
public void testSetParameters() {
final List expected = Collections
.singletonList(new LiteralExpressionImpl(10d));
// do not try this at home
function.setParameters(expected);
assertEquals(expected, function.params);
}
public void testGetArgs() {
final List expected = Collections
.singletonList(new LiteralExpressionImpl(10d));
function.setParameters(expected);
Expression[] args = function.getArgs();
List actual = Arrays.asList(args);
assertEquals(expected, actual);
}
public void testSetArgs() {
final List expected = Collections
.singletonList(new LiteralExpressionImpl(10d));
org.geotools.filter.Expression[] args = (org.geotools.filter.Expression[]) expected.toArray(new org.geotools.filter.Expression[1]);
function.setArgs(args);
assertEquals(expected, function.params);
}
public void testGetArgCount() {
final List expected = Collections
.singletonList(new LiteralExpressionImpl(10d));
function.setParameters(expected);
assertEquals(1, function.getArgCount());
}
public void testGetImplementationHints() {
assertNotNull(function.getImplementationHints());
assertTrue(function.getImplementationHints().isEmpty());
}
public void testImplementations() throws IOException,
ClassNotFoundException, InstantiationException,
IllegalAccessException {
List functionClasses = loadFunctionClasses();
List errors = new LinkedList();
for (Iterator it = functionClasses.iterator(); it.hasNext();) {
Class functionClass = (Class) it.next();
Function function = (Function) functionClass.newInstance();
testFunction(function, errors);
}
if (errors.size() > 0) {
String errorsMessage = buildErrosMessage(errors);
LOGGER.info(errorsMessage);
fail(errorsMessage);
}
}
/**
* @param errors
* List<Exception>
* @return a formatted error message
*/
private String buildErrosMessage(List errors) {
StringBuffer sb = new StringBuffer(
"Some function expression implementations violates contract:\n");
int errorCount = 1;
for (Iterator it = errors.iterator(); it.hasNext();) {
String error = (String) it.next();
sb.append(errorCount++ + " - ");
sb.append(error);
sb.append("\n");
}
return sb.toString();
}
private void testFunction(Function function, List errors)
throws InstantiationException, IllegalAccessException {
final String functionClass = function.getClass().getName();
if (null == function.getName()) {
errors.add(functionClass + ": getName() == null");
}
testVisitor = new TestExpressionVisitor();
Object extraData = "[extraData correctly returned]";
function.accept(testVisitor, extraData);
Object[] functionVisited = testVisitor.functionVisited;
if (Boolean.TRUE != functionVisited[0]
|| extraData != functionVisited[1]) {
errors
.add(functionClass
+ ": accept didn't called visitor.visit(Function, extraData): "
+ " visited: " + functionVisited[0]
+ ", extraData: " + functionVisited[1]);
}
try {
String toString = function.toString();
} catch (Exception e) {
addExceptionError(errors, functionClass, "toString", e);
}
if (function instanceof FunctionExpression) {
testDeprecatedMethods((FunctionExpression) function, errors);
}
List<Expression> parameters = function.getParameters();
if(parameters == null){
errors.add(functionClass + ".getParameters() returns null");
}
}
private void addExceptionError(List errors, final String functionClass,
final String method, Exception e) {
/*
* StringWriter stringWriter = new StringWriter(); e.printStackTrace(new
* PrintWriter(stringWriter));
*
* errors.add(functionClass + "." + method + "() throwed an exception: " +
* stringWriter.getBuffer());
*/
errors.add(functionClass + "." + method + "() throwed an exception: "
+ e.getMessage());
}
private void testDeprecatedMethods(FunctionExpression function, List errors)
throws InstantiationException, IllegalAccessException {
final String functionClass = function.getClass().getName();
int argCount = function.getArgCount();
final org.geotools.filter.Expression[] expected = new org.geotools.filter.Expression[argCount];
for (int i = 0; i < argCount; i++) {
org.geotools.filter.Expression ex = new AttributeExpressionImpl("attName");
expected[i] = ex;
}
final List expectedList = Arrays.asList(expected);
try {
function.setArgs(expected);
} catch (Exception e) {
addExceptionError(errors, functionClass, "setArgs", e);
}
List returnedParams = function.getParameters();
if (returnedParams == null) {
errors
.add(functionClass
+ ".getParameters() returned null when parameters were set through setArgs(Expression[])");
} else if (!expectedList.equals(returnedParams)) {
errors
.add(functionClass
+ ".getParameters() returned a wrong result when parameters were set through setArgs(Expression[])");
}
function = (FunctionExpression) function.getClass().newInstance();
function.setParameters(expectedList);
Expression[] returnedArgs = function.getArgs();
if (returnedArgs == null) {
errors
.add(functionClass
+ ".getArgs() returns null then arguments set through setParameters()");
} else {
returnedParams = Arrays.asList(expected);
if (!expectedList.equals(returnedParams)) {
errors.add(functionClass
+ ".getArgs() incompatible with getParameters()");
}
}
if (ExpressionType.FUNCTION != function.getType()) {
errors
.add(functionClass + ".getType != "
+ ExpressionType.FUNCTION);
}
}
private List loadFunctionClasses() throws IOException,
ClassNotFoundException {
final String spiDefinitionResource = "/META-INF/services/org.opengis.filter.expression.Function";
InputStream in = getClass().getResourceAsStream(spiDefinitionResource);
if (in == null) {
throw new FileNotFoundException(spiDefinitionResource);
}
List functionClasses = new LinkedList();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String className;
while ((className = reader.readLine()) != null) {
Object functionClazz = Class.forName(className);
functionClasses.add(functionClazz);
}
return functionClasses;
}
/**
* An ExpressionVisitor for function expressions test purposes that stores
* the visited status in a public field
*
* @author Gabriel Roldan, Axios Engineering
*
*/
private static class TestExpressionVisitor implements ExpressionVisitor {
public Object[] functionVisited = { Boolean.FALSE, null };
public Object visit(Function expression, Object extraData) {
functionVisited[0] = Boolean.TRUE;
functionVisited[1] = extraData;
return null;
}
public Object visit(Add expression, Object extraData) {
return null;
}
public Object visit(Divide expression, Object extraData) {
return null;
}
public Object visit(Literal expression, Object extraData) {
return null;
}
public Object visit(Multiply expression, Object extraData) {
return null;
}
public Object visit(PropertyName expression, Object extraData) {
return null;
}
public Object visit(Subtract expression, Object extraData) {
return null;
}
public Object visit(NilExpression arg0, Object arg1) {
return null;
}
}
}