org.mvel2.optimizers.impl.refl.ReflectiveAccessorOptimizer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of virtdata-lib-realer Show documentation
Show all versions of virtdata-lib-realer Show documentation
With inspiration from other libraries
/**
* MVEL 2.0
* Copyright (C) 2007 The Codehaus
* Mike Brock, Dhanji Prasanna, John Graham, Mark Proctor
*
* 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
*
* 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.
*/
package org.mvel2.optimizers.impl.refl;
import org.mvel2.CompileException;
import org.mvel2.MVEL;
import org.mvel2.OptimizationFailure;
import org.mvel2.ParserContext;
import org.mvel2.PropertyAccessException;
import org.mvel2.ast.FunctionInstance;
import org.mvel2.ast.TypeDescriptor;
import org.mvel2.compiler.Accessor;
import org.mvel2.compiler.AccessorNode;
import org.mvel2.compiler.ExecutableLiteral;
import org.mvel2.compiler.ExecutableStatement;
import org.mvel2.compiler.PropertyVerifier;
import org.mvel2.integration.GlobalListenerFactory;
import org.mvel2.integration.PropertyHandler;
import org.mvel2.integration.VariableResolver;
import org.mvel2.integration.VariableResolverFactory;
import org.mvel2.optimizers.AbstractOptimizer;
import org.mvel2.optimizers.AccessorOptimizer;
import org.mvel2.optimizers.impl.refl.collection.ArrayCreator;
import org.mvel2.optimizers.impl.refl.collection.ExprValueAccessor;
import org.mvel2.optimizers.impl.refl.collection.ListCreator;
import org.mvel2.optimizers.impl.refl.collection.MapCreator;
import org.mvel2.optimizers.impl.refl.nodes.ArrayAccessor;
import org.mvel2.optimizers.impl.refl.nodes.ArrayAccessorNest;
import org.mvel2.optimizers.impl.refl.nodes.ArrayLength;
import org.mvel2.optimizers.impl.refl.nodes.ConstructorAccessor;
import org.mvel2.optimizers.impl.refl.nodes.DynamicFieldAccessor;
import org.mvel2.optimizers.impl.refl.nodes.DynamicFunctionAccessor;
import org.mvel2.optimizers.impl.refl.nodes.FieldAccessor;
import org.mvel2.optimizers.impl.refl.nodes.FieldAccessorNH;
import org.mvel2.optimizers.impl.refl.nodes.FunctionAccessor;
import org.mvel2.optimizers.impl.refl.nodes.GetterAccessor;
import org.mvel2.optimizers.impl.refl.nodes.GetterAccessorNH;
import org.mvel2.optimizers.impl.refl.nodes.IndexedCharSeqAccessor;
import org.mvel2.optimizers.impl.refl.nodes.IndexedCharSeqAccessorNest;
import org.mvel2.optimizers.impl.refl.nodes.IndexedVariableAccessor;
import org.mvel2.optimizers.impl.refl.nodes.ListAccessor;
import org.mvel2.optimizers.impl.refl.nodes.ListAccessorNest;
import org.mvel2.optimizers.impl.refl.nodes.MapAccessor;
import org.mvel2.optimizers.impl.refl.nodes.MapAccessorNest;
import org.mvel2.optimizers.impl.refl.nodes.MethodAccessor;
import org.mvel2.optimizers.impl.refl.nodes.MethodAccessorNH;
import org.mvel2.optimizers.impl.refl.nodes.Notify;
import org.mvel2.optimizers.impl.refl.nodes.NullSafe;
import org.mvel2.optimizers.impl.refl.nodes.PropertyHandlerAccessor;
import org.mvel2.optimizers.impl.refl.nodes.SetterAccessor;
import org.mvel2.optimizers.impl.refl.nodes.StaticReferenceAccessor;
import org.mvel2.optimizers.impl.refl.nodes.StaticVarAccessor;
import org.mvel2.optimizers.impl.refl.nodes.StaticVarAccessorNH;
import org.mvel2.optimizers.impl.refl.nodes.ThisValueAccessor;
import org.mvel2.optimizers.impl.refl.nodes.Union;
import org.mvel2.optimizers.impl.refl.nodes.VariableAccessor;
import org.mvel2.optimizers.impl.refl.nodes.WithAccessor;
import org.mvel2.util.ArrayTools;
import org.mvel2.util.ErrorUtil;
import org.mvel2.util.MethodStub;
import org.mvel2.util.NullType;
import org.mvel2.util.ParseTools;
import org.mvel2.util.PropertyTools;
import org.mvel2.util.StringAppender;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import static java.lang.Integer.parseInt;
import static java.lang.Thread.currentThread;
import static java.lang.reflect.Array.getLength;
import static org.mvel2.DataConversion.canConvert;
import static org.mvel2.DataConversion.convert;
import static org.mvel2.MVEL.eval;
import static org.mvel2.ast.TypeDescriptor.getClassReference;
import static org.mvel2.integration.GlobalListenerFactory.*;
import static org.mvel2.integration.PropertyHandlerFactory.*;
import static org.mvel2.util.CompilerTools.expectType;
import static org.mvel2.util.ParseTools.*;
import static org.mvel2.util.PropertyTools.getFieldOrAccessor;
import static org.mvel2.util.PropertyTools.getFieldOrWriteAccessor;
import static org.mvel2.util.ReflectionUtil.toNonPrimitiveType;
import static org.mvel2.util.Varargs.normalizeArgsForVarArgs;
import static org.mvel2.util.Varargs.paramTypeVarArgsSafe;
public class ReflectiveAccessorOptimizer extends AbstractOptimizer implements AccessorOptimizer {
private AccessorNode rootNode;
private AccessorNode currNode;
private Object ctx;
private Object thisRef;
private Object val;
private VariableResolverFactory variableFactory;
private static final int DONE = -1;
private static final Object[] EMPTYARG = new Object[0];
private static final Class[] EMPTYCLS = new Class[0];
private boolean first = true;
private Class ingressType;
private Class returnType;
public ReflectiveAccessorOptimizer() {
}
public void init() {
}
private ReflectiveAccessorOptimizer(ParserContext pCtx, char[] property, int start, int offset, Object ctx,
Object thisRef, VariableResolverFactory variableFactory) {
super(pCtx);
this.expr = property;
this.start = start;
this.length = property != null ? offset : start;
this.end = start + length;
this.ctx = ctx;
this.variableFactory = variableFactory;
this.thisRef = thisRef;
}
public Accessor optimizeAccessor(ParserContext pCtx, char[] property, int start, int offset, Object ctx, Object thisRef,
VariableResolverFactory factory, boolean root, Class ingressType) {
this.rootNode = this.currNode = null;
this.expr = property;
this.start = start;
this.end = start + offset;
this.length = end - start;
this.first = true;
this.ctx = ctx;
this.thisRef = thisRef;
this.variableFactory = factory;
this.ingressType = ingressType;
this.pCtx = pCtx;
return compileGetChain();
}
public Accessor optimizeSetAccessor(ParserContext pCtx, char[] property, int start, int offset, Object ctx,
Object thisRef, VariableResolverFactory factory, boolean rootThisRef,
Object value, Class ingressType) {
this.rootNode = this.currNode = null;
this.expr = property;
this.start = start;
this.first = true;
this.length = start + offset;
this.ctx = ctx;
this.thisRef = thisRef;
this.variableFactory = factory;
this.ingressType = ingressType;
char[] root = null;
int split = findLastUnion();
PropertyVerifier verifier = new PropertyVerifier(property, this.pCtx = pCtx);
if (split != -1) {
root = subset(property, 0, split++);
//todo: must use the property verifier.
property = subset(property, split, property.length - split);
}
if (root != null) {
this.length = end = (this.expr = root).length;
compileGetChain();
ctx = this.val;
}
if (ctx == null) {
throw new PropertyAccessException("could not access property: " + new String(property, this.start, Math.min(length, property.length)) + "; parent is null: "
+ new String(expr), expr, this.start, pCtx);
}
try {
this.length = end = (this.expr = property).length;
int st;
this.cursor = st = 0
;
skipWhitespace();
if (collection) {
st = cursor;
if (cursor == end)
throw new PropertyAccessException("unterminated '['", expr, this.start, pCtx);
if (scanTo(']'))
throw new PropertyAccessException("unterminated '['", expr, this.start, pCtx);
String ex = new String(property, st, cursor - st);
if (ctx instanceof Map) {
if (MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING && hasPropertyHandler(Map.class)) {
propHandlerSet(ex, ctx, Map.class, value);
}
else {
//noinspection unchecked
((Map) ctx).put(eval(ex, ctx, variableFactory), convert(value, returnType = verifier.analyze()));
addAccessorNode(new MapAccessorNest(ex, returnType));
}
return rootNode;
}
else if (ctx instanceof List) {
if (MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING && hasPropertyHandler(List.class)) {
propHandlerSet(ex, ctx, List.class, value);
}
else {
//noinspection unchecked
((List) ctx).set(eval(ex, ctx, variableFactory, Integer.class),
convert(value, returnType = verifier.analyze()));
addAccessorNode(new ListAccessorNest(ex, returnType));
}
return rootNode;
}
else if (MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING && hasPropertyHandler(ctx.getClass())) {
propHandlerSet(ex, ctx, ctx.getClass(), value);
return rootNode;
}
else if (ctx.getClass().isArray()) {
if (MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING && hasPropertyHandler(Array.class)) {
propHandlerSet(ex, ctx, Array.class, value);
}
else {
//noinspection unchecked
Array.set(ctx, eval(ex, ctx, variableFactory, Integer.class),
convert(value, getBaseComponentType(ctx.getClass())));
addAccessorNode(new ArrayAccessorNest(ex));
}
return rootNode;
}
else {
throw new PropertyAccessException("cannot bind to collection property: " + new String(property) +
": not a recognized collection type: " + ctx.getClass(), expr, this.st, pCtx);
}
}
else if (MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING && hasPropertyHandler(ctx.getClass())) {
propHandlerSet(new String(property), ctx, ctx.getClass(), value);
return rootNode;
}
String tk = new String(property, 0, length).trim();
if (hasSetListeners()) {
notifySetListeners(ctx, tk, variableFactory, value);
addAccessorNode(new Notify(tk));
}
Member member = getFieldOrWriteAccessor(ctx.getClass(), tk, value == null ? null : ingressType);
if (member instanceof Field) {
Field fld = (Field) member;
if (value != null && !fld.getType().isAssignableFrom(value.getClass())) {
if (!canConvert(fld.getType(), value.getClass())) {
throw new CompileException("cannot convert type: "
+ value.getClass() + ": to " + fld.getType(), this.expr, this.start);
}
fld.set(ctx, convert(value, fld.getType()));
addAccessorNode(new DynamicFieldAccessor(fld));
}
else if (value == null && fld.getType().isPrimitive()) {
fld.set(ctx, PropertyTools.getPrimitiveInitialValue(fld.getType()));
addAccessorNode(new FieldAccessor(fld));
}
else {
fld.set(ctx, value);
addAccessorNode(new FieldAccessor(fld));
}
}
else if (member != null) {
Method meth = (Method) member;
if (value != null && !meth.getParameterTypes()[0].isAssignableFrom(value.getClass())) {
if (!canConvert(meth.getParameterTypes()[0], value.getClass())) {
throw new CompileException("cannot convert type: "
+ value.getClass() + ": to " + meth.getParameterTypes()[0], this.expr, this.start);
}
meth.invoke(ctx, convert(value, meth.getParameterTypes()[0]));
}
else if (value == null && meth.getParameterTypes()[0].isPrimitive()) {
meth.invoke(ctx, PropertyTools.getPrimitiveInitialValue(meth.getParameterTypes()[0]));
}
else {
meth.invoke(ctx, value);
}
addAccessorNode(new SetterAccessor(meth));
}
else if (ctx instanceof Map) {
//noinspection unchecked
((Map) ctx).put(tk, value);
addAccessorNode(new MapAccessor(tk));
}
else {
throw new PropertyAccessException("could not access property (" + tk + ") in: " + ingressType.getName()
, this.expr, this.start, pCtx);
}
}
catch (InvocationTargetException e) {
throw new PropertyAccessException("could not access property: " + new String(property), this.expr, st, e, pCtx);
}
catch (IllegalAccessException e) {
throw new PropertyAccessException("could not access property: " + new String(property), this.expr, st, e, pCtx);
}
catch (IllegalArgumentException e) {
throw new PropertyAccessException("error binding property: " + new String(property) + " (value <<" + value + ">>::"
+ (value == null ? "null" : value.getClass().getCanonicalName()) + ")", this.expr, st, e, pCtx);
}
return rootNode;
}
private Accessor compileGetChain() {
Object curr = ctx;
cursor = start;
try {
if (!MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING) {
while (cursor < end) {
switch (nextSubToken()) {
case BEAN:
curr = getBeanProperty(curr, capture());
break;
case METH:
curr = getMethod(curr, capture());
break;
case COL:
curr = getCollectionProperty(curr, capture());
break;
case WITH:
curr = getWithProperty(curr);
break;
case DONE:
break;
}
first = false;
if (curr != null) returnType = curr.getClass();
if (cursor < end) {
if (nullSafe) {
int os = expr[cursor] == '.' ? 1 : 0;
addAccessorNode(new NullSafe(expr, cursor + os, end - cursor - os, pCtx));
if (curr == null) break;
}
if (curr == null) throw new NullPointerException();
}
staticAccess = false;
}
}
else {
while (cursor < end) {
switch (nextSubToken()) {
case BEAN:
curr = getBeanPropertyAO(curr, capture());
break;
case METH:
curr = getMethod(curr, capture());
break;
case COL:
curr = getCollectionPropertyAO(curr, capture());
break;
case WITH:
curr = getWithProperty(curr);
break;
case DONE:
break;
}
first = false;
if (curr != null) returnType = curr.getClass();
if (cursor < end) {
if (nullSafe) {
int os = expr[cursor] == '.' ? 1 : 0;
addAccessorNode(new NullSafe(expr, cursor + os, length - cursor - os, pCtx));
if (curr == null) break;
}
if (curr == null) throw new NullPointerException();
}
staticAccess = false;
}
}
val = curr;
return rootNode;
}
catch (InvocationTargetException e) {
if (MVEL.INVOKED_METHOD_EXCEPTIONS_BUBBLE) {
if (e.getTargetException() instanceof RuntimeException) {
throw (RuntimeException) e.getTargetException();
}
else {
throw new RuntimeException(e);
}
}
throw new PropertyAccessException(new String(expr, start, length) + ": "
+ e.getTargetException().getMessage(), this.expr, this.st, e, pCtx);
}
catch (IllegalAccessException e) {
throw new PropertyAccessException(new String(expr, start, length) + ": "
+ e.getMessage(), this.expr, this.st, e, pCtx);
}
catch (IndexOutOfBoundsException e) {
throw new PropertyAccessException(new String(expr, start, length)
+ ": array index out of bounds.", this.expr, this.st, e, pCtx);
}
catch (CompileException e) {
throw e;
}
catch (NullPointerException e) {
throw new PropertyAccessException("null pointer: " + new String(expr, start, length), this.expr, this.st, e, pCtx);
}
catch (Exception e) {
e.printStackTrace();
throw new CompileException(e.getMessage(), this.expr, st, e);
}
}
private void addAccessorNode(AccessorNode an) {
if (rootNode == null)
rootNode = currNode = an;
else {
currNode = currNode.setNextNode(an);
}
}
private Object getWithProperty(Object ctx) {
currType = null;
String root = start == cursor ? null : new String(expr, start, cursor - 1).trim();
int st = cursor + 1;
cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '{', pCtx);
WithAccessor wa = new WithAccessor(pCtx, root, expr, st, cursor++ - st, ingressType);
addAccessorNode(wa);
return wa.getValue(ctx, thisRef, variableFactory);
}
private Object getBeanPropertyAO(Object ctx, String property)
throws Exception {
if (GlobalListenerFactory.hasGetListeners()) {
notifyGetListeners(ctx, property, variableFactory);
addAccessorNode(new Notify(property));
}
if (ctx != null && hasPropertyHandler(ctx.getClass())) return propHandler(property, ctx, ctx.getClass());
return getBeanProperty(ctx, property);
}
private Object getBeanProperty(Object ctx, String property) throws Exception {
if ((pCtx == null ? currType : pCtx.getVarOrInputTypeOrNull(property)) == Object.class
&& !pCtx.isStrongTyping()) {
currType = null;
}
if (first) {
if ("this".equals(property)) {
addAccessorNode(new ThisValueAccessor());
return this.thisRef;
}
else if (variableFactory != null && variableFactory.isResolveable(property)) {
if (variableFactory.isIndexedFactory() && variableFactory.isTarget(property)) {
int idx;
addAccessorNode(new IndexedVariableAccessor(idx = variableFactory.variableIndexOf(property)));
VariableResolver vr = variableFactory.getIndexedVariableResolver(idx);
if (vr == null) {
variableFactory.setIndexedVariableResolver(idx, variableFactory.getVariableResolver(property));
}
return variableFactory.getIndexedVariableResolver(idx).getValue();
}
else {
addAccessorNode(new VariableAccessor(property));
return variableFactory.getVariableResolver(property).getValue();
}
}
}
boolean classRef = false;
Class> cls;
if (ctx instanceof Class) {
if (MVEL.COMPILER_OPT_SUPPORT_JAVA_STYLE_CLASS_LITERALS
&& "class".equals(property)) {
return ctx;
}
cls = (Class>) ctx;
classRef = true;
}
else if (ctx != null) {
cls = ctx.getClass();
}
else {
cls = currType;
}
if (hasPropertyHandler(cls)) {
PropertyHandlerAccessor acc = new PropertyHandlerAccessor(property, cls, getPropertyHandler(cls));
addAccessorNode(acc);
return acc.getValue(ctx, thisRef, variableFactory);
}
Member member = cls != null ? getFieldOrAccessor(cls, property) : null;
if (member != null && classRef && (member.getModifiers() & Modifier.STATIC) == 0) {
member = null;
}
Object o;
if (member instanceof Method) {
try {
o = ctx != null ? ((Method) member).invoke(ctx, EMPTYARG) : null;
if (hasNullPropertyHandler()) {
addAccessorNode(new GetterAccessorNH((Method) member, getNullPropertyHandler()));
if (o == null) o = getNullPropertyHandler().getProperty(member.getName(), ctx, variableFactory);
}
else {
addAccessorNode(new GetterAccessor((Method) member));
}
}
catch (IllegalAccessException e) {
Method iFaceMeth = determineActualTargetMethod((Method) member);
if (iFaceMeth == null)
throw new PropertyAccessException("could not access field: "
+ cls.getName() + "." + property, this.expr, this.start, pCtx);
o = iFaceMeth.invoke(ctx, EMPTYARG);
if (hasNullPropertyHandler()) {
addAccessorNode(new GetterAccessorNH((Method) member, getNullMethodHandler()));
if (o == null) o = getNullMethodHandler().getProperty(member.getName(), ctx, variableFactory);
}
else {
addAccessorNode(new GetterAccessor(iFaceMeth));
}
}
catch (IllegalArgumentException e) {
if (member.getDeclaringClass().equals(ctx)) {
try {
Class c = Class.forName(member.getDeclaringClass().getName() + "$" + property);
throw new CompileException("name collision between innerclass: " + c.getCanonicalName()
+ "; and bean accessor: " + property + " (" + member.toString() + ")", expr, tkStart);
}
catch (ClassNotFoundException e2) {
//fallthru
}
}
throw e;
}
currType = toNonPrimitiveType(((Method) member).getReturnType());
return o;
}
else if (member != null) {
Field f = (Field) member;
if ((f.getModifiers() & Modifier.STATIC) != 0) {
o = f.get(null);
if (hasNullPropertyHandler()) {
addAccessorNode(new StaticVarAccessorNH((Field) member, getNullMethodHandler()));
if (o == null) o = getNullMethodHandler().getProperty(member.getName(), ctx, variableFactory);
}
else {
addAccessorNode(new StaticVarAccessor((Field) member));
}
}
else {
o = ctx != null ? f.get(ctx) : null;
if (hasNullPropertyHandler()) {
addAccessorNode(new FieldAccessorNH((Field) member, getNullMethodHandler()));
if (o == null) o = getNullMethodHandler().getProperty(member.getName(), ctx, variableFactory);
}
else {
addAccessorNode(new FieldAccessor((Field) member));
}
}
currType = toNonPrimitiveType(f.getType());
return o;
}
else if (ctx instanceof Map && (((Map) ctx).containsKey(property) || nullSafe)) {
addAccessorNode(new MapAccessor(property));
return ((Map) ctx).get(property);
}
else if (ctx != null && "length".equals(property) && ctx.getClass().isArray()) {
addAccessorNode(new ArrayLength());
return getLength(ctx);
}
else if (LITERALS.containsKey(property)) {
addAccessorNode(new StaticReferenceAccessor(ctx = LITERALS.get(property)));
return ctx;
}
else {
Object tryStaticMethodRef = tryStaticAccess();
staticAccess = true;
if (tryStaticMethodRef != null) {
if (tryStaticMethodRef instanceof Class) {
addAccessorNode(new StaticReferenceAccessor(tryStaticMethodRef));
return tryStaticMethodRef;
}
else if (tryStaticMethodRef instanceof Field) {
addAccessorNode(new StaticVarAccessor((Field) tryStaticMethodRef));
return ((Field) tryStaticMethodRef).get(null);
}
else {
addAccessorNode(new StaticReferenceAccessor(tryStaticMethodRef));
return tryStaticMethodRef;
}
}
else if (ctx instanceof Class) {
Class c = (Class) ctx;
for (Method m : c.getMethods()) {
if (property.equals(m.getName())) {
if (pCtx!=null&& pCtx.getParserConfiguration()!=null?pCtx.getParserConfiguration().isAllowNakedMethCall():MVEL.COMPILER_OPT_ALLOW_NAKED_METH_CALL) {
o = m.invoke(null, EMPTY_OBJ_ARR);
if (hasNullMethodHandler()) {
addAccessorNode(new MethodAccessorNH(m, new ExecutableStatement[0], getNullMethodHandler()));
if (o == null)
o = getNullMethodHandler().getProperty(m.getName(), ctx, variableFactory);
}
else {
addAccessorNode(new MethodAccessor(m, new ExecutableStatement[0]));
}
return o;
}
else {
addAccessorNode(new StaticReferenceAccessor(m));
return m;
}
}
}
try {
Class subClass = findClass(variableFactory, c.getName() + "$" + property, pCtx);
addAccessorNode(new StaticReferenceAccessor(subClass));
return subClass;
}
catch (ClassNotFoundException cnfe) {
// fall through.
}
}
else if (pCtx!=null&& pCtx.getParserConfiguration()!=null?pCtx.getParserConfiguration().isAllowNakedMethCall():MVEL.COMPILER_OPT_ALLOW_NAKED_METH_CALL) {
return getMethod(ctx, property);
}
// if it is not already using this as context try to read the property value from this
if (ctx != this.thisRef && this.thisRef != null) {
addAccessorNode(new ThisValueAccessor());
return getBeanProperty(this.thisRef, property);
}
if (ctx == null) {
throw new PropertyAccessException("unresolvable property or identifier: " + property, expr, start, pCtx);
}
else {
throw new PropertyAccessException("could not access: " + property + "; in class: "
+ ctx.getClass().getName(), expr, start, pCtx);
}
}
}
/**
* Handle accessing a property embedded in a collections, map, or array
*
* @param ctx -
* @param prop -
* @return -
* @throws Exception -
*/
private Object getCollectionProperty(Object ctx, String prop) throws Exception {
if (prop.length() > 0) {
ctx = getBeanProperty(ctx, prop);
}
currType = null;
if (ctx == null) return null;
int start = ++cursor;
skipWhitespace();
if (cursor == end)
throw new CompileException("unterminated '['", this.expr, this.start);
String item;
if (scanTo(']'))
throw new CompileException("unterminated '['", this.expr, this.start);
item = new String(expr, start, cursor - start);
boolean itemSubExpr = true;
Object idx = null;
try {
idx = parseInt(item);
itemSubExpr = false;
}
catch (Exception e) {
// not a number;
}
ExecutableStatement itemStmt = null;
if (itemSubExpr) {
try {
idx = (itemStmt = (ExecutableStatement) subCompileExpression(item.toCharArray(), pCtx))
.getValue(ctx, thisRef, variableFactory);
}
catch (CompileException e) {
e.setExpr(this.expr);
e.setCursor(start);
throw e;
}
}
++cursor;
if (ctx instanceof Map) {
if (itemSubExpr) {
addAccessorNode(new MapAccessorNest(itemStmt, null));
}
else {
addAccessorNode(new MapAccessor(parseInt(item)));
}
return ((Map) ctx).get(idx);
}
else if (ctx instanceof List) {
if (itemSubExpr) {
addAccessorNode(new ListAccessorNest(itemStmt, null));
}
else {
addAccessorNode(new ListAccessor(parseInt(item)));
}
return ((List) ctx).get((Integer) idx);
}
else if (ctx.getClass().isArray()) {
if (itemSubExpr) {
addAccessorNode(new ArrayAccessorNest(itemStmt));
}
else {
addAccessorNode(new ArrayAccessor(parseInt(item)));
}
return Array.get(ctx, (Integer) idx);
}
else if (ctx instanceof CharSequence) {
if (itemSubExpr) {
addAccessorNode(new IndexedCharSeqAccessorNest(itemStmt));
}
else {
addAccessorNode(new IndexedCharSeqAccessor(parseInt(item)));
}
return ((CharSequence) ctx).charAt((Integer) idx);
}
else if (ctx instanceof Class) {
TypeDescriptor tDescr = new TypeDescriptor(expr, this.start, length, 0);
if (tDescr.isArray()) {
Class cls = getClassReference((Class) ctx, tDescr, variableFactory, pCtx);
rootNode = new StaticReferenceAccessor(cls);
return cls;
}
}
throw new CompileException("illegal use of []: unknown type: "
+ ctx.getClass().getName(), this.expr, this.start);
}
private Object getCollectionPropertyAO(Object ctx, String prop) throws Exception {
if (prop.length() > 0) {
ctx = getBeanPropertyAO(ctx, prop);
}
currType = null;
if (ctx == null) return null;
int _start = ++cursor;
skipWhitespace();
if (cursor == end)
throw new CompileException("unterminated '['", this.expr, this.start);
String item;
if (scanTo(']'))
throw new CompileException("unterminated '['", this.expr, this.start);
item = new String(expr, _start, cursor - _start);
boolean itemSubExpr = true;
Object idx = null;
try {
idx = parseInt(item);
itemSubExpr = false;
}
catch (Exception e) {
// not a number;
}
ExecutableStatement itemStmt = null;
if (itemSubExpr) {
idx = (itemStmt = (ExecutableStatement) subCompileExpression(item.toCharArray(), pCtx))
.getValue(ctx, thisRef, variableFactory);
}
++cursor;
if (ctx instanceof Map) {
if (hasPropertyHandler(Map.class)) {
return propHandler(item, ctx, Map.class);
}
else {
if (itemSubExpr) {
addAccessorNode(new MapAccessorNest(itemStmt, null));
}
else {
addAccessorNode(new MapAccessor(parseInt(item)));
}
return ((Map) ctx).get(idx);
}
}
else if (ctx instanceof List) {
if (hasPropertyHandler(List.class)) {
return propHandler(item, ctx, List.class);
}
else {
if (itemSubExpr) {
addAccessorNode(new ListAccessorNest(itemStmt, null));
}
else {
addAccessorNode(new ListAccessor(parseInt(item)));
}
return ((List) ctx).get((Integer) idx);
}
}
else if (ctx.getClass().isArray()) {
if (hasPropertyHandler(Array.class)) {
return propHandler(item, ctx, Array.class);
}
else {
if (itemSubExpr) {
addAccessorNode(new ArrayAccessorNest(itemStmt));
}
else {
addAccessorNode(new ArrayAccessor(parseInt(item)));
}
return Array.get(ctx, (Integer) idx);
}
}
else if (ctx instanceof CharSequence) {
if (hasPropertyHandler(CharSequence.class)) {
return propHandler(item, ctx, CharSequence.class);
}
else {
if (itemSubExpr) {
addAccessorNode(new IndexedCharSeqAccessorNest(itemStmt));
}
else {
addAccessorNode(new IndexedCharSeqAccessor(parseInt(item)));
}
return ((CharSequence) ctx).charAt((Integer) idx);
}
}
else {
TypeDescriptor tDescr = new TypeDescriptor(expr, this.start, end - this.start, 0);
if (tDescr.isArray()) {
Class cls = getClassReference((Class) ctx, tDescr, variableFactory, pCtx);
rootNode = new StaticReferenceAccessor(cls);
return cls;
}
throw new CompileException("illegal use of []: unknown type: "
+ ctx.getClass().getName(), this.expr, this.st);
}
}
/**
* Find an appropriate method, execute it, and return it's response.
*
* @param ctx -
* @param name -
* @return -
* @throws Exception -
*/
@SuppressWarnings({"unchecked"})
private Object getMethod(Object ctx, String name) throws Exception {
int st = cursor;
String tk = cursor != end
&& expr[cursor] == '(' && ((cursor = balancedCapture(expr, cursor, '(')) - st) > 1 ?
new String(expr, st + 1, cursor - st - 1) : "";
cursor++;
Object[] args;
Class[] argTypes;
ExecutableStatement[] es;
if (tk.length() == 0) {
args = ParseTools.EMPTY_OBJ_ARR;
argTypes = ParseTools.EMPTY_CLS_ARR;
es = null;
}
else {
List subtokens = parseParameterList(tk.toCharArray(), 0, -1);
es = new ExecutableStatement[subtokens.size()];
args = new Object[subtokens.size()];
argTypes = new Class[subtokens.size()];
for (int i = 0; i < subtokens.size(); i++) {
try {
args[i] = (es[i] = (ExecutableStatement) subCompileExpression(subtokens.get(i), pCtx))
.getValue(this.thisRef, thisRef, variableFactory);
}
catch (CompileException e) {
throw ErrorUtil.rewriteIfNeeded(e, this.expr, this.start);
}
if (es[i].isExplicitCast()) argTypes[i] = es[i].getKnownEgressType();
}
if (pCtx.isStrictTypeEnforcement()) {
for (int i = 0; i < args.length; i++) {
argTypes[i] = es[i].getKnownEgressType();
if (es[i] instanceof ExecutableLiteral && ((ExecutableLiteral)es[i]).getLiteral() == null) {
argTypes[i] = NullType.class;
}
}
}
else {
for (int i = 0; i < args.length; i++) {
if (argTypes[i] != null) continue;
if (es[i].getKnownEgressType() == Object.class) {
argTypes[i] = args[i] == null ? null : args[i].getClass();
}
else {
argTypes[i] = es[i].getKnownEgressType();
}
}
}
}
return getMethod(ctx, name, args, argTypes, es);
}
@SuppressWarnings({"unchecked"})
private Object getMethod(Object ctx, String name, Object[] args, Class[] argTypes, ExecutableStatement[] es) throws Exception {
if (first && variableFactory != null && variableFactory.isResolveable(name)) {
Object ptr = variableFactory.getVariableResolver(name).getValue();
if (ptr instanceof Method) {
ctx = ((Method) ptr).getDeclaringClass();
name = ((Method) ptr).getName();
}
else if (ptr instanceof MethodStub) {
ctx = ((MethodStub) ptr).getClassReference();
name = ((MethodStub) ptr).getMethodName();
}
else if (ptr instanceof FunctionInstance) {
FunctionInstance func = (FunctionInstance) ptr;
if (!name.equals(func.getFunction().getName())) {
getBeanProperty(ctx, name);
addAccessorNode(new DynamicFunctionAccessor(es));
}
else {
addAccessorNode(new FunctionAccessor(func, es));
}
return func.call(ctx, thisRef, variableFactory, args);
}
else {
throw new OptimizationFailure("attempt to optimize a method call for a reference that does not point to a method: "
+ name + " (reference is type: " + (ctx != null ? ctx.getClass().getName() : null) + ")");
}
first = false;
}
if (ctx == null && currType == null) {
throw new PropertyAccessException("null pointer or function not found: " + name, this.expr, this.start, pCtx);
}
boolean classTarget = false;
Class> cls = currType != null ? currType : ((classTarget = ctx instanceof Class) ? (Class>) ctx : ctx.getClass());
currType = null;
Method m;
Class[] parameterTypes = null;
/**
* If we have not cached the method then we need to go ahead and try to resolve it.
*/
/**
* Try to find an instance method from the class target.
*/
if ((m = getBestCandidate(argTypes, name, cls, cls.getMethods(), false, classTarget)) != null) {
parameterTypes = m.getParameterTypes();
}
if (m == null && classTarget) {
/**
* If we didn't find anything, maybe we're looking for the actual java.lang.Class methods.
*/
if ((m = getBestCandidate(argTypes, name, cls, Class.class.getMethods(), false)) != null) {
parameterTypes = m.getParameterTypes();
}
}
// If we didn't find anything and the declared class is different from the actual one try also with the actual one
if (m == null && ctx != null && cls != ctx.getClass() && !(ctx instanceof Class)) {
cls = ctx.getClass();
if ((m = getBestCandidate(argTypes, name, cls, cls.getMethods(), false, classTarget)) != null) {
parameterTypes = m.getParameterTypes();
}
}
if (m == null) {
StringAppender errorBuild = new StringAppender();
if ("size".equals(name) && args.length == 0 && cls.isArray()) {
addAccessorNode(new ArrayLength());
return getLength(ctx);
}
// if it is not already using this as context try to access the method this
if (ctx != this.thisRef && this.thisRef != null) {
addAccessorNode(new ThisValueAccessor());
return getMethod(this.thisRef, name, args, argTypes, es);
}
for (int i = 0; i < args.length; i++) {
errorBuild.append(args[i] != null ? args[i].getClass().getName() : null);
if (i < args.length - 1) errorBuild.append(", ");
}
throw new PropertyAccessException("unable to resolve method: " + cls.getName() + "."
+ name + "(" + errorBuild.toString() + ") [arglength=" + args.length + "]", this.expr, this.st, pCtx);
}
if (es != null) {
ExecutableStatement cExpr;
for (int i = 0; i < es.length; i++) {
cExpr = es[i];
if (cExpr.getKnownIngressType() == null) {
cExpr.setKnownIngressType(paramTypeVarArgsSafe(parameterTypes, i, m.isVarArgs()));
cExpr.computeTypeConversionRule();
}
if (!cExpr.isConvertableIngressEgress()) {
args[i] = convert(args[i], paramTypeVarArgsSafe(parameterTypes, i, m.isVarArgs()));
}
}
}
else {
/**
* Coerce any types if required.
*/
for (int i = 0; i < args.length; i++)
args[i] = convert(args[i], paramTypeVarArgsSafe(parameterTypes, i, m.isVarArgs()));
}
Method method = getWidenedTarget(cls, m);
Object o = ctx != null ? method.invoke(ctx, normalizeArgsForVarArgs(parameterTypes, args, m.isVarArgs())) : null;
if (hasNullMethodHandler()) {
addAccessorNode(new MethodAccessorNH(method, (ExecutableStatement[]) es, getNullMethodHandler()));
if (o == null) o = getNullMethodHandler().getProperty(m.getName(), ctx, variableFactory);
}
else {
addAccessorNode(new MethodAccessor(method, (ExecutableStatement[]) es));
}
/**
* return the response.
*/
currType = toNonPrimitiveType(method.getReturnType());
return o;
}
public Object getValue(Object ctx, Object elCtx, VariableResolverFactory variableFactory) throws Exception {
return rootNode.getValue(ctx, elCtx, variableFactory);
}
private Accessor _getAccessor(Object o, Class type) {
if (o instanceof List) {
Accessor[] a = new Accessor[((List) o).size()];
int i = 0;
for (Object item : (List) o) {
a[i++] = _getAccessor(item, type);
}
returnType = List.class;
return new ListCreator(a);
}
else if (o instanceof Map) {
Accessor[] k = new Accessor[((Map) o).size()];
Accessor[] v = new Accessor[k.length];
int i = 0;
for (Object item : ((Map) o).keySet()) {
k[i] = _getAccessor(item, type); // key
v[i++] = _getAccessor(((Map) o).get(item), type); // value
}
returnType = Map.class;
return new MapCreator(k, v);
}
else if (o instanceof Object[]) {
Accessor[] a = new Accessor[((Object[]) o).length];
int i = 0;
int dim = 0;
if (type != null) {
String nm = type.getName();
while (nm.charAt(dim) == '[') dim++;
}
else {
type = Object[].class;
dim = 1;
}
try {
Class base = getBaseComponentType(type);
Class cls = dim > 1 ? findClass(null, repeatChar('[', dim - 1) + "L" + base.getName() + ";", pCtx)
: type;
for (Object item : (Object[]) o) {
expectType(pCtx, a[i++] = _getAccessor(item, cls), base, true);
}
return new ArrayCreator(a, getSubComponentType(type));
}
catch (ClassNotFoundException e) {
throw new RuntimeException("this error should never throw:" + getBaseComponentType(type).getName(), e);
}
}
else {
if (returnType == null) returnType = Object.class;
if (type.isArray()) {
return new ExprValueAccessor((String) o, type, ctx, variableFactory, pCtx);
}
else {
return new ExprValueAccessor((String) o, Object.class, ctx, variableFactory, pCtx);
}
}
}
public Accessor optimizeCollection(ParserContext pCtx, Object o, Class type, char[] property, int start, int offset,
Object ctx, Object thisRef, VariableResolverFactory factory) {
this.start = this.cursor = start;
this.length = start + offset;
this.returnType = type;
this.ctx = ctx;
this.variableFactory = factory;
this.pCtx = pCtx;
Accessor root = _getAccessor(o, returnType);
if (property != null && length > start) {
return new Union(pCtx, root, property, cursor, offset);
}
else {
return root;
}
}
public Accessor optimizeObjectCreation(ParserContext pCtx, char[] property, int start, int offset,
Object ctx, Object thisRef, VariableResolverFactory factory) {
this.length = start + offset;
this.cursor = this.start = start;
this.pCtx = pCtx;
try {
return compileConstructor(property, ctx, factory);
}
catch (CompileException e) {
throw ErrorUtil.rewriteIfNeeded(e, property, this.start);
}
catch (ClassNotFoundException e) {
throw new CompileException("could not resolve class: " + e.getMessage(), property, this.start, e);
}
catch (Exception e) {
throw new CompileException("could not create constructor: " + e.getMessage(), property, this.start, e);
}
}
private void setRootNode(AccessorNode rootNode) {
this.rootNode = this.currNode = rootNode;
}
private AccessorNode getRootNode() {
return rootNode;
}
public Object getResultOptPass() {
return val;
}
@SuppressWarnings({"WeakerAccess"})
private AccessorNode compileConstructor(char[] expression, Object ctx, VariableResolverFactory vars) throws
InstantiationException, IllegalAccessException, InvocationTargetException,
ClassNotFoundException, NoSuchMethodException {
String[] cnsRes = captureContructorAndResidual(expression, start, length);
List constructorParms = parseMethodOrConstructor(cnsRes[0].toCharArray());
if (constructorParms != null) {
String s = new String(subset(expression, 0, ArrayTools.findFirst('(', start, length, expression)));
Class cls = ParseTools.findClass(vars, s, pCtx);
ExecutableStatement[] cStmts = new ExecutableStatement[constructorParms.size()];
for (int i = 0; i < constructorParms.size(); i++) {
cStmts[i] = (ExecutableStatement) subCompileExpression(constructorParms.get(i), pCtx);
}
Object[] parms = new Object[constructorParms.size()];
for (int i = 0; i < constructorParms.size(); i++) {
parms[i] = cStmts[i].getValue(ctx, vars);
}
Constructor cns = getBestConstructorCandidate(parms, cls, pCtx.isStrongTyping());
if (cns == null) {
StringBuilder error = new StringBuilder();
for (int i = 0; i < parms.length; i++) {
error.append(parms[i].getClass().getName());
if (i + 1 < parms.length) error.append(", ");
}
throw new CompileException("unable to find constructor: " + cls.getName()
+ "(" + error.toString() + ")", this.expr, this.start);
}
for (int i = 0; i < parms.length; i++) {
//noinspection unchecked
parms[i] = convert(parms[i], paramTypeVarArgsSafe(cns.getParameterTypes(), i, cns.isVarArgs()));
}
parms = normalizeArgsForVarArgs(cns.getParameterTypes(), parms, cns.isVarArgs());
AccessorNode ca = new ConstructorAccessor(cns, cStmts);
if (cnsRes.length > 1) {
ReflectiveAccessorOptimizer compiledOptimizer
= new ReflectiveAccessorOptimizer(pCtx, cnsRes[1].toCharArray(), 0, cnsRes[1].length(),
cns.newInstance(parms), ctx, vars);
compiledOptimizer.ingressType = cns.getDeclaringClass();
compiledOptimizer.setRootNode(ca);
compiledOptimizer.compileGetChain();
ca = compiledOptimizer.getRootNode();
this.val = compiledOptimizer.getResultOptPass();
}
return ca;
}
else {
ClassLoader classLoader = pCtx != null ? pCtx.getClassLoader() : currentThread().getContextClassLoader();
Constructor> cns = Class.forName(new String(expression), true, classLoader ).getConstructor(EMPTYCLS);
AccessorNode ca = new ConstructorAccessor(cns, null);
if (cnsRes.length > 1) {
//noinspection NullArgumentToVariableArgMethod
ReflectiveAccessorOptimizer compiledOptimizer
= new ReflectiveAccessorOptimizer(pCtx, cnsRes[1].toCharArray(), 0, cnsRes[1].length(), cns.newInstance(null), ctx, vars);
compiledOptimizer.setRootNode(ca);
compiledOptimizer.compileGetChain();
ca = compiledOptimizer.getRootNode();
this.val = compiledOptimizer.getResultOptPass();
}
return ca;
}
}
public Class getEgressType() {
return returnType;
}
public boolean isLiteralOnly() {
return false;
}
private Object propHandler(String property, Object ctx, Class handler) {
PropertyHandler ph = getPropertyHandler(handler);
addAccessorNode(new PropertyHandlerAccessor(property, handler, ph));
return ph.getProperty(property, ctx, variableFactory);
}
public void propHandlerSet(String property, Object ctx, Class handler, Object value) {
PropertyHandler ph = getPropertyHandler(handler);
addAccessorNode(new PropertyHandlerAccessor(property, handler, ph));
ph.setProperty(property, ctx, variableFactory, value);
}
}