/*
***************************************************************************************
* Copyright (C) 2006 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
***************************************************************************************
*/
package com.espertech.esper.epl.core;
import com.espertech.esper.client.*;
import com.espertech.esper.client.annotation.BuiltinAnnotation;
import com.espertech.esper.client.hook.AggregationFunctionFactory;
import com.espertech.esper.client.util.ClassForNameProvider;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.epl.agg.access.AggregationStateType;
import com.espertech.esper.epl.agg.factory.AggregationFactoryFactory;
import com.espertech.esper.epl.approx.CountMinSketchAggType;
import com.espertech.esper.epl.expression.accessagg.ExprAggCountMinSketchNode;
import com.espertech.esper.epl.expression.accessagg.ExprAggMultiFunctionLinearAccessNode;
import com.espertech.esper.epl.expression.accessagg.ExprAggMultiFunctionSortedMinMaxByNode;
import com.espertech.esper.epl.expression.core.ExprCurrentEvaluationContextNode;
import com.espertech.esper.epl.expression.core.ExprNode;
import com.espertech.esper.epl.expression.methodagg.*;
import com.espertech.esper.epl.expression.time.TimeAbacus;
import com.espertech.esper.epl.index.quadtree.AdvancedIndexFactoryProviderMXCIFQuadTree;
import com.espertech.esper.epl.index.quadtree.AdvancedIndexFactoryProviderPointRegionQuadTree;
import com.espertech.esper.epl.index.service.AdvancedIndexFactoryProvider;
import com.espertech.esper.type.MinMaxTypeEnum;
import com.espertech.esper.util.JavaClassHelper;
import com.espertech.esper.util.MethodResolver;
import com.espertech.esper.util.TransientConfigurationResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.MathContext;
import java.util.*;
/**
* Implementation for engine-level imports.
*/
public class EngineImportServiceImpl implements EngineImportService {
private static final Logger log = LoggerFactory.getLogger(EngineImportServiceImpl.class);
private final List<String> imports;
private final List<String> annotationImports;
private final Map<String, ConfigurationPlugInAggregationFunction> aggregationFunctions;
private final List<Pair<Set<String>, ConfigurationPlugInAggregationMultiFunction>> aggregationAccess;
private final Map<String, EngineImportSingleRowDesc> singleRowFunctions;
private final Map<String, ConfigurationMethodRef> methodInvocationRef;
private final boolean allowExtendedAggregationFunc;
private final boolean isUdfCache;
private final boolean isDuckType;
private final boolean sortUsingCollator;
private final MathContext optionalDefaultMathContext;
private final TimeZone timeZone;
private final TimeAbacus timeAbacus;
private final ConfigurationEngineDefaults.ThreadingProfile threadingProfile;
private final Map<String, Object> transientConfiguration;
private final AggregationFactoryFactory aggregationFactoryFactory;
private final LinkedHashMap<String, AdvancedIndexFactoryProvider> advancedIndexProviders = new LinkedHashMap<>(8);
public EngineImportServiceImpl(boolean allowExtendedAggregationFunc, boolean isUdfCache, boolean isDuckType, boolean sortUsingCollator, MathContext optionalDefaultMathContext, TimeZone timeZone, TimeAbacus timeAbacus, ConfigurationEngineDefaults.ThreadingProfile threadingProfile, Map<String, Object> transientConfiguration, AggregationFactoryFactory aggregationFactoryFactory) {
imports = new ArrayList<String>();
annotationImports = new ArrayList<String>(2);
aggregationFunctions = new HashMap<String, ConfigurationPlugInAggregationFunction>();
aggregationAccess = new ArrayList<Pair<Set<String>, ConfigurationPlugInAggregationMultiFunction>>();
singleRowFunctions = new HashMap<String, EngineImportSingleRowDesc>();
methodInvocationRef = new HashMap<String, ConfigurationMethodRef>();
this.allowExtendedAggregationFunc = allowExtendedAggregationFunc;
this.isUdfCache = isUdfCache;
this.isDuckType = isDuckType;
this.sortUsingCollator = sortUsingCollator;
this.optionalDefaultMathContext = optionalDefaultMathContext;
this.timeZone = timeZone;
this.timeAbacus = timeAbacus;
this.threadingProfile = threadingProfile;
this.transientConfiguration = transientConfiguration;
this.aggregationFactoryFactory = aggregationFactoryFactory;
this.advancedIndexProviders.put("pointregionquadtree", new AdvancedIndexFactoryProviderPointRegionQuadTree());
this.advancedIndexProviders.put("mxcifquadtree", new AdvancedIndexFactoryProviderMXCIFQuadTree());
}
public boolean isUdfCache() {
return isUdfCache;
}
public boolean isDuckType() {
return isDuckType;
}
public ConfigurationMethodRef getConfigurationMethodRef(String className) {
return methodInvocationRef.get(className);
}
public ClassForNameProvider getClassForNameProvider() {
return TransientConfigurationResolver.resolveClassForNameProvider(transientConfiguration);
}
public ClassLoader getFastClassClassLoader(Class clazz) {
return TransientConfigurationResolver.resolveFastClassClassLoaderProvider(transientConfiguration).classloader(clazz);
}
public ClassLoader getClassLoader() {
return TransientConfigurationResolver.resolveClassLoader(transientConfiguration).classloader();
}
/**
* Adds cache configs for method invocations for from-clause.
*
* @param configs cache configs
*/
public void addMethodRefs(Map<String, ConfigurationMethodRef> configs) {
methodInvocationRef.putAll(configs);
}
public void addImport(String importName) throws EngineImportException {
validateImportAndAdd(importName, imports);
}
public void addAnnotationImport(String importName) throws EngineImportException {
validateImportAndAdd(importName, annotationImports);
}
public void addAggregation(String functionName, ConfigurationPlugInAggregationFunction aggregationDesc) throws EngineImportException {
validateFunctionName("aggregation function", functionName);
if (aggregationDesc.getFactoryClassName() == null || !isClassName(aggregationDesc.getFactoryClassName())) {
throw new EngineImportException("Invalid class name for aggregation factory '" + aggregationDesc.getFactoryClassName() + "'");
}
aggregationFunctions.put(functionName.toLowerCase(Locale.ENGLISH), aggregationDesc);
}
public void addSingleRow(String functionName, String singleRowFuncClass, String methodName, ConfigurationPlugInSingleRowFunction.ValueCache valueCache, ConfigurationPlugInSingleRowFunction.FilterOptimizable filterOptimizable, boolean rethrowExceptions, String optionalEventTypeName) throws EngineImportException {
validateFunctionName("single-row", functionName);
if (!isClassName(singleRowFuncClass)) {
throw new EngineImportException("Invalid class name for aggregation '" + singleRowFuncClass + "'");
}
singleRowFunctions.put(functionName.toLowerCase(Locale.ENGLISH), new EngineImportSingleRowDesc(singleRowFuncClass, methodName, valueCache, filterOptimizable, rethrowExceptions, optionalEventTypeName));
}
public AggregationFunctionFactory resolveAggregationFactory(String name) throws EngineImportUndefinedException, EngineImportException {
ConfigurationPlugInAggregationFunction desc = aggregationFunctions.get(name);
if (desc == null) {
desc = aggregationFunctions.get(name.toLowerCase(Locale.ENGLISH));
}
if (desc == null || desc.getFactoryClassName() == null) {
throw new EngineImportUndefinedException("A function named '" + name + "' is not defined");
}
String className = desc.getFactoryClassName();
Class clazz;
try {
clazz = getClassForNameProvider().classForName(className);
} catch (ClassNotFoundException ex) {
throw new EngineImportException("Could not load aggregation factory class by name '" + className + "'", ex);
}
Object object;
try {
object = clazz.newInstance();
} catch (InstantiationException e) {
throw new EngineImportException("Error instantiating aggregation factory class by name '" + className + "'", e);
} catch (IllegalAccessException e) {
throw new EngineImportException("Illegal access instatiating aggregation factory class by name '" + className + "'", e);
}
if (!(object instanceof AggregationFunctionFactory)) {
throw new EngineImportException("Aggregation class by name '" + className + "' does not implement AggregationFunctionFactory");
}
return (AggregationFunctionFactory) object;
}
public void addAggregationMultiFunction(ConfigurationPlugInAggregationMultiFunction desc) throws EngineImportException {
LinkedHashSet<String> orderedImmutableFunctionNames = new LinkedHashSet<String>();
for (String functionName : desc.getFunctionNames()) {
orderedImmutableFunctionNames.add(functionName.toLowerCase(Locale.ENGLISH));
validateFunctionName("aggregation multi-function", functionName.toLowerCase(Locale.ENGLISH));
}
if (!isClassName(desc.getMultiFunctionFactoryClassName())) {
throw new EngineImportException("Invalid class name for aggregation multi-function factory '" + desc.getMultiFunctionFactoryClassName() + "'");
}
aggregationAccess.add(new Pair<Set<String>, ConfigurationPlugInAggregationMultiFunction>(orderedImmutableFunctionNames, desc));
}
public ConfigurationPlugInAggregationMultiFunction resolveAggregationMultiFunction(String name) {
for (Pair<Set<String>, ConfigurationPlugInAggregationMultiFunction> config : aggregationAccess) {
if (config.getFirst().contains(name.toLowerCase(Locale.ENGLISH))) {
return config.getSecond();
}
}
return null;
}
public Pair<Class, EngineImportSingleRowDesc> resolveSingleRow(String name) throws EngineImportException, EngineImportUndefinedException {
EngineImportSingleRowDesc pair = singleRowFunctions.get(name);
if (pair == null) {
pair = singleRowFunctions.get(name.toLowerCase(Locale.ENGLISH));
}
if (pair == null) {
throw new EngineImportUndefinedException("A function named '" + name + "' is not defined");
}
Class clazz;
try {
clazz = getClassForNameProvider().classForName(pair.getClassName());
} catch (ClassNotFoundException ex) {
throw new EngineImportException("Could not load single-row function class by name '" + pair.getClassName() + "'", ex);
}
return new Pair<Class, EngineImportSingleRowDesc>(clazz, pair);
}
public Method resolveMethodOverloadChecked(String className, String methodName, Class[] paramTypes, boolean[] allowEventBeanType, boolean[] allowEventBeanCollType)
throws EngineImportException {
Class clazz;
try {
clazz = resolveClassInternal(className, false, false);
} catch (ClassNotFoundException e) {
throw new EngineImportException("Could not load class by name '" + className + "', please check imports", e);
}
try {
return MethodResolver.resolveMethod(clazz, methodName, paramTypes, false, allowEventBeanType, allowEventBeanCollType);
} catch (EngineNoSuchMethodException e) {
throw convert(clazz, methodName, paramTypes, e, false);
}
}
public Constructor resolveCtor(Class clazz, Class[] paramTypes) throws EngineImportException {
try {
return MethodResolver.resolveCtor(clazz, paramTypes);
} catch (EngineNoSuchCtorException e) {
throw convert(clazz, paramTypes, e);
}
}
public Method resolveMethodOverloadChecked(String className, String methodName) throws EngineImportException {
Class clazz;
try {
clazz = resolveClassInternal(className, false, false);
} catch (ClassNotFoundException e) {
throw new EngineImportException("Could not load class by name '" + className + "', please check imports", e);
}
return resolveMethodInternalCheckOverloads(clazz, methodName, MethodModifiers.REQUIRE_STATIC_AND_PUBLIC);
}
public Method resolveMethodOverloadChecked(Class clazz, String methodName) throws EngineImportException {
return resolveMethodInternalCheckOverloads(clazz, methodName, MethodModifiers.REQUIRE_STATIC_AND_PUBLIC);
}
public Method resolveNonStaticMethodOverloadChecked(Class clazz, String methodName) throws EngineImportException {
return resolveMethodInternalCheckOverloads(clazz, methodName, MethodModifiers.REQUIRE_NONSTATIC_AND_PUBLIC);
}
public Class resolveClass(String className, boolean forAnnotation) throws EngineImportException {
Class clazz;
try {
clazz = resolveClassInternal(className, false, forAnnotation);
} catch (ClassNotFoundException e) {
throw new EngineImportException("Could not load class by name '" + className + "', please check imports", e);
}
return clazz;
}
public Class resolveAnnotation(String className) throws EngineImportException {
Class clazz;
try {
clazz = resolveClassInternal(className, true, true);
} catch (ClassNotFoundException e) {
throw new EngineImportException("Could not load annotation class by name '" + className + "', please check imports", e);
}
return clazz;
}
/**
* Finds a class by class name using the auto-import information provided.
*
* @param className is the class name to find
* @param requireAnnotation whether the class must be an annotation
* @param forAnnotationUse whether resolving class for use with annotations
* @return class
* @throws ClassNotFoundException if the class cannot be loaded
*/
protected Class resolveClassInternal(String className, boolean requireAnnotation, boolean forAnnotationUse) throws ClassNotFoundException {
// Attempt to retrieve the class with the name as-is
try {
return getClassForNameProvider().classForName(className);
} catch (ClassNotFoundException e) {
if (log.isDebugEnabled()) {
log.debug("Class not found for resolving from name as-is '" + className + "'");
}
}
// check annotation-specific imports first
if (forAnnotationUse) {
Class clazz = checkImports(annotationImports, requireAnnotation, className);
if (clazz != null) {
return clazz;
}
}
// check all imports
Class clazz = checkImports(imports, requireAnnotation, className);
if (clazz != null) {
return clazz;
}
if (!forAnnotationUse) {
// try to resolve from method references
for (String name : methodInvocationRef.keySet()) {
if (JavaClassHelper.isSimpleNameFullyQualfied(className, name)) {
try {
Class found = getClassForNameProvider().classForName(name);
if (!requireAnnotation || found.isAnnotation()) {
return found;
}
} catch (ClassNotFoundException e1) {
if (log.isDebugEnabled()) {
log.debug("Class not found for resolving from method invocation ref:" + name);
}
}
}
}
}
// No import worked, the class isn't resolved
throw new ClassNotFoundException("Unknown class " + className);
}
public Method resolveMethod(Class clazz, String methodName, Class[] paramTypes, boolean[] allowEventBeanType, boolean[] allowEventBeanCollType)
throws EngineImportException {
try {
return MethodResolver.resolveMethod(clazz, methodName, paramTypes, true, allowEventBeanType, allowEventBeanType);
} catch (EngineNoSuchMethodException e) {
throw convert(clazz, methodName, paramTypes, e, true);
}
}
private EngineImportException convert(Class clazz, String methodName, Class[] paramTypes, EngineNoSuchMethodException e, boolean isInstance) {
String expected = JavaClassHelper.getParameterAsString(paramTypes);
String message = "Could not find ";
if (!isInstance) {
message += "static ";
} else {
message += "enumeration method, date-time method or instance ";
}
if (paramTypes.length > 0) {
message += "method named '" + methodName + "' in class '" + JavaClassHelper.getClassNameFullyQualPretty(clazz) + "' with matching parameter number and expected parameter type(s) '" + expected + "'";
} else {
message += "method named '" + methodName + "' in class '" + JavaClassHelper.getClassNameFullyQualPretty(clazz) + "' taking no parameters";
}
if (e.getNearestMissMethod() != null) {
message += " (nearest match found was '" + e.getNearestMissMethod().getName();
if (e.getNearestMissMethod().getParameterTypes().length == 0) {
message += "' taking no parameters";
} else {
message += "' taking type(s) '" + JavaClassHelper.getParameterAsString(e.getNearestMissMethod().getParameterTypes()) + "'";
}
message += ")";
}
return new EngineImportException(message, e);
}
private EngineImportException convert(Class clazz, Class[] paramTypes, EngineNoSuchCtorException e) {
String expected = JavaClassHelper.getParameterAsString(paramTypes);
String message = "Could not find constructor ";
if (paramTypes.length > 0) {
message += "in class '" + JavaClassHelper.getClassNameFullyQualPretty(clazz) + "' with matching parameter number and expected parameter type(s) '" + expected + "'";
} else {
message += "in class '" + JavaClassHelper.getClassNameFullyQualPretty(clazz) + "' taking no parameters";
}
if (e.getNearestMissCtor() != null) {
message += " (nearest matching constructor ";
if (e.getNearestMissCtor().getParameterTypes().length == 0) {
message += "taking no parameters";
} else {
message += "taking type(s) '" + JavaClassHelper.getParameterAsString(e.getNearestMissCtor().getParameterTypes()) + "'";
}
message += ")";
}
return new EngineImportException(message, e);
}
public ExprNode resolveSingleRowExtendedBuiltin(String name) {
String nameLowerCase = name.toLowerCase(Locale.ENGLISH);
if (nameLowerCase.equals("current_evaluation_context")) {
return new ExprCurrentEvaluationContextNode();
}
return null;
}
public ExprNode resolveAggExtendedBuiltin(String name, boolean isDistinct) {
if (!allowExtendedAggregationFunc) {
return null;
}
String nameLowerCase = name.toLowerCase(Locale.ENGLISH);
if (nameLowerCase.equals("first")) {
return new ExprAggMultiFunctionLinearAccessNode(AggregationStateType.FIRST);
}
if (nameLowerCase.equals("last")) {
return new ExprAggMultiFunctionLinearAccessNode(AggregationStateType.LAST);
}
if (nameLowerCase.equals("window")) {
return new ExprAggMultiFunctionLinearAccessNode(AggregationStateType.WINDOW);
}
if (nameLowerCase.equals("firstever")) {
return new ExprFirstEverNode(isDistinct);
}
if (nameLowerCase.equals("lastever")) {
return new ExprLastEverNode(isDistinct);
}
if (nameLowerCase.equals("countever")) {
return new ExprCountEverNode(isDistinct);
}
if (nameLowerCase.equals("minever")) {
return new ExprMinMaxAggrNode(isDistinct, MinMaxTypeEnum.MIN, false, true);
}
if (nameLowerCase.equals("maxever")) {
return new ExprMinMaxAggrNode(isDistinct, MinMaxTypeEnum.MAX, false, true);
}
if (nameLowerCase.equals("fminever")) {
return new ExprMinMaxAggrNode(isDistinct, MinMaxTypeEnum.MIN, true, true);
}
if (nameLowerCase.equals("fmaxever")) {
return new ExprMinMaxAggrNode(isDistinct, MinMaxTypeEnum.MAX, true, true);
}
if (nameLowerCase.equals("rate")) {
return new ExprRateAggNode(isDistinct);
}
if (nameLowerCase.equals("nth")) {
return new ExprNthAggNode(isDistinct);
}
if (nameLowerCase.equals("leaving")) {
return new ExprLeavingAggNode(isDistinct);
}
if (nameLowerCase.equals("maxby")) {
return new ExprAggMultiFunctionSortedMinMaxByNode(true, false, false);
}
if (nameLowerCase.equals("maxbyever")) {
return new ExprAggMultiFunctionSortedMinMaxByNode(true, true, false);
}
if (nameLowerCase.equals("minby")) {
return new ExprAggMultiFunctionSortedMinMaxByNode(false, false, false);
}
if (nameLowerCase.equals("minbyever")) {
return new ExprAggMultiFunctionSortedMinMaxByNode(false, true, false);
}
if (nameLowerCase.equals("sorted")) {
return new ExprAggMultiFunctionSortedMinMaxByNode(false, false, true);
}
CountMinSketchAggType cmsType = CountMinSketchAggType.fromNameMayMatch(nameLowerCase);
if (cmsType != null) {
return new ExprAggCountMinSketchNode(isDistinct, cmsType);
}
return null;
}
public MathContext getDefaultMathContext() {
return optionalDefaultMathContext;
}
public TimeZone getTimeZone() {
return timeZone;
}
public TimeAbacus getTimeAbacus() {
return timeAbacus;
}
public ConfigurationEngineDefaults.ThreadingProfile getThreadingProfile() {
return threadingProfile;
}
public boolean isSortUsingCollator() {
return sortUsingCollator;
}
public AggregationFactoryFactory getAggregationFactoryFactory() {
return aggregationFactoryFactory;
}
public AdvancedIndexFactoryProvider resolveAdvancedIndexProvider(String indexTypeName) throws EngineImportException {
AdvancedIndexFactoryProvider provider = advancedIndexProviders.get(indexTypeName);
if (provider == null) {
throw new EngineImportException("Unrecognized advanced-type index '" + indexTypeName + "'");
}
return provider;
}
/**
* For testing, returns imports.
*
* @return returns auto-import list as array
*/
protected String[] getImports() {
return imports.toArray(new String[imports.size()]);
}
private static boolean isFunctionName(String functionName) {
String classNameRegEx = "\\w+";
return functionName.matches(classNameRegEx);
}
private static boolean isClassName(String importName) {
String classNameRegEx = "(\\w+\\.)*\\w+(\\$\\w+)?";
return importName.matches(classNameRegEx);
}
private static boolean isPackageName(String importName) {
String classNameRegEx = "(\\w+\\.)+\\*";
return importName.matches(classNameRegEx);
}
// Strip off the final ".*"
private static String getPackageName(String importName) {
return importName.substring(0, importName.length() - 2);
}
private void validateFunctionName(String functionType, String functionName) throws EngineImportException {
String functionNameLower = functionName.toLowerCase(Locale.ENGLISH);
if (aggregationFunctions.containsKey(functionNameLower)) {
throw new EngineImportException("Aggregation function by name '" + functionName + "' is already defined");
}
if (singleRowFunctions.containsKey(functionNameLower)) {
throw new EngineImportException("Single-row function by name '" + functionName + "' is already defined");
}
for (Pair<Set<String>, ConfigurationPlugInAggregationMultiFunction> pairs : aggregationAccess) {
if (pairs.getFirst().contains(functionNameLower)) {
throw new EngineImportException("Aggregation multi-function by name '" + functionName + "' is already defined");
}
}
if (!isFunctionName(functionName)) {
throw new EngineImportException("Invalid " + functionType + " name '" + functionName + "'");
}
}
private Method resolveMethodInternalCheckOverloads(Class clazz, String methodName, MethodModifiers methodModifiers)
throws EngineImportException {
Method[] methods = clazz.getMethods();
Set<Method> overloadeds = null;
Method methodByName = null;
// check each method by name, add to overloads when multiple methods for the same name
for (Method method : methods) {
if (method.getName().equals(methodName)) {
int modifiers = method.getModifiers();
boolean isPublic = Modifier.isPublic(modifiers);
boolean isStatic = Modifier.isStatic(modifiers);
if (methodModifiers.acceptsPublicFlag(isPublic) && methodModifiers.acceptsStaticFlag(isStatic)) {
if (methodByName != null) {
if (overloadeds == null) {
overloadeds = new HashSet<>();
}
overloadeds.add(method);
} else {
methodByName = method;
}
}
}
}
if (methodByName == null) {
throw new EngineImportException("Could not find " + methodModifiers.getText() + " method named '" + methodName + "' in class '" + clazz.getName() + "'");
}
if (overloadeds == null) {
return methodByName;
}
// determine that all overloads have the same result type
for (Method overloaded : overloadeds) {
if (!overloaded.getReturnType().equals(methodByName.getReturnType())) {
throw new EngineImportException("Method by name '" + methodName + "' is overloaded in class '" + clazz.getName() + "' and overloaded methods do not return the same type");
}
}
return methodByName;
}
private Class checkImports(List<String> imports, boolean requireAnnotation, String className) throws ClassNotFoundException {
// Try all the imports
for (String importName : imports) {
boolean isClassName = isClassName(importName);
boolean containsPackage = importName.indexOf('.') != -1;
String classNameWithDot = "." + className;
String classNameWithDollar = "$" + className;
// Import is a class name
if (isClassName) {
if ((containsPackage && importName.endsWith(classNameWithDot)) ||
(containsPackage && importName.endsWith(classNameWithDollar)) ||
(!containsPackage && importName.equals(className)) ||
(!containsPackage && importName.endsWith(classNameWithDollar))) {
return getClassForNameProvider().classForName(importName);
}
String prefixedClassName = importName + '$' + className;
try {
Class clazz = getClassForNameProvider().classForName(prefixedClassName);
if (!requireAnnotation || clazz.isAnnotation()) {
return clazz;
}
} catch (ClassNotFoundException e) {
if (log.isDebugEnabled()) {
log.debug("Class not found for resolving from name '" + prefixedClassName + "'");
}
}
} else {
if (requireAnnotation && importName.equals(Configuration.ANNOTATION_IMPORT)) {
Class clazz = BuiltinAnnotation.BUILTIN.get(className.toLowerCase(Locale.ENGLISH));
if (clazz != null) {
return clazz;
}
}
// Import is a package name
String prefixedClassName = getPackageName(importName) + '.' + className;
try {
Class clazz = getClassForNameProvider().classForName(prefixedClassName);
if (!requireAnnotation || clazz.isAnnotation()) {
return clazz;
}
} catch (ClassNotFoundException e) {
if (log.isDebugEnabled()) {
log.debug("Class not found for resolving from name '" + prefixedClassName + "'");
}
}
}
}
return null;
}
private void validateImportAndAdd(String importName, List<String> imports) throws EngineImportException {
if (!isClassName(importName) && !isPackageName(importName)) {
throw new EngineImportException("Invalid import name '" + importName + "'");
}
if (log.isDebugEnabled()) {
log.debug("Adding import " + importName);
}
imports.add(importName);
}
private enum MethodModifiers {
REQUIRE_STATIC_AND_PUBLIC("public static", true),
REQUIRE_NONSTATIC_AND_PUBLIC("public non-static", false);
private final String text;
private final boolean requiredStaticFlag;
MethodModifiers(String text, boolean requiredStaticFlag) {
this.text = text;
this.requiredStaticFlag = requiredStaticFlag;
}
public boolean acceptsPublicFlag(boolean isPublic) {
return isPublic;
}
public boolean acceptsStaticFlag(boolean isStatic) {
return requiredStaticFlag == isStatic;
}
public String getText() {
return text;
}
}
}