blob: fa901fcb0fbe9858b7230aa8a703eff3031c55b1 [file] [log] [blame]
/*
* Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package javax.el;
import java.util.Map;
import java.util.HashMap;
import java.lang.reflect.Method;
/**
* A standard ELContext suitable for use in a stand alone environment.
* This class provides a default implementation of an ELResolver that contains
* a number of useful ELResolvers. It also provides local repositories for
* the FunctionMapper, VariableMapper, and BeanNameResolver.
*
* @since EL 3.0
*/
public class StandardELContext extends ELContext {
/*
* The ELResolver for this ELContext.
*/
private ELResolver elResolver;
/*
* The list of the custom ELResolvers added to the ELResolvers.
* An ELResolver is added to the list when addELResolver is called.
*/
private CompositeELResolver customResolvers;
/*
* The ELResolver implementing the query operators.
*/
private ELResolver streamELResolver;
/*
* The FunctionMapper for this ELContext.
*/
private FunctionMapper functionMapper;
/*
* The pre-confured init function map;
*/
private Map<String, Method> initFunctionMap;
/*
* The VariableMapper for this ELContext.
*/
private VariableMapper variableMapper;
/*
* If non-null, indicates the presence of a delegate ELContext.
* When a Standard is constructed from another ELContext, there is no
* easy way to get its private context map, therefore delegation is needed.
*/
private ELContext delegate = null;
/**
* A bean repository local to this context
*/
private Map<String, Object> beans = new HashMap<String, Object>();
/**
* Construct a default ELContext for a stand-alone environment.
* @param factory The ExpressionFactory
*/
public StandardELContext(ExpressionFactory factory) {
this.streamELResolver = factory.getStreamELResolver();
initFunctionMap = factory.getInitFunctionMap();
}
/**
* Construct a StandardELContext from another ELContext.
* @param context The ELContext that acts as a delegate in most cases
*/
public StandardELContext(ELContext context) {
this.delegate = context;
// Copy all attributes except map and resolved
CompositeELResolver elr = new CompositeELResolver();
elr.add(new BeanNameELResolver(new LocalBeanNameResolver()));
customResolvers = new CompositeELResolver();
elr.add(customResolvers);
elr.add(context.getELResolver());
elResolver = elr;
functionMapper = context.getFunctionMapper();
variableMapper = context.getVariableMapper();
setLocale(context.getLocale());
}
@Override
public void putContext(Class key, Object contextObject) {
if (delegate !=null) {
delegate.putContext(key, contextObject);
} else {
super.putContext(key, contextObject);
}
}
@Override
public Object getContext(Class key) {
if (delegate !=null) {
return delegate.getContext(key);
} else {
return super.getContext(key);
}
}
/**
* Construct (if needed) and return a default ELResolver.
* <p>Retrieves the <code>ELResolver</code> associated with this context.
* This is a <code>CompositeELResover</code> consists of an ordered list of
* <code>ELResolver</code>s.
* <ol>
* <li>A {@link BeanNameELResolver} for beans defined locally</li>
* <li>Any custom <code>ELResolver</code>s</li>
* <li>An <code>ELResolver</code> supporting the collection operations</li>
* <li>A {@link StaticFieldELResolver} for resolving static fields</li>
* <li>A {@link MapELResolver} for resolving Map properties</li>
* <li>A {@link ResourceBundleELResolver} for resolving ResourceBundle properties</li>
* <li>A {@link ListELResolver} for resolving List properties</li>
* <li>An {@link ArrayELResolver} for resolving array properties</li>
* <li>A {@link BeanELResolver} for resolving bean properties</li>
* </ol>
* </p>
* @return The ELResolver for this context.
*/
@Override
public ELResolver getELResolver() {
if (elResolver == null) {
CompositeELResolver resolver = new CompositeELResolver();
customResolvers = new CompositeELResolver();
resolver.add(customResolvers);
resolver.add(new BeanNameELResolver(new LocalBeanNameResolver()));
if (streamELResolver != null) {
resolver.add(streamELResolver);
}
resolver.add(new StaticFieldELResolver());
resolver.add(new MapELResolver());
resolver.add(new ResourceBundleELResolver());
resolver.add(new ListELResolver());
resolver.add(new ArrayELResolver());
resolver.add(new BeanELResolver());
elResolver = resolver;
}
return elResolver;
}
/**
* Add a custom ELResolver to the context. The list of the custom
* ELResolvers will be accessed in the order they are added.
* A custom ELResolver added to the context cannot be removed.
* @param cELResolver The new ELResolver to be added to the context
*/
public void addELResolver(ELResolver cELResolver) {
getELResolver(); // make sure elResolver is constructed
customResolvers.add(cELResolver);
}
/**
* Get the local bean repository
* @return the bean repository
*/
Map<String, Object> getBeans() {
return beans;
}
/**
* Construct (if needed) and return a default FunctionMapper.
* @return The default FunctionMapper
*/
@Override
public FunctionMapper getFunctionMapper() {
if (functionMapper == null) {
functionMapper = new DefaultFunctionMapper(initFunctionMap);
}
return functionMapper;
}
/**
* Construct (if needed) and return a default VariableMapper() {
* @return The default Variable
*/
@Override
public VariableMapper getVariableMapper() {
if (variableMapper == null) {
variableMapper = new DefaultVariableMapper();
}
return variableMapper;
}
private static class DefaultFunctionMapper extends FunctionMapper {
private Map<String, Method> functions = null;
DefaultFunctionMapper(Map<String, Method> initMap){
functions = (initMap == null)?
new HashMap<String, Method>():
new HashMap<String, Method>(initMap);
}
@Override
public Method resolveFunction(String prefix, String localName) {
return functions.get(prefix + ":" + localName);
}
@Override
public void mapFunction(String prefix, String localName, Method meth){
functions.put(prefix + ":" + localName, meth);
}
}
private static class DefaultVariableMapper extends VariableMapper {
private Map<String, ValueExpression> variables = null;
@Override
public ValueExpression resolveVariable (String variable) {
if (variables == null) {
return null;
}
return variables.get(variable);
}
@Override
public ValueExpression setVariable(String variable,
ValueExpression expression) {
if (variables == null) {
variables = new HashMap<String, ValueExpression>();
}
ValueExpression prev = null;
if (expression == null) {
prev = variables.remove(variable);
} else {
prev = variables.put(variable, expression);
}
return prev;
}
}
private class LocalBeanNameResolver extends BeanNameResolver {
@Override
public boolean isNameResolved(String beanName) {
return beans.containsKey(beanName);
}
@Override
public Object getBean(String beanName) {
return beans.get(beanName);
}
@Override
public void setBeanValue(String beanName, Object value) {
beans.put(beanName, value);
}
@Override
public boolean isReadOnly(String beanName) {
return false;
}
@Override
public boolean canCreateBean(String beanName) {
return true;
}
}
}