| /* |
| * Copyright (c) 2012, 2019 Oracle and/or its affiliates and others. |
| * 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 jakarta.el; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Encapsulates a parameterized {@link ValueExpression}. |
| * |
| * <p> |
| * A <code>LambdaExpression</code> is a representation of the Jakarta Expression Language 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 Jakarta Expression Language expression containing a Lambda |
| * expression is evaluated. |
| * |
| * <p> |
| * A <code>LambdaExpression</code> can be invoked by calling {@link LambdaExpression#invoke}, with an |
| * {@link 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. |
| * |
| * @see ELContext#getLambdaArgument |
| * @see ELContext#enterLambdaScope |
| * @see ELContext#exitLambdaScope |
| */ |
| public class LambdaExpression { |
| |
| private List<String> formalParameters = new ArrayList<>(); |
| private ValueExpression expression; |
| private ELContext context; |
| // Arguments from nesting lambdas, when the body is another lambda |
| private Map<String, Object> envirArgs; |
| |
| /** |
| * 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<>(); |
| } |
| |
| /** |
| * 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<>(); |
| |
| // 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); |
| try { |
| 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); |
| } |
| return ret; |
| } finally { |
| elContext.exitLambdaScope(); |
| } |
| |
| |
| } |
| |
| /** |
| * 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(context, args); |
| } |
| } |