AstValue.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/* Generated By:JJTree: Do not edit this line. AstValue.java */
package org.apache.el.parser;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import jakarta.el.ELException;
import jakarta.el.ELResolver;
import jakarta.el.LambdaExpression;
import jakarta.el.MethodInfo;
import jakarta.el.MethodReference;
import jakarta.el.PropertyNotFoundException;
import jakarta.el.ValueReference;
import org.apache.el.lang.ELSupport;
import org.apache.el.lang.EvaluationContext;
import org.apache.el.stream.Optional;
import org.apache.el.util.MessageFactory;
import org.apache.el.util.ReflectionUtil;
/**
* @author Jacob Hookom [jacob@hookom.net]
*/
public final class AstValue extends SimpleNode {
private static final Object[] EMPTY_ARRAY = new Object[0];
protected static class Target {
protected Object base;
protected Object property;
}
public AstValue(int id) {
super(id);
}
@Override
public Class<?> getType(EvaluationContext ctx) throws ELException {
Target t = getTarget(ctx);
ctx.setPropertyResolved(false);
Class<?> result = ctx.getELResolver().getType(ctx, t.base, t.property);
if (!ctx.isPropertyResolved()) {
throw new PropertyNotFoundException(MessageFactory.get("error.resolver.unhandled", t.base, t.property));
}
return result;
}
private Target getTarget(EvaluationContext ctx) throws ELException {
// evaluate expr-a to value-a
Object base = this.children[0].getValue(ctx);
// if our base is null (we know there are more properties to evaluate)
if (base == null) {
throw new PropertyNotFoundException(
MessageFactory.get("error.unreachable.base", this.children[0].getImage()));
}
// set up our start/end
Object property = null;
int propCount = this.jjtGetNumChildren();
int i = 1;
// Evaluate any properties or methods before our target
ELResolver resolver = ctx.getELResolver();
while (i < propCount) {
if (i + 2 < propCount && this.children[i + 1] instanceof AstMethodParameters) {
// Method call not at end of expression
base = resolver.invoke(ctx, base, this.children[i].getValue(ctx), null,
((AstMethodParameters) this.children[i + 1]).getParameters(ctx));
i += 2;
} else if (i + 2 == propCount && this.children[i + 1] instanceof AstMethodParameters) {
// Method call at end of expression
ctx.setPropertyResolved(false);
property = this.children[i].getValue(ctx);
i += 2;
if (property == null) {
throw new PropertyNotFoundException(MessageFactory.get("error.unreachable.property", property));
}
} else if (i + 1 < propCount) {
// Object with property not at end of expression
property = this.children[i].getValue(ctx);
ctx.setPropertyResolved(false);
base = resolver.getValue(ctx, base, property);
i++;
} else {
// Object with property at end of expression
ctx.setPropertyResolved(false);
property = this.children[i].getValue(ctx);
i++;
if (property == null) {
throw new PropertyNotFoundException(MessageFactory.get("error.unreachable.property", property));
}
}
if (base == null) {
throw new PropertyNotFoundException(MessageFactory.get("error.unreachable.property", property));
}
}
Target t = new Target();
t.base = base;
t.property = property;
return t;
}
@Override
public Object getValue(EvaluationContext ctx) throws ELException {
Object base = this.children[0].getValue(ctx);
int propCount = this.jjtGetNumChildren();
int i = 1;
Object suffix = null;
ELResolver resolver = ctx.getELResolver();
while (base != null && i < propCount) {
suffix = this.children[i].getValue(ctx);
if (i + 1 < propCount && (this.children[i + 1] instanceof AstMethodParameters)) {
AstMethodParameters mps = (AstMethodParameters) this.children[i + 1];
if (base instanceof Optional && "orElseGet".equals(suffix) && mps.jjtGetNumChildren() == 1) {
Node paramFoOptional = mps.jjtGetChild(0);
if (!(paramFoOptional instanceof AstLambdaExpression ||
paramFoOptional instanceof LambdaExpression)) {
throw new ELException(MessageFactory.get("stream.optional.paramNotLambda", suffix));
}
}
// This is a method
Object[] paramValues = mps.getParameters(ctx);
base = resolver.invoke(ctx, base, suffix, getTypesFromValues(paramValues), paramValues);
i += 2;
} else {
// This is a property
if (suffix == null) {
return null;
}
ctx.setPropertyResolved(false);
base = resolver.getValue(ctx, base, suffix);
i++;
}
}
if (!ctx.isPropertyResolved()) {
throw new PropertyNotFoundException(MessageFactory.get("error.resolver.unhandled", base, suffix));
}
return base;
}
@Override
public boolean isReadOnly(EvaluationContext ctx) throws ELException {
Target t = getTarget(ctx);
ctx.setPropertyResolved(false);
boolean result = ctx.getELResolver().isReadOnly(ctx, t.base, t.property);
if (!ctx.isPropertyResolved()) {
throw new PropertyNotFoundException(MessageFactory.get("error.resolver.unhandled", t.base, t.property));
}
return result;
}
@Override
public void setValue(EvaluationContext ctx, Object value) throws ELException {
Target t = getTarget(ctx);
ctx.setPropertyResolved(false);
ELResolver resolver = ctx.getELResolver();
// coerce to the expected type
Class<?> targetClass = resolver.getType(ctx, t.base, t.property);
resolver.setValue(ctx, t.base, t.property, ELSupport.coerceToType(ctx, value, targetClass));
if (!ctx.isPropertyResolved()) {
throw new PropertyNotFoundException(MessageFactory.get("error.resolver.unhandled", t.base, t.property));
}
}
@Override
// Interface el.parser.Node uses raw types (and is auto-generated)
public MethodInfo getMethodInfo(EvaluationContext ctx, @SuppressWarnings("rawtypes") Class[] paramTypes)
throws ELException {
Target t = getTarget(ctx);
Class<?>[] types = null;
if (isParametersProvided()) {
Object[] values = ((AstMethodParameters) this.jjtGetChild(this.jjtGetNumChildren() - 1)).getParameters(ctx);
types = getTypesFromValues(values);
} else {
types = paramTypes;
}
Method m = ReflectionUtil.getMethod(ctx, t.base, t.property, types, null);
return new MethodInfo(m.getName(), m.getReturnType(), m.getParameterTypes());
}
@Override
// Interface el.parser.Node uses a raw type (and is auto-generated)
public Object invoke(EvaluationContext ctx, @SuppressWarnings("rawtypes") Class[] paramTypes, Object[] paramValues)
throws ELException {
Target t = getTarget(ctx);
Method m = null;
Object[] values = null;
Class<?>[] types = null;
if (isParametersProvided()) {
values = ((AstMethodParameters) this.jjtGetChild(this.jjtGetNumChildren() - 1)).getParameters(ctx);
types = getTypesFromValues(values);
} else {
values = paramValues;
types = paramTypes;
}
m = ReflectionUtil.getMethod(ctx, t.base, t.property, types, values);
// Handle varArgs and any coercion required
values = convertArgs(ctx, values, m);
Object result = null;
try {
result = m.invoke(t.base, values);
} catch (IllegalAccessException | IllegalArgumentException e) {
throw new ELException(e);
} catch (InvocationTargetException ite) {
Throwable cause = ite.getCause();
if (cause instanceof VirtualMachineError) {
throw (VirtualMachineError) cause;
}
throw new ELException(cause);
}
return result;
}
@Override
public MethodReference getMethodReference(EvaluationContext ctx) {
Target t = getTarget(ctx);
Method m = null;
Object[] values = null;
Class<?>[] types = null;
if (isParametersProvided()) {
values = ((AstMethodParameters) this.jjtGetChild(this.jjtGetNumChildren() - 1)).getParameters(ctx);
types = getTypesFromValues(values);
}
m = ReflectionUtil.getMethod(ctx, t.base, t.property, types, values);
// Handle varArgs and any coercion required
values = convertArgs(ctx, values, m);
return new MethodReference(t.base, getMethodInfo(ctx, types), m.getAnnotations(), values);
}
private Object[] convertArgs(EvaluationContext ctx, Object[] src, Method m) {
Class<?>[] types = m.getParameterTypes();
if (types.length == 0) {
// Treated as if parameters have been provided so src is ignored
return EMPTY_ARRAY;
}
int paramCount = types.length;
if (m.isVarArgs() && paramCount > 1 && (src == null || paramCount > src.length) ||
!m.isVarArgs() && (paramCount > 0 && src == null || src != null && src.length != paramCount)) {
String srcCount = null;
if (src != null) {
srcCount = Integer.toString(src.length);
}
String msg;
if (m.isVarArgs()) {
msg = MessageFactory.get("error.invoke.tooFewParams", m.getName(), srcCount,
Integer.toString(paramCount));
} else {
msg = MessageFactory.get("error.invoke.wrongParams", m.getName(), srcCount,
Integer.toString(paramCount));
}
throw new IllegalArgumentException(msg);
}
if (src == null) {
// Must be a varargs method with a single parameter.
// Use a new array every time since the called code could modify the
// contents of the array
return new Object[1];
}
Object[] dest = new Object[paramCount];
for (int i = 0; i < paramCount - 1; i++) {
dest[i] = ELSupport.coerceToType(ctx, src[i], types[i]);
}
if (m.isVarArgs()) {
Class<?> varArgType = m.getParameterTypes()[paramCount - 1].getComponentType();
Object[] varArgs = (Object[]) Array.newInstance(varArgType, src.length - (paramCount - 1));
for (int i = 0; i < src.length - (paramCount - 1); i++) {
varArgs[i] = ELSupport.coerceToType(ctx, src[paramCount - 1 + i], varArgType);
}
dest[paramCount - 1] = varArgs;
} else {
dest[paramCount - 1] = ELSupport.coerceToType(ctx, src[paramCount - 1], types[paramCount - 1]);
}
return dest;
}
private Class<?>[] getTypesFromValues(Object[] values) {
if (values == null) {
return null;
}
Class<?> result[] = new Class<?>[values.length];
for (int i = 0; i < values.length; i++) {
if (values[i] == null) {
result[i] = null;
} else {
result[i] = values[i].getClass();
}
}
return result;
}
/**
* @since EL 2.2
*/
@Override
public ValueReference getValueReference(EvaluationContext ctx) {
// Check this is a reference to a base and a property
if (this.children.length > 2 && this.jjtGetChild(2) instanceof AstMethodParameters) {
// This is a method call
return null;
}
Target t = getTarget(ctx);
return new ValueReference(t.base, t.property);
}
/**
* @since EL 2.2
*/
@Override
public boolean isParametersProvided() {
// Assumption is that method parameters, if present, will be the last
// child
int len = children.length;
if (len > 2) {
if (this.jjtGetChild(len - 1) instanceof AstMethodParameters) {
return true;
}
}
return false;
}
}