All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.omnifaces.el.MethodExpressionValueExpressionAdapter Maven / Gradle / Ivy

There is a newer version: 4.4.1
Show newest version
/*
 * Copyright OmniFaces
 *
 * 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
 *
 *     https://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 org.omnifaces.el;

import static java.util.logging.Level.FINEST;
import static org.omnifaces.util.Utils.unmodifiableSet;

import java.util.Set;
import java.util.logging.Logger;

import jakarta.el.ELContext;
import jakarta.el.ELException;
import jakarta.el.ELResolver;
import jakarta.el.MethodExpression;
import jakarta.el.MethodInfo;
import jakarta.el.MethodNotFoundException;
import jakarta.el.PropertyNotFoundException;
import jakarta.el.ValueExpression;
import jakarta.faces.convert.ConverterException;
import jakarta.faces.validator.ValidatorException;

/**
 * This MethodExpression wraps a ValueExpression.
 * 

* With this wrapper a value expression can be used where a method expression is expected. * The return value of the method execution will be the value represented by the value expression. * * @author Arjan Tijms * */ public class MethodExpressionValueExpressionAdapter extends MethodExpression { private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(MethodExpressionValueExpressionAdapter.class.getName()); private static final Set> EXCEPTIONS_TO_UNWRAP = unmodifiableSet( MethodNotFoundException.class, // Needed for proper action listener error handling. ConverterException.class, // Needed for proper conversion error handling. ValidatorException.class); // Needed for proper validation error handling. private final ValueExpression valueExpression; /** * Construct method expression which adapts the given value expression. * @param valueExpression Value expression to be adapted to method expression. */ public MethodExpressionValueExpressionAdapter(ValueExpression valueExpression) { this.valueExpression = valueExpression; } @Override public Object invoke(ELContext context, Object[] params) { try { return valueExpression.getValue(new ValueToInvokeElContext(context, params)); } catch (ELException e) { for (Throwable cause = e.getCause(); cause != null; cause = cause.getCause()) { if (EXCEPTIONS_TO_UNWRAP.contains(cause.getClass())) { throw (RuntimeException) cause; } } throw e; } } @Override public MethodInfo getMethodInfo(ELContext context) { return ExpressionInspector.getMethodReference(context, valueExpression); } @Override public boolean isLiteralText() { return false; } @Override public int hashCode() { return valueExpression.hashCode(); } @Override public String getExpressionString() { return valueExpression.getExpressionString(); } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof MethodExpressionValueExpressionAdapter) { return ((MethodExpressionValueExpressionAdapter)obj).getValueExpression().equals(valueExpression); } return false; } /** * Returns the underlying value expression. * @return The underlying value expression. */ public ValueExpression getValueExpression() { return valueExpression; } /** * Custom ELContext implementation that wraps a given ELContext to be able to provide a custom * ElResolver. * */ static class ValueToInvokeElContext extends ELContextWrapper { // The parameters provided by the client that calls the EL method expression, as opposed to those // parameters that are bound to the expression when it's created in EL (like #{bean.myMethod(param1, param2)}). private final Object[] callerProvidedParameters; public ValueToInvokeElContext(ELContext elContext, Object[] callerProvidedParameters) { super(elContext); this.callerProvidedParameters = callerProvidedParameters; } @Override public ELResolver getELResolver() { return new ValueToInvokeElResolver(super.getELResolver(), callerProvidedParameters); } } /** * Custom EL Resolver that turns calls for value expression calls (getValue) into method expression calls (invoke). * */ static class ValueToInvokeElResolver extends ELResolverWrapper { // Null should theoretically be accepted, but some EL implementations want an empty array. private static final Object[] EMPTY_PARAMETERS = new Object[0]; private final Object[] callerProvidedParameters; public ValueToInvokeElResolver(ELResolver elResolver, Object[] callerProvidedParameters) { super(elResolver); this.callerProvidedParameters = callerProvidedParameters; } @Override public Object getValue(ELContext context, Object base, Object property) { // If base is null, we're resolving it. Base should always be resolved as a value expression. if (base == null) { return super.getValue(context, null, property); } // Turn getValue calls into invoke. // Note 1: We can not directly delegate to invoke() here, since otherwise chained expressions // "like base.value.value.expression" will not resolve correctly. // // Note 2: Some EL implementations call getValue when invoke should be called already. This typically happens when the // method expression takes no parameters, but is specified with parentheses, e.g. "#{mybean.doAction()}". try { return super.getValue(context, base, property); } catch (PropertyNotFoundException ignore) { logger.log(FINEST, "Ignoring thrown exception; there is really no clean way to distinguish a ValueExpression from a MethodExpression.", ignore); try { return super.invoke(context, base, property, null, callerProvidedParameters != null ? callerProvidedParameters : EMPTY_PARAMETERS); } catch (MethodNotFoundException e) { // Wrap into new ELException since down the call chain, ElExceptions might be caught, unwrapped one level and then wrapped in // a new ELException. Without wrapping here, we'll then loose this exception. throw new ELException(e.getMessage(), e); } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy