| /* |
| * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. |
| * Copyright 2004 The Apache Software Foundation |
| * |
| * 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 javax.el; |
| |
| import java.util.Map; |
| import java.util.Stack; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.ArrayList; |
| import java.util.Locale; |
| |
| /** |
| * Context information for expression parsing and evaluation. |
| * |
| * <p>To parse or evaluate an {@link Expression}, an <code>ELContext</code> |
| * must be provided. The <code>ELContext</code> holds: |
| * <ul> |
| * <li>a reference to {@link FunctionMapper} that will be used |
| * to resolve EL Functions. This is used only in parsing.</li> |
| * <li>a reference to {@link VariableMapper} that will be used |
| * to resolve EL Variables. This is used only in parsing.</li> |
| * <li>a reference to the base {@link ELResolver} that will be consulted |
| * to resolve model objects and their properties</li> |
| * <li>a collection of all the relevant context objects for use by |
| * <code>ELResolver</code>s</li> |
| * <li>state information during the evaluation of an expression, such as |
| * whether a property has been resolved yet</li> |
| * <li>a reference to {@link ImportHandler} that will be consulted to |
| * resolve classes that have been imported</li> |
| * <li>a reference to the arguments for the active {@link LambdaExpression}s</li> |
| * <li>a reference to the list of registered evaluation listeners</li> |
| * </ul></p> |
| * |
| * <p>The collection of context objects is necessary because each |
| * <code>ELResolver</code> may need access to a different context object. |
| * For example, JSP and Faces resolvers need access to a |
| * {@link javax.servlet.jsp.JspContext} and a |
| * {@link javax.faces.context.FacesContext}, respectively.</p> |
| * |
| * <p>When used in a web container, the creation of |
| * <code>ELContext</code> objects is controlled through |
| * the underlying technology. For example, in JSP the |
| * <code>JspContext.getELContext()</code> factory method is used. |
| * Some technologies provide the ability to add an {@link ELContextListener} |
| * so that applications and frameworks can ensure their own context objects |
| * are attached to any newly created <code>ELContext</code>.</p> |
| * |
| * <p>When used in a stand-alone environment, {@link StandardELContext} |
| * provides a default <code>ELContext</code>, which is managed and modified |
| * by {@link ELManager}. |
| * |
| * <p>Because it stores state during expression evaluation, an |
| * <code>ELContext</code> object is not thread-safe. Care should be taken |
| * to never share an <code>ELContext</code> instance between two or more |
| * threads.</p> |
| * |
| * @see ELContextListener |
| * @see ELContextEvent |
| * @see ELResolver |
| * @see FunctionMapper |
| * @see VariableMapper |
| * @see ImportHandler |
| * @see LambdaExpression |
| * @see StandardELContext |
| * @see javax.servlet.jsp.JspContext |
| * @since EL 2.1 and EL 3.0 |
| */ |
| public abstract class ELContext { |
| |
| /** |
| * Called to indicate that a <code>ELResolver</code> has successfully |
| * resolved a given (base, property) pair. |
| * Use {@link #setPropertyResolved(Object, Object)} if |
| * resolved is true and to notify {@link EvaluationListener}s. |
| * |
| * <p>The {@link CompositeELResolver} checks this property to determine |
| * whether it should consider or skip other component resolvers.</p> |
| * |
| * @see CompositeELResolver |
| * @param resolved true if the property has been resolved, or false if |
| * not. |
| */ |
| public void setPropertyResolved(boolean resolved) { |
| this.resolved = resolved; |
| } |
| |
| /** |
| * Called to indicate that a <code>ELResolver</code> has successfully |
| * resolved a given (base, property) pair and to notify the |
| * {@link EvaluationListener}s. |
| * |
| * <p>The {@link CompositeELResolver} checks this property to determine |
| * whether it should consider or skip other component resolvers.</p> |
| * |
| * @see CompositeELResolver |
| * @param base The base object |
| * @param property The property object |
| * |
| * @since EL 3.0 |
| */ |
| public void setPropertyResolved(Object base, Object property) { |
| setPropertyResolved(true); // Don't set the variable here, for 2.2 users |
| // ELContext may be overridden or delegated. |
| notifyPropertyResolved(base, property); |
| } |
| |
| /** |
| * Returns whether an {@link ELResolver} has successfully resolved a |
| * given (base, property) pair. |
| * |
| * <p>The {@link CompositeELResolver} checks this property to determine |
| * whether it should consider or skip other component resolvers.</p> |
| * |
| * @see CompositeELResolver |
| * @return true if the property has been resolved, or false if not. |
| */ |
| public boolean isPropertyResolved() { |
| return resolved; |
| } |
| |
| /** |
| * Associates a context object with this <code>ELContext</code>. |
| * |
| * <p>The <code>ELContext</code> maintains a collection of context objects |
| * relevant to the evaluation of an expression. These context objects |
| * are used by <code>ELResolver</code>s. This method is used to |
| * add a context object to that collection.</p> |
| * |
| * <p>By convention, the <code>contextObject</code> will be of the |
| * type specified by the <code>key</code>. However, this is not |
| * required and the key is used strictly as a unique identifier.</p> |
| * |
| * @param key The key used by an @{link ELResolver} to identify this |
| * context object. |
| * @param contextObject The context object to add to the collection. |
| * @throws NullPointerException if key is null or contextObject is null. |
| */ |
| public void putContext(Class key, Object contextObject) { |
| if((key == null) || (contextObject == null)) { |
| throw new NullPointerException(); |
| } |
| map.put(key, contextObject); |
| } |
| |
| /** |
| * Returns the context object associated with the given key. |
| * |
| * <p>The <code>ELContext</code> maintains a collection of context objects |
| * relevant to the evaluation of an expression. These context objects |
| * are used by <code>ELResolver</code>s. This method is used to |
| * retrieve the context with the given key from the collection.</p> |
| * |
| * <p>By convention, the object returned will be of the type specified by |
| * the <code>key</code>. However, this is not required and the key is |
| * used strictly as a unique identifier.</p> |
| * |
| * @param key The unique identifier that was used to associate the |
| * context object with this <code>ELContext</code>. |
| * @return The context object associated with the given key, or null |
| * if no such context was found. |
| * @throws NullPointerException if key is null. |
| */ |
| public Object getContext(Class key) { |
| if(key == null) { |
| throw new NullPointerException(); |
| } |
| return map.get(key); |
| } |
| |
| /** |
| * Retrieves the <code>ELResolver</code> associated with this context. |
| * |
| * <p>The <code>ELContext</code> maintains a reference to the |
| * <code>ELResolver</code> that will be consulted to resolve variables |
| * and properties during an expression evaluation. This method |
| * retrieves the reference to the resolver.</p> |
| * |
| * <p>Once an <code>ELContext</code> is constructed, the reference to the |
| * <code>ELResolver</code> associated with the context cannot be changed.</p> |
| * |
| * @return The resolver to be consulted for variable and |
| * property resolution during expression evaluation. |
| */ |
| public abstract ELResolver getELResolver(); |
| |
| /** |
| * Retrieves the <code>ImportHandler</code> associated with this |
| * <code>ELContext</code>. |
| * |
| * @return The import handler to manage imports of classes and packages. |
| * @since EL 3.0 |
| */ |
| public ImportHandler getImportHandler() { |
| if (importHandler == null) { |
| importHandler = new ImportHandler(); |
| } |
| return importHandler; |
| } |
| |
| /** |
| * Retrieves the <code>FunctionMapper</code> associated with this |
| * <code>ELContext</code>. |
| * |
| * @return The function mapper to be consulted for the resolution of |
| * EL functions. |
| */ |
| public abstract FunctionMapper getFunctionMapper(); |
| |
| /** |
| * Holds value of property locale. |
| */ |
| private Locale locale; |
| |
| /** |
| * Get the <code>Locale</code> stored by a previous invocation to |
| * {@link #setLocale}. If this method returns non <code>null</code>, |
| * this <code>Locale</code> must be used for all localization needs |
| * in the implementation. The <code>Locale</code> must not be cached |
| * to allow for applications that change <code>Locale</code> dynamically. |
| * |
| * @return The <code>Locale</code> in which this instance is operating. |
| * Used primarily for message localization. |
| */ |
| |
| public Locale getLocale() { |
| |
| return this.locale; |
| } |
| |
| /** |
| * Sets the <code>Locale</code> for this instance. This method may be |
| * called by the party creating the instance, such as JavaServer |
| * Faces or JSP, to enable the EL implementation to provide localized |
| * messages to the user. If no <code>Locale</code> is set, the implementation |
| * must use the locale returned by <code>Locale.getDefault( )</code>. |
| */ |
| public void setLocale(Locale locale) { |
| |
| this.locale = locale; |
| } |
| |
| |
| /** |
| * Retrieves the <code>VariableMapper</code> associated with this |
| * <code>ELContext</code>. |
| * |
| * @return The variable mapper to be consulted for the resolution of |
| * EL variables. |
| */ |
| public abstract VariableMapper getVariableMapper(); |
| |
| /** |
| * Registers an evaluation listener to the ELContext. |
| * |
| * @param listener The listener to be added. |
| * |
| * @since EL 3.0 |
| */ |
| public void addEvaluationListener(EvaluationListener listener) { |
| if (listeners == null) { |
| listeners = new ArrayList<EvaluationListener>(); |
| } |
| listeners.add(listener); |
| } |
| |
| /** |
| * Returns the list of registered evaluation listeners. |
| * @return The list of registered evaluation listeners. |
| * |
| * @since EL 3.0 |
| */ |
| public List<EvaluationListener> getEvaluationListeners() { |
| return listeners; |
| } |
| |
| /** |
| * Notifies the listeners before an EL expression is evaluated |
| * @param expr The EL expression string to be evaluated |
| */ |
| public void notifyBeforeEvaluation(String expr) { |
| if (getEvaluationListeners() == null) |
| return; |
| for (EvaluationListener listener: getEvaluationListeners()) { |
| listener.beforeEvaluation(this, expr); |
| } |
| } |
| |
| /** |
| * Notifies the listeners after an EL expression is evaluated |
| * @param expr The EL expression string that has been evaluated |
| */ |
| public void notifyAfterEvaluation(String expr) { |
| if (getEvaluationListeners() == null) |
| return; |
| for (EvaluationListener listener: getEvaluationListeners()) { |
| listener.afterEvaluation(this, expr); |
| } |
| } |
| |
| /** |
| * Notifies the listeners when the (base, property) pair is resolved |
| * @param base The base object |
| * @param property The property Object |
| */ |
| public void notifyPropertyResolved(Object base, Object property) { |
| if (getEvaluationListeners() == null) |
| return; |
| for (EvaluationListener listener: getEvaluationListeners()) { |
| listener.propertyResolved(this, base, property); |
| } |
| } |
| |
| /** |
| * Inquires if the name is a LambdaArgument |
| * @param arg A possible Lambda formal parameter name |
| * @return true if arg is a LambdaArgument, false otherwise. |
| */ |
| public boolean isLambdaArgument(String arg) { |
| if (lambdaArgs == null) { |
| return false; |
| } |
| |
| for (int i = lambdaArgs.size() - 1; i >= 0; i--) { |
| Map<String, Object> lmap = lambdaArgs.elementAt(i); |
| if (lmap.containsKey(arg)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Retrieves the Lambda argument associated with a formal parameter. |
| * If the Lambda expression is nested within other Lambda expressions, the |
| * arguments for the current Lambda expression is first searched, and if |
| * not found, the arguments for the immediate nesting Lambda expression |
| * then searched, and so on. |
| * |
| * @param arg The formal parameter for the Lambda argument |
| * @return The object associated with formal parameter. Null if |
| * no object has been associated with the parameter. |
| * @since EL 3.0 |
| */ |
| public Object getLambdaArgument(String arg) { |
| if (lambdaArgs == null) { |
| return null; |
| } |
| |
| for (int i = lambdaArgs.size() - 1; i >= 0; i--) { |
| Map<String, Object> lmap = lambdaArgs.elementAt(i); |
| Object v = lmap.get(arg); |
| if (v != null) { |
| return v; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Installs a Lambda argument map, in preparation for the evaluation |
| * of a Lambda expression. The arguments in the map will be in scope |
| * during the evaluation of the Lambda expression. |
| * @param args The Lambda arguments map |
| * @since EL 3.0 |
| */ |
| public void enterLambdaScope(Map<String,Object> args) { |
| if (lambdaArgs == null) { |
| lambdaArgs = new Stack<Map<String,Object>>(); |
| } |
| lambdaArgs.push(args); |
| } |
| |
| /** |
| * Exits the Lambda expression evaluation. The Lambda argument map that |
| * was previously installed is removed. |
| * @since EL 3.0 |
| */ |
| public void exitLambdaScope() { |
| if (lambdaArgs != null) { |
| lambdaArgs.pop(); |
| } |
| } |
| |
| /** |
| * Converts an object to a specific type. If a custom converter in the |
| * <code>ELResolver</code> handles this conversion, it is used. Otherwise |
| * the standard coercions is applied. |
| * |
| * <p>An <code>ELException</code> is thrown if an error occurs during |
| * the conversion.</p> |
| * |
| * @param obj The object to convert. |
| * @param targetType The target type for the conversion. |
| * @throws ELException thrown if errors occur. |
| * |
| * @since EL 3.0 |
| */ |
| public Object convertToType(Object obj, |
| Class<?> targetType) { |
| boolean propertyResolvedSave = isPropertyResolved(); |
| try { |
| setPropertyResolved(false); |
| ELResolver elResolver = getELResolver(); |
| if (elResolver != null) { |
| Object res = elResolver.convertToType(this, obj, targetType); |
| if (isPropertyResolved()) { |
| return res; |
| } |
| } |
| } catch (ELException ex) { |
| throw ex; |
| } catch (Exception ex) { |
| throw new ELException(ex); |
| } finally { |
| setPropertyResolved(propertyResolvedSave); |
| } |
| |
| ExpressionFactory exprFactory = (ExpressionFactory)getContext(ExpressionFactory.class); |
| if (exprFactory == null) { |
| exprFactory = ELUtil.getExpressionFactory(); |
| } |
| return exprFactory.coerceToType(obj, targetType); |
| } |
| |
| private boolean resolved; |
| private HashMap<Class<?>, Object> map = new HashMap<Class<?>, Object>(); |
| private transient List<EvaluationListener> listeners = null; |
| private Stack<Map<String,Object>> lambdaArgs; |
| private ImportHandler importHandler; |
| } |
| |