/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2004-2010, 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.
*
* reated on October 27, 2004, 11:27 AM
*/
package org.geotools.filter.function;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.FunctionExpression;
import org.geotools.filter.FunctionExpressionImpl;
import org.geotools.filter.FunctionFactory;
import org.geotools.filter.FunctionImpl;
import org.geotools.util.logging.Logging;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.capability.FunctionName;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.expression.Function;
import org.opengis.filter.expression.Literal;
/**
* Filter function factory that uses the spi lookup mechanism to create functions.
*
* @author Justin Deoliveira, OpenGeo
*
*
* @source $URL: http://svn.osgeo.org/geotools/trunk/modules/library/main/src/main/java/org/geotools/filter/function/DefaultFunctionFactory.java $
*/
public class DefaultFunctionFactory implements FunctionFactory {
private static final Logger LOGGER = Logging.getLogger("org.geotools.filter");
private FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory(null);
private Map<String,FunctionDescriptor> functionCache;
public List<FunctionName> getFunctionNames() {
ArrayList<FunctionName> list = new ArrayList<FunctionName>(functionCache().size());
for (FunctionDescriptor fd : functionCache().values()) {
list.add(fd.name);
// if( "rint".equals(fd.name.getName())){
// System.out.println("Listed rint");
// }
}
return list;
}
public Function function(String name, List<Expression> parameters, Literal fallback) {
// cache lookup
String key = functionName(name);
FunctionDescriptor fd = functionCache().get(key);
if (fd == null) {
fd = functionCache().get(name);
if( fd == null ){
//no such function
return null;
}
// LOGGER.warning("Name conflict between '"+name+"' and '"+key+"'");
}
try {
Function newFunction = fd.newFunction(parameters, fallback);
return newFunction;
}
catch(Exception e){
LOGGER.log( Level.FINER, "Unable to create function " + name + "Function", e);
//just continue on to return null
}
return null;
}
private Map<String,FunctionDescriptor> functionCache() {
if (functionCache == null) {
synchronized(this) {
if (functionCache == null) {
functionCache = loadFunctions();
}
}
}
return functionCache;
}
private FunctionName getFunctionName( Function function ){
String name = function.getName();
FunctionName functionName = function.getFunctionName();
if( functionName == null && function instanceof FunctionExpressionImpl){
functionName = function.getFunctionName();
}
if( functionName == null ){
int argc;
if( function instanceof FunctionExpression ){
argc = ((FunctionExpression)function).getArgCount();
}
else {
argc = function.getParameters().size();
}
functionName = filterFactory.functionName(name, argc );
}
else {
if( !functionName.getName().equals(name )){
LOGGER.warning( function.getClass() +" has name conflict betwee '"+name+"' and '"+functionName.getName()+"'");
}
}
return functionName;
}
private Map<String,FunctionDescriptor> loadFunctions() {
Map<String,FunctionDescriptor> functionMap = new HashMap<String,FunctionDescriptor>();
Set<Function> functions = CommonFactoryFinder.getFunctions(null);
for (Iterator<Function> i = functions.iterator(); i.hasNext();) {
Function function = (Function) i.next();
FunctionName functionName = getFunctionName( function );
String name = function.getName();
FunctionDescriptor fd = new FunctionDescriptor( functionName, function.getClass() );
// if("rint".equals(functionName.getName())){
// System.out.println("Loaded rint");
// }
// needed to insert justin's name hack here to ensure consistent lookup
String key = functionName(name);
if( functionMap.containsKey(key)){
// conflicted name - probably a cut and paste error when creating functionName
FunctionDescriptor conflict = functionMap.get(key);
LOGGER.warning("Function "+key+" clash between "+conflict.clazz.getSimpleName()+" and "+function.getClass().getSimpleName());
}
functionMap.put( key, fd );
}
return functionMap;
}
private String functionName(String name) {
//strip off "Function" prefix
int index = -1;
if ((index = name.indexOf("Function")) != -1) {
name = name.substring(0, index);
}
//convert to lower case
name = name.toLowerCase().trim();
//JD: not sure why the first character is converted back to upper case, disabling this and
// just keeping everything lower case, as this is only used internally to store functions
// it should affect any existing clients
//char c = name.charAt(0);
//name = name.replaceFirst("" + c, "" + Character.toUpperCase(c));
return name;
}
static class FunctionDescriptor {
FunctionName name;
Class clazz;
FunctionDescriptor(FunctionName name, Class clazz) {
this.name = name;
this.clazz = clazz;
}
Function newFunction(List<Expression> parameters, Literal fallback) throws Exception {
// cache lookup
if (FunctionExpression.class.isAssignableFrom(clazz)) {
FunctionExpression function = (FunctionExpression) clazz.newInstance();
if(parameters != null) {
function.setParameters(parameters);
}
if( fallback != null && function instanceof ClassificationFunction){
ClassificationFunction classification = (ClassificationFunction) function;
classification.setFallbackValue( fallback );
}
return function;
}
if(FunctionImpl.class.isAssignableFrom(clazz)) {
FunctionImpl function = (FunctionImpl) clazz.newInstance();
if(parameters != null){
function.setParameters( (List) parameters );
}
if(fallback != null)
function.setFallbackValue( fallback );
return function;
}
//Function function = (Function) functionClass.newInstance();
Constructor<Function> constructor = clazz.getConstructor( new Class[]{ List.class, Literal.class} );
return constructor.newInstance( parameters, fallback );
}
}
}