| /* |
| * 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.List; |
| import java.util.Map; |
| import java.util.HashMap; |
| import java.util.ArrayList; |
| |
| /** |
| * <p>Encapsulates a parameterized {@link ValueExpression}.</p> |
| * |
| * <p>A <code>LambdaExpression</code> is a representation of the EL Lambda |
| * expression syntax. It consists of a list of the formal parameters and a |
| * body, represented by a {@link ValueExpression}. |
| * The body can be any valid <code>Expression</code>, including another |
| * <code>LambdaExpression</code>.</p> |
| * A <code>LambdaExpression</code> is created when an EL expression containing |
| * a Lambda expression is evaluated.</p> |
| * <p>A <code>LambdaExpression</code> can be invoked by calling |
| * {@link LambdaExpression#invoke}, with |
| * an {@link javax.el.ELContext} and a list of the actual arguments. |
| * Alternately, a <code>LambdaExpression</code> can be invoked without passing |
| * a <code>ELContext</code>, in which case the <code>ELContext</code> previously |
| * set by calling {@link LambdaExpression#setELContext} will be used. |
| * The evaluation of the <code>ValueExpression</code> in the body uses the |
| * {@link ELContext} to resolve references to the parameters, and to evaluate |
| * the lambda expression. |
| * The result of the evaluation is returned.</p> |
| * @see ELContext#getLambdaArgument |
| * @see ELContext#enterLambdaScope |
| * @see ELContext#exitLambdaScope |
| */ |
| |
| public class LambdaExpression { |
| |
| private List<String> formalParameters = new ArrayList<String>(); |
| private ValueExpression expression; |
| private ELContext context; |
| // Arguments from nesting lambdas, when the body is another lambda |
| private Map<String, Object> envirArgs = null; |
| |
| /** |
| * Creates a new LambdaExpression. |
| * @param formalParameters The list of String representing the formal |
| * parameters. |
| * @param expression The <code>ValueExpression</code> representing the |
| * body. |
| */ |
| public LambdaExpression (List<String> formalParameters, |
| ValueExpression expression) { |
| this.formalParameters = formalParameters; |
| this.expression = expression; |
| this.envirArgs = new HashMap<String, Object>(); |
| } |
| |
| /** |
| * Set the ELContext to use in evaluating the LambdaExpression. |
| * The ELContext must to be set prior to the invocation of the LambdaExpression, |
| * unless it is supplied with {@link LambdaExpression#invoke}. |
| * @param context The ELContext to use in evaluating the LambdaExpression. |
| */ |
| public void setELContext(ELContext context) { |
| this.context = context; |
| } |
| |
| /** |
| * Invoke the encapsulated Lambda expression. |
| * <p> The supplied arguments are matched, in |
| * the same order, to the formal parameters. If there are more arguments |
| * than the formal parameters, the extra arguments are ignored. If there |
| * are less arguments than the formal parameters, an |
| * <code>ELException</code> is thrown.</p> |
| * |
| * <p>The actual Lambda arguments are added to the ELContext and are |
| * available during the evaluation of the Lambda expression. They are |
| * removed after the evaluation.</p> |
| * |
| * @param elContext The ELContext used for the evaluation of the expression |
| * The ELContext set by {@link #setELContext} is ignored. |
| * @param args The arguments to invoke the Lambda expression. For calls with |
| * no arguments, an empty array must be provided. A Lambda argument |
| * can be <code>null</code>. |
| * @return The result of invoking the Lambda expression |
| * @throws ELException if not enough arguments are provided |
| * @throws NullPointerException is elContext is null |
| */ |
| public Object invoke(ELContext elContext, Object... args) |
| throws ELException { |
| int i = 0; |
| Map<String, Object> lambdaArgs = new HashMap<String, Object>(); |
| |
| // First get arguments injected from the outter lambda, if any |
| lambdaArgs.putAll(envirArgs); |
| |
| for (String fParam: formalParameters) { |
| if (i >= args.length) { |
| throw new ELException("Expected Argument " + fParam + |
| " missing in Lambda Expression"); |
| } |
| lambdaArgs.put(fParam, args[i++]); |
| } |
| |
| elContext.enterLambdaScope(lambdaArgs); |
| Object ret = expression.getValue(elContext); |
| |
| // If the result of evaluating the body is another LambdaExpression, |
| // whose body has not been evaluated yet. (A LambdaExpression is |
| // evaluated iff when its invoke method is called.) The current lambda |
| // arguments may be needed in that body when it is evaluated later, |
| // after the current lambda exits. To make these arguments available |
| // then, they are injected into it. |
| if (ret instanceof LambdaExpression) { |
| ((LambdaExpression) ret).envirArgs.putAll(lambdaArgs); |
| } |
| elContext.exitLambdaScope(); |
| return ret; |
| } |
| |
| /** |
| * Invoke the encapsulated Lambda expression. |
| * <p> The supplied arguments are matched, in |
| * the same order, to the formal parameters. If there are more arguments |
| * than the formal parameters, the extra arguments are ignored. If there |
| * are less arguments than the formal parameters, an |
| * <code>ELException</code> is thrown.</p> |
| * |
| * <p>The actual Lambda arguments are added to the ELContext and are |
| * available during the evaluation of the Lambda expression. They are |
| * removed after the evaluation.</p> |
| * |
| * The ELContext set by {@link LambdaExpression#setELContext} is used in |
| * the evaluation of the lambda Expression. |
| * |
| * @param args The arguments to invoke the Lambda expression. For calls with |
| * no arguments, an empty array must be provided. A Lambda argument |
| * can be <code>null</code>. |
| * @return The result of invoking the Lambda expression |
| * @throws ELException if not enough arguments are provided |
| */ |
| public Object invoke(Object... args) { |
| return invoke(this.context, args); |
| } |
| } |