/*
* © Copyright FOCONIS AG, 2014
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
*/
package org.openntf.formula;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.openntf.formula.impl.AtFunction;
import org.openntf.formula.impl.AtFunctionGeneric;
import org.openntf.formula.impl.AtFunctionSimple;
import org.openntf.service.IServiceLocator;
import org.openntf.service.ServiceLocatorFinder;
/**
*
* @author Roland Praml, Foconis AG
*
*/
public class FunctionFactory {
private static ThreadLocal<IServiceLocator> currentServiceLocator_ = new ThreadLocal<IServiceLocator>();
private final Map<String, Function> functions = new HashMap<String, Function>();
@SuppressWarnings("unused")
private boolean immutable;
/**
* Default constructor
*/
private FunctionFactory() {
super();
}
public static FunctionFactory createInstance() {
FunctionFactory instance = new FunctionFactory();
// ServiceLoader<FunctionSet> loader = ServiceLoader.load(FunctionSet.class);
List<FunctionSet> loaderList = findApplicationServices(FunctionSet.class);
// if (loader.iterator().hasNext()) {
// System.out.println("FunctionSet Service found.");
// for (FunctionSet fact : loader) {
// loaderList.add(fact);
// System.out.println("Using " + fact.getClass().getName());
// }
// } else {
// // case if serviceLoader does not work (notesAgent)
// System.out.println("FunctionSet Service not found. Using defaults");
// loaderList.add(new org.openntf.formula.function.Operators.Functions());
// loaderList.add(new org.openntf.formula.function.OperatorsBool.Functions());
// loaderList.add(new org.openntf.formula.function.Negators.Functions());
// loaderList.add(new org.openntf.formula.function.Comparators.Functions());
// loaderList.add(new org.openntf.formula.function.Constants.Functions());
// loaderList.add(new org.openntf.formula.function.MathFunctions.Functions());
// loaderList.add(new org.openntf.formula.function.DateTimeFunctions.Functions());
// loaderList.add(new org.openntf.formula.function.TextFunctions.Functions());
// }
//
Collections.sort(loaderList, new Comparator<FunctionSet>() {
public int compare(final FunctionSet paramT1, final FunctionSet paramT2) {
return paramT2.getPriority() - paramT1.getPriority();
}
});
for (FunctionSet fact : loaderList) {
instance.functions.putAll(fact.getFunctions());
}
instance.setImmutable();
return instance;
}
public static <T> List<T> findApplicationServices(final Class<T> serviceClazz) {
IServiceLocator serviceLocator = currentServiceLocator_.get();
if (serviceLocator == null) {
serviceLocator = ServiceLocatorFinder.findServiceLocator();
currentServiceLocator_.set(serviceLocator);
}
return serviceLocator.findApplicationServices(serviceClazz);
}
/**
* Returns the atFunction object that can evaluate 'funcName'
*/
public AtFunction getFunction(final String funcName) {
return (AtFunction) functions.get(funcName.toLowerCase());
}
/**
* Returns all avaliable at-Functions
*/
public Map<String, Function> getFunctions() {
return Collections.unmodifiableMap(functions);
}
/**
* Sets the factory to immutable, so that it is safe to cache them.
*/
public void setImmutable() {
immutable = true;
}
/**
* Initializes a class
*
* @throws
*/
public static Map<String, Function> getFunctions(final Class<?> cls) {
final Map<String, Function> ret = new HashMap<String, Function>();
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
String methodName = method.getName();
if (methodName.startsWith("at")) {
if (Modifier.isPrivate(method.getModifiers())) {
// skip methods declared as private
} else if (Modifier.isStatic(method.getModifiers())) {
methodName = "@".concat(methodName.substring(2));
// here the magic happens. If the return type of the implemented function is
// a ValueHolder then we create an AtFunctionGeneric. You have to do multi value handling
// otherwise an AtFunctionSimple is created that does multi value handling for you.
Function f;
if (ValueHolder.class.isAssignableFrom(method.getReturnType())) {
f = new AtFunctionGeneric(methodName, method);
} else {
f = new AtFunctionSimple(methodName, method);
}
ret.put(f.getImage().toLowerCase(), f);
} else {
throw new IllegalAccessError("Method " + methodName + " is either not static.");
}
}
}
return null;
}
});
} catch (PrivilegedActionException e) {
}
return ret;
}
public static FunctionFactory getMinimalFF() {
return minimalFF;
}
private static FunctionFactory minimalFF;
static {
minimalFF = new FunctionFactory();
ArrayList<FunctionSet> fsl = new ArrayList<FunctionSet>();
fsl.add(new org.openntf.formula.function.Operators.Functions());
fsl.add(new org.openntf.formula.function.OperatorsBool.Functions());
fsl.add(new org.openntf.formula.function.Negators.Functions());
fsl.add(new org.openntf.formula.function.Comparators.Functions());
fsl.add(new org.openntf.formula.function.Constants.Functions());
fsl.add(new org.openntf.formula.function.MathFunctions.Functions());
fsl.add(new org.openntf.formula.function.DateTimeFunctions.Functions());
fsl.add(new org.openntf.formula.function.TextFunctions.Functions());
for (FunctionSet fact : fsl)
minimalFF.functions.putAll(fact.getFunctions());
minimalFF.setImmutable();
}
}