org.mvel2.ast.NewObjectNode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tbel Show documentation
Show all versions of tbel Show documentation
TBEL is a powerful expression language for ThingsBoard platform user-defined functions.
Original implementation is based on MVEL.
The newest version!
/**
* 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.ast;
import org.mvel2.CompileException;
import org.mvel2.ErrorDetail;
import org.mvel2.ExecutionContext;
import org.mvel2.ParserContext;
import org.mvel2.PropertyAccessor;
import org.mvel2.compiler.Accessor;
import org.mvel2.compiler.ExecutableStatement;
import org.mvel2.compiler.PropertyVerifier;
import org.mvel2.integration.VariableResolverFactory;
import org.mvel2.optimizers.AccessorOptimizer;
import org.mvel2.optimizers.OptimizerFactory;
import org.mvel2.util.ArrayTools;
import org.mvel2.util.ErrorUtil;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import static java.lang.reflect.Array.newInstance;
import static org.mvel2.DataConversion.convert;
import static org.mvel2.MVEL.analyze;
import static org.mvel2.MVEL.eval;
import static org.mvel2.optimizers.OptimizerFactory.getThreadAccessorOptimizer;
import static org.mvel2.util.ArrayTools.findFirst;
import static org.mvel2.util.CompilerTools.getInjectedImports;
import static org.mvel2.util.ParseTools.*;
import static org.mvel2.util.ReflectionUtil.toPrimitiveArrayType;
/**
* @author Christopher Brock
*/
@SuppressWarnings({"ManualArrayCopy"})
public class NewObjectNode extends ASTNode {
private static final Logger LOG = Logger.getLogger(NewObjectNode.class.getName());
private transient Accessor newObjectOptimizer;
private TypeDescriptor typeDescr;
private char[] name;
private static final Class[] EMPTYCLS = new Class[0];
public NewObjectNode(TypeDescriptor typeDescr, int fields, ParserContext pCtx) {
super(pCtx);
this.typeDescr = typeDescr;
this.fields = fields;
this.expr = typeDescr.getExpr();
this.start = typeDescr.getStart();
this.offset = typeDescr.getOffset();
if (offset < expr.length) {
this.name = subArray(expr, start, start + offset);
}
else {
this.name = expr;
}
if ((fields & COMPILE_IMMEDIATE) != 0) {
if (pCtx != null && pCtx.hasImport(typeDescr.getClassName())) {
pCtx.setAllowBootstrapBypass(false);
egressType = pCtx.getImport(typeDescr.getClassName());
}
else {
try {
egressType = Class.forName(typeDescr.getClassName(), true, getClassLoader());
}
catch (ClassNotFoundException e) {
if (pCtx.isStrongTyping())
pCtx.addError(new ErrorDetail(expr, start, true, "could not resolve class: " + typeDescr.getClassName()));
return;
// do nothing.
}
}
if (egressType != null) {
rewriteClassReferenceToFQCN(fields);
if (typeDescr.isArray()) {
try {
egressType = egressType.isPrimitive() ?
toPrimitiveArrayType(egressType) :
findClass(null, repeatChar('[', typeDescr.getArrayLength()) + "L" + egressType.getName() + ";", pCtx);
}
catch (Exception e) {
LOG.log(Level.WARNING,"", e);
// for now, don't handle this.
}
}
}
if (pCtx != null) {
if (egressType == null) {
pCtx.addError(new ErrorDetail(expr, start, true, "could not resolve class: " + typeDescr.getClassName()));
return;
}
if (!typeDescr.isArray()) {
String[] cnsResid = captureContructorAndResidual(expr, start, offset);
final List constructorParms
= parseMethodOrConstructor(cnsResid[0].toCharArray());
final Class[] parms = new Class[constructorParms.size()];
for (int i = 0; i < parms.length; i++) {
parms[i] = analyze(constructorParms.get(i), pCtx);
}
if (getBestConstructorCandidate(parms, egressType, true) == null) {
if (pCtx.isStrongTyping())
pCtx.addError(new ErrorDetail(expr, start, pCtx.isStrongTyping(), "could not resolve constructor " + typeDescr.getClassName()
+ Arrays.toString(parms)));
}
if (cnsResid.length == 2) {
String residualProperty =
cnsResid[1].trim();
if (residualProperty.length() == 0) return;
this.egressType = new PropertyVerifier(residualProperty, pCtx, egressType).analyze();
}
}
}
}
}
private void rewriteClassReferenceToFQCN(int fields) {
String FQCN = egressType.getName();
if (typeDescr.getClassName().indexOf('.') == -1) {
int idx = ArrayTools.findFirst('(', 0, name.length, name);
char[] fqcn = FQCN.toCharArray();
if (idx == -1) {
this.name = new char[idx = fqcn.length];
for (int i = 0; i < idx; i++)
this.name[i] = fqcn[i];
}
else {
char[] newName = new char[fqcn.length + (name.length - idx)];
for (int i = 0; i < fqcn.length; i++)
newName[i] = fqcn[i];
int i0 = name.length - idx;
int i1 = fqcn.length;
for (int i = 0; i < i0; i++)
newName[i + i1] = name[i + idx];
this.name = newName;
}
this.typeDescr.updateClassName(name, 0, name.length, fields);
}
}
public Object getReducedValueAccelerated(Object ctx, Object thisValue, VariableResolverFactory factory) {
if (newObjectOptimizer == null) {
if (egressType == null) {
/**
* This means we couldn't resolve the type at the time this AST node was created, which means
* we have to attempt runtime resolution.
*/
if (factory != null && factory.isResolveable(typeDescr.getClassName())) {
try {
egressType = (Class) factory.getVariableResolver(typeDescr.getClassName()).getValue();
rewriteClassReferenceToFQCN(COMPILE_IMMEDIATE);
if (typeDescr.isArray()) {
try {
egressType = findClass(factory,
repeatChar('[', typeDescr.getArrayLength()) + "L" + egressType.getName() + ";", pCtx);
}
catch (Exception e) {
// for now, don't handle this.
}
}
}
catch (ClassCastException e) {
throw new CompileException("cannot construct object: " + typeDescr.getClassName()
+ " is not a class reference", expr, start, e);
}
}
}
if (typeDescr.isArray()) {
return (newObjectOptimizer = new NewObjectArray(getBaseComponentType(egressType.getComponentType()), typeDescr.getCompiledArraySize()))
.getValue(ctx, thisValue, factory);
}
try {
AccessorOptimizer optimizer = getThreadAccessorOptimizer();
ParserContext pCtx = this.pCtx;
if (pCtx == null) {
pCtx = new ParserContext();
pCtx.getParserConfiguration().setAllImports(getInjectedImports(factory));
}
newObjectOptimizer = optimizer.optimizeObjectCreation(pCtx, name, 0, name.length, ctx, thisValue, factory);
/**
* Check to see if the optimizer actually produced the object during optimization. If so,
* we return that value now.
*/
if (optimizer.getResultOptPass() != null) {
egressType = optimizer.getEgressType();
return optimizer.getResultOptPass();
}
}
catch (CompileException e) {
throw ErrorUtil.rewriteIfNeeded(e, expr, start);
}
finally {
OptimizerFactory.clearThreadAccessorOptimizer();
}
}
return newObjectOptimizer.getValue(ctx, thisValue, factory);
}
public Object getReducedValue(Object ctx, Object thisValue, VariableResolverFactory factory) {
try {
if (typeDescr.isArray()) {
Class cls = findClass(factory, typeDescr.getClassName(), pCtx);
int[] s = new int[typeDescr.getArrayLength()];
ArraySize[] arraySize = typeDescr.getArraySize();
for (int i = 0; i < s.length; i++) {
s[i] = convert(eval(arraySize[i].value, ctx, factory), Integer.class);
}
checkArray(ctx, cls, s);
return newInstance(cls, s);
}
else {
String[] cnsRes = captureContructorAndResidual(name, 0, name.length);
List constructorParms = parseMethodOrConstructor(cnsRes[0].toCharArray());
if (constructorParms != null) {
Class cls = findClass(factory, new String(subset(name, 0, findFirst('(', 0, name.length, name))).trim(), pCtx);
Object[] parms = new Object[constructorParms.size()];
for (int i = 0; i < constructorParms.size(); i++) {
parms[i] = eval(constructorParms.get(i), ctx, factory);
}
Constructor cns = getBestConstructorCandidate(parms, cls, false);
if (cns == null)
throw new CompileException("unable to find constructor for: " + cls.getName(), expr, start);
for (int i = 0; i < parms.length; i++) {
//noinspection unchecked
parms[i] = convert(parms[i], cns.getParameterTypes()[i]);
}
if (cnsRes.length > 1) {
return PropertyAccessor.get(cnsRes[1], cns.newInstance(parms), factory, thisValue, pCtx);
}
else {
return cns.newInstance(parms);
}
}
else {
Constructor> cns = Class.forName(typeDescr.getClassName(), true, pCtx.getParserConfiguration().getClassLoader())
.getConstructor(EMPTYCLS);
if (cnsRes.length > 1) {
return PropertyAccessor.get(cnsRes[1], cns.newInstance(), factory, thisValue, pCtx);
}
else {
return cns.newInstance();
}
}
}
}
catch (CompileException e) {
throw e;
}
catch (ClassNotFoundException e) {
throw new CompileException("unable to resolve class: " + e.getMessage(), expr, start, e);
}
catch (NoSuchMethodException e) {
throw new CompileException("cannot resolve constructor: " + e.getMessage(), expr, start, e);
}
catch (Exception e) {
throw new CompileException("could not instantiate class: " + e.getMessage(), expr, start, e);
}
}
private boolean isPrototypeFunction() {
return pCtx.getFunctions().containsKey(typeDescr.getClassName());
}
private Object createPrototypalObject(Object ctx, Object thisRef, VariableResolverFactory factory) {
final Function function = pCtx.getFunction(typeDescr.getClassName());
return function.getReducedValueAccelerated(ctx, thisRef, factory);
}
public static class NewObjectArray implements Accessor, Serializable {
private ExecutableStatement[] sizes;
private Class arrayType;
public NewObjectArray(Class arrayType, ExecutableStatement[] sizes) {
this.arrayType = arrayType;
this.sizes = sizes;
}
public Object getValue(Object ctx, Object elCtx, VariableResolverFactory variableFactory) {
int[] s = new int[sizes.length];
for (int i = 0; i < s.length; i++) {
s[i] = convert(sizes[i].getValue(ctx, elCtx, variableFactory), Integer.class);
}
if (ctx instanceof ExecutionContext) {
((ExecutionContext)ctx).checkArray(arrayType, s);
}
return newInstance(arrayType, s);
}
public Object setValue(Object ctx, Object elCtx, VariableResolverFactory variableFactory, Object value) {
return null;
}
public Class getKnownEgressType() {
try {
return Class.forName("[L" + arrayType.getName() + ";");
}
catch (ClassNotFoundException cne) {
return null;
}
}
}
public TypeDescriptor getTypeDescr() {
return typeDescr;
}
public Accessor getNewObjectOptimizer() {
return newObjectOptimizer;
}
}