jakarta.el.LambdaExpression Maven / Gradle / Ivy
/*
* 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}.
*
*
* A LambdaExpression
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 Expression
, including another LambdaExpression
.
*
*
* A LambdaExpression
is created when an Jakarta Expression Language expression containing a Lambda
* expression is evaluated.
*
*
* A LambdaExpression
can be invoked by calling {@link LambdaExpression#invoke}, with an
* {@link ELContext} and a list of the actual arguments. Alternately, a LambdaExpression
can be
* invoked without passing a ELContext
, in which case the ELContext
previously set by calling
* {@link LambdaExpression#setELContext} will be used. The evaluation of the ValueExpression
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 formalParameters = new ArrayList<>();
private ValueExpression expression;
private ELContext context;
// Arguments from nesting lambdas, when the body is another lambda
private Map envirArgs;
/**
* Creates a new LambdaExpression.
*
* @param formalParameters The list of String representing the formal parameters.
* @param expression The ValueExpression
representing the body.
*/
public LambdaExpression(List 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.
*
* 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
* ELException
is thrown.
*
*
*
* 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.
*
*
* @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 null
.
* @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 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.
*
* 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
* ELException
is thrown.
*
*
*
* 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.
*
*
* 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 null
.
* @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);
}
}