org.mule.mvel2.ParserConfiguration Maven / Gradle / Ivy
Go to download
MVEL is a powerful expression language for Java-based applications.
It provides a plethora of features and is suited for everything
from the smallest property binding and extraction, to full blown scripts.
This is a fork of MVEL customized for use in Mule.
/**
* MVEL 2.0
* Copyright (C) 2007 The Codehaus
* Mike Brock, Dhanji Prasanna, John Graham, Mark Proctor
*
* 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.mule.mvel2;
import org.mule.mvel2.ast.Proto;
import org.mule.mvel2.compiler.AbstractParser;
import org.mule.mvel2.integration.Interceptor;
import org.mule.mvel2.util.MethodStub;
import org.mule.mvel2.util.PropertyTools;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* The resusable parser configuration object.
*/
public class ParserConfiguration implements Serializable {
private static final int MAX_NEGATIVE_CACHE_SIZE;
protected Map imports;
protected HashSet packageImports;
protected Map interceptors;
protected transient ClassLoader classLoader;
private transient Set nonValidImports;
private boolean allowBootstrapBypass = true;
static {
String negCacheSize = System.getProperty("mvel2.compiler.max_neg_cache_size");
if (negCacheSize != null) {
MAX_NEGATIVE_CACHE_SIZE = Integer.parseInt(negCacheSize);
}
else {
MAX_NEGATIVE_CACHE_SIZE = 1000;
}
}
public ParserConfiguration() {
}
public ParserConfiguration(Map imports, Map interceptors) {
addAllImports(imports);
this.interceptors = interceptors;
}
public ParserConfiguration(Map imports, HashSet packageImports,
Map interceptors) {
addAllImports(imports);
this.packageImports = packageImports;
this.interceptors = interceptors;
}
public HashSet getPackageImports() {
return packageImports;
}
public void setPackageImports(HashSet packageImports) {
this.packageImports = packageImports;
}
public Class getImport(String name) {
if (imports != null && imports.containsKey(name) && imports.get(name) instanceof Class) {
return (Class) imports.get(name);
}
return (Class) (AbstractParser.LITERALS.get(name) instanceof Class ? AbstractParser.LITERALS.get(name) : null);
}
public MethodStub getStaticImport(String name) {
return imports != null ? (MethodStub) imports.get(name) : null;
}
public Object getStaticOrClassImport(String name) {
return (imports != null && imports.containsKey(name) ? imports.get(name) : AbstractParser.LITERALS.get(name));
}
public void addPackageImport(String packageName) {
if (packageImports == null) packageImports = new LinkedHashSet();
packageImports.add(packageName);
if (!addClassMemberStaticImports(packageName)) packageImports.add(packageName);
}
private boolean addClassMemberStaticImports(String packageName) {
try {
Class c = Class.forName(packageName);
initImports();
if (c.isEnum()) {
//noinspection unchecked
for (Enum e : (EnumSet>) EnumSet.allOf(c)) {
imports.put(e.name(), e);
}
return true;
}
else {
for (Field f : c.getDeclaredFields()) {
if ((f.getModifiers() & (Modifier.STATIC | Modifier.PUBLIC)) != 0) {
imports.put(f.getName(), f.get(null));
}
}
}
}
catch (ClassNotFoundException e) {
// do nothing.
}
catch (IllegalAccessException e) {
throw new RuntimeException("error adding static imports for: " + packageName, e);
}
return false;
}
public void addAllImports(Map imports) {
if (imports == null) return;
initImports();
Object o;
for (Map.Entry entry : imports.entrySet()) {
if ((o = entry.getValue()) instanceof Method) {
this.imports.put(entry.getKey(), new MethodStub((Method) o));
}
else {
this.imports.put(entry.getKey(), o);
}
}
}
private boolean checkForDynamicImport(String className) {
if (packageImports == null) return false;
if (!Character.isJavaIdentifierStart(className.charAt(0))) return false;
if (nonValidImports != null && nonValidImports.contains(className)) return false;
int found = 0;
Class cls = null;
for (String pkg : packageImports) {
try {
cls = Class.forName(pkg + "." + className, true, getClassLoader());
found++;
}
catch (ClassNotFoundException e) {
// do nothing.
}
catch (NoClassDefFoundError e) {
if (PropertyTools.contains(e.getMessage(), "wrong name")) {
// do nothing. this is a weirdness in the jvm.
// see MVEL-43
}
else {
throw e;
}
}
}
if (found > 1) throw new RuntimeException("ambiguous class name: " + className);
if (found == 1) {
addImport(className, cls);
return true;
}
cacheNegativeHitForDynamicImport(className);
return false;
}
public boolean hasImport(String name) {
return (imports != null && imports.containsKey(name)) ||
AbstractParser.CLASS_LITERALS.containsKey(name) ||
checkForDynamicImport(name);
}
private void initImports() {
if (this.imports == null) {
this.imports = new ConcurrentHashMap();
}
}
public void addImport(Class cls) {
initImports();
addImport(cls.getSimpleName(), cls);
}
public void addImport(String name, Class cls) {
initImports();
this.imports.put(name, cls);
}
public void addImport(String name, Proto proto) {
initImports();
this.imports.put(name, proto);
}
public void addImport(String name, Method method) {
addImport(name, new MethodStub(method));
}
public void addImport(String name, MethodStub method) {
initImports();
this.imports.put(name, method);
}
public Map getInterceptors() {
return interceptors;
}
public void setInterceptors(Map interceptors) {
this.interceptors = interceptors;
}
public Map getImports() {
return imports;
}
public void setImports(Map imports) {
if (imports == null) return;
Object val;
for (Map.Entry entry : imports.entrySet()) {
if ((val = entry.getValue()) instanceof Class) {
addImport(entry.getKey(), (Class) val);
}
else if (val instanceof Method) {
addImport(entry.getKey(), (Method) val);
}
else if (val instanceof MethodStub) {
addImport(entry.getKey(), (MethodStub) val);
}
else if (val instanceof Proto) {
addImport(entry.getKey(), (Proto) entry.getValue());
}
else {
throw new RuntimeException("invalid element in imports map: " + entry.getKey() + " (" + val + ")");
}
}
}
public boolean hasImports() {
return !(imports != null && imports.isEmpty()) || (packageImports != null && packageImports.size() != 0);
}
public ClassLoader getClassLoader() {
return classLoader == null ? classLoader = Thread.currentThread().getContextClassLoader() : classLoader;
}
public void setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public void setAllImports(Map imports) {
initImports();
this.imports.clear();
if (imports != null) this.imports.putAll(imports);
}
public void setImports(HashMap imports) {
// TODO: this method is here for backward compatibility. Could it be removed/deprecated?
setAllImports(imports);
}
private void cacheNegativeHitForDynamicImport(String negativeHit) {
if (nonValidImports == null) {
nonValidImports = new LinkedHashSet();
}
else if (nonValidImports.size() > 1000) {
Iterator i = nonValidImports.iterator();
i.next();
i.remove();
}
nonValidImports.add(negativeHit);
}
public void flushCaches() {
if (nonValidImports != null)
nonValidImports.clear();
}
public boolean isAllowBootstrapBypass() {
return allowBootstrapBypass;
}
public void setAllowBootstrapBypass(boolean allowBootstrapBypass) {
this.allowBootstrapBypass = allowBootstrapBypass;
}
}