blob: 2bdad86a68d870dd2e2793ee68cdaa427d43348b [file] [log] [blame]
/**
* 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.
*/
package com.datatorrent.lib.util;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.codehaus.commons.compiler.CompileException;
import org.codehaus.commons.compiler.CompilerFactoryFactory;
import org.codehaus.commons.compiler.IScriptEvaluator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.datatorrent.lib.expression.Expression;
import com.datatorrent.lib.expression.JavaExpressionParser;
/**
* @since 2.1.0
*/
public class PojoUtils
{
private static final Logger logger = LoggerFactory.getLogger(PojoUtils.class);
public static final String DEFAULT_EXP_OBJECT_PLACEHOLDER = "{$}";
public static final String DEFAULT_EXP_VAL_PLACEHOLDER = "{#}";
public static final String DEFAULT_EXPRESSION_OBJ_PLACEHOLDER = "$";
private static final String OBJECT = "object";
private static final String VAL = "val";
private static final String GET = "get";
private static final String IS = "is";
private static final String SET = "set";
private static final Map<Class<?>, Class<?>> primitiveClassToGetterInterface = Maps.newHashMap();
private static final Map<Class<?>, Class<?>> primitiveClassToSetterInterface = Maps.newHashMap();
static {
primitiveClassToGetterInterface.put(boolean.class, GetterBoolean.class);
primitiveClassToGetterInterface.put(byte.class, GetterByte.class);
primitiveClassToGetterInterface.put(char.class, GetterChar.class);
primitiveClassToGetterInterface.put(short.class, GetterShort.class);
primitiveClassToGetterInterface.put(int.class, GetterInt.class);
primitiveClassToGetterInterface.put(long.class, GetterLong.class);
primitiveClassToGetterInterface.put(float.class, GetterFloat.class);
primitiveClassToGetterInterface.put(double.class, GetterDouble.class);
primitiveClassToSetterInterface.put(boolean.class, SetterBoolean.class);
primitiveClassToSetterInterface.put(byte.class, SetterByte.class);
primitiveClassToSetterInterface.put(char.class, SetterChar.class);
primitiveClassToSetterInterface.put(short.class, SetterShort.class);
primitiveClassToSetterInterface.put(int.class, SetterInt.class);
primitiveClassToSetterInterface.put(long.class, SetterLong.class);
primitiveClassToSetterInterface.put(float.class, SetterFloat.class);
primitiveClassToSetterInterface.put(double.class, SetterDouble.class);
}
private PojoUtils()
{
}
public interface GetterBoolean<T>
{
boolean get(T obj);
}
public interface GetterByte<T>
{
byte get(T obj);
}
public interface GetterChar<T>
{
char get(T obj);
}
public interface GetterShort<T>
{
short get(T obj);
}
public interface GetterInt<T>
{
int get(T obj);
}
public interface GetterLong<T>
{
long get(T obj);
}
public interface GetterFloat<T>
{
float get(T obj);
}
public interface GetterDouble<T>
{
double get(T obj);
}
public interface Getter<T, V>
{
V get(T obj);
}
public static <T> GetterBoolean<T> createGetterBoolean(Class<? extends T> pojoClass, String getterExpr)
{
return createGetterBoolean(pojoClass, getterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> GetterBoolean<T> createGetterBoolean(Class<? extends T> pojoClass, String getterExpr, String exprObjectPlaceholder)
{
return (GetterBoolean<T>)createGetter(pojoClass, getterExpr, exprObjectPlaceholder, boolean.class,
GetterBoolean.class);
}
public static <T> GetterByte<T> createGetterByte(Class<? extends T> pojoClass, String getterExpr)
{
return createGetterByte(pojoClass, getterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> GetterByte<T> createGetterByte(Class<? extends T> pojoClass, String getterExpr, String exprObjectPlaceholder)
{
return (GetterByte<T>)createGetter(pojoClass, getterExpr, exprObjectPlaceholder, byte.class, GetterByte.class);
}
public static <T> GetterChar<T> createGetterChar(Class<? extends T> pojoClass, String getterExpr)
{
return createGetterChar(pojoClass, getterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER);
}
@SuppressWarnings({ "unchecked"})
public static <T> GetterChar<T> createGetterChar(Class<? extends T> pojoClass, String getterExpr, String exprObjectPlaceholder)
{
return (GetterChar<T>)createGetter(pojoClass, getterExpr, exprObjectPlaceholder, char.class, GetterChar.class);
}
public static <T> GetterShort<T> createGetterShort(Class<? extends T> pojoClass, String getterExpr)
{
return createGetterShort(pojoClass, getterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> GetterShort<T> createGetterShort(Class<? extends T> pojoClass, String getterExpr, String exprObjectPlaceholder)
{
return (GetterShort<T>)createGetter(pojoClass, getterExpr, exprObjectPlaceholder, short.class, GetterShort.class);
}
public static <T> GetterInt<T> createGetterInt(Class<? extends T> pojoClass, String getterExpr)
{
return createGetterInt(pojoClass, getterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> GetterInt<T> createGetterInt(Class<? extends T> pojoClass, String getterExpr, String exprObjectPlaceholder)
{
return (GetterInt<T>)createGetter(pojoClass, getterExpr, exprObjectPlaceholder, int.class, GetterInt.class);
}
public static <T> GetterLong<T> createGetterLong(Class<? extends T> pojoClass, String getterExpr)
{
return createGetterLong(pojoClass, getterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> GetterLong<T> createGetterLong(Class<? extends T> pojoClass, String getterExpr, String exprObjectPlaceholder)
{
return (GetterLong<T>)createGetter(pojoClass, getterExpr, exprObjectPlaceholder, long.class, GetterLong.class);
}
public static <T> GetterFloat<T> createGetterFloat(Class<? extends T> pojoClass, String getterExpr)
{
return createGetterFloat(pojoClass, getterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> GetterFloat<T> createGetterFloat(Class<? extends T> pojoClass, String getterExpr, String exprObjectPlaceholder)
{
return (GetterFloat<T>)createGetter(pojoClass, getterExpr, exprObjectPlaceholder, float.class, GetterFloat.class);
}
public static <T> GetterDouble<T> createGetterDouble(Class<? extends T> pojoClass, String getterExpr)
{
return createGetterDouble(pojoClass, getterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> GetterDouble<T> createGetterDouble(Class<? extends T> pojoClass, String getterExpr, String exprObjectPlaceholder)
{
return (GetterDouble<T>)createGetter(pojoClass, getterExpr, exprObjectPlaceholder, double.class,
GetterDouble.class);
}
public static <T, V> Getter<T, V> createGetter(Class<? extends T> pojoClass, String getterExpr, Class<? extends V> exprClass)
{
return createGetter(pojoClass, getterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER, exprClass);
}
@SuppressWarnings("unchecked")
public static <T, V> Getter<T, V> createGetter(Class<? extends T> pojoClass, String getterExpr, String exprObjectPlaceholder, Class<? extends V> exprClass)
{
if (primitiveClassToGetterInterface.get(exprClass) != null) {
throw new IllegalArgumentException("createGetter does not allow primitive class \"" + exprClass.getName() +
"\" for exprClass argument. Use createGetter" + upperCaseWord(exprClass.getName()) + " or constructGetter().");
}
return (Getter<T, V>)createGetter(pojoClass, getterExpr, exprObjectPlaceholder, exprClass, Getter.class);
}
public static Object constructGetter(Class<?> pojoClass, String getterExpr, Class<?> exprClass)
{
return constructGetter(pojoClass, getterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER, exprClass);
}
public static Object constructGetter(Class<?> pojoClass, String getterExpr, String exprObjectPlaceholder, Class<?> exprClass)
{
Class<?> interfaceToImplement = primitiveClassToGetterInterface.get(exprClass);
if (interfaceToImplement == null) {
interfaceToImplement = Getter.class;
}
return createGetter(pojoClass, getterExpr, exprObjectPlaceholder, exprClass, interfaceToImplement);
}
/**
* Setter interface for <tt>boolean</tt> primitives
* @param <T> class of objects that the setter applies to
*/
public interface SetterBoolean<T>
{
void set(T obj, boolean booleanVal);
}
/**
* Setter interface for <tt>byte</tt> primitives
* @param <T> class of objects that the setter applies to
*/
public interface SetterByte<T>
{
void set(T obj, byte byteVal);
}
/**
* Setter interface for <tt>char</tt> primitives
* @param <T> class of objects that the setter applies to
*/
public interface SetterChar<T>
{
void set(T obj, char charVal);
}
/**
* Setter interface for <tt>short</tt> primitives
* @param <T> class of objects that the setter applies to
*/
public interface SetterShort<T>
{
void set(T obj, short shortVal);
}
/**
* Setter interface for <tt>int</tt> primitives
* @param <T> class of objects that the setter applies to
*/
public interface SetterInt<T>
{
void set(T obj, int intVal);
}
/**
* Setter interface for <tt>long</tt> primitives
* @param <T> class of objects that the setter applies to
*/
public interface SetterLong<T>
{
void set(T obj, long longVal);
}
/**
* Setter interface for <tt>float</tt> primitives
* @param <T> class of objects that the setter applies to
*/
public interface SetterFloat<T>
{
void set(T obj, float floatVal);
}
/**
* Setter interface for <tt>double</tt> primitives
* @param <T> class of objects that the setter applies to
*/
public interface SetterDouble<T>
{
void set(T obj, double doubleVal);
}
/**
* Setter interface for arbitrary object
* @param <T> class of objects that the setter applies to
* @param <V> class of the rhs expression
*/
public interface Setter<T, V>
{
void set(T obj, V value);
}
public static <T> SetterBoolean<T> createSetterBoolean(Class<? extends T> pojoClass, String setterExpr)
{
return createSetterBoolean(pojoClass, setterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER, DEFAULT_EXP_VAL_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> SetterBoolean<T> createSetterBoolean(Class<? extends T> pojoClass, String setterExpr, String exprObjectPlaceholder, String exprValuePlaceholder)
{
return (SetterBoolean<T>)createSetter(pojoClass, setterExpr, exprObjectPlaceholder, exprValuePlaceholder,
boolean.class, SetterBoolean.class);
}
public static <T> SetterByte<T> createSetterByte(Class<? extends T> pojoClass, String setterExpr)
{
return createSetterByte(pojoClass, setterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER, DEFAULT_EXP_VAL_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> SetterByte<T> createSetterByte(Class<? extends T> pojoClass, String setterExpr, String exprObjectPlaceholder, String exprValuePlaceholder)
{
return (SetterByte<T>)createSetter(pojoClass, setterExpr, exprObjectPlaceholder, exprValuePlaceholder, byte.class,
SetterByte.class);
}
public static <T> SetterChar<T> createSetterChar(Class<? extends T> pojoClass, String setterExpr)
{
return createSetterChar(pojoClass, setterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER, DEFAULT_EXP_VAL_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> SetterChar<T> createSetterChar(Class<? extends T> pojoClass, String setterExpr, String exprObjectPlaceholder, String exprValuePlaceholder)
{
return (SetterChar<T>)createSetter(pojoClass, setterExpr, exprObjectPlaceholder, exprValuePlaceholder, char.class,
SetterChar.class);
}
public static <T> SetterShort<T> createSetterShort(Class<? extends T> pojoClass, String setterExpr)
{
return createSetterShort(pojoClass, setterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER, DEFAULT_EXP_VAL_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> SetterShort<T> createSetterShort(Class<? extends T> pojoClass, String setterExpr, String exprObjectPlaceholder, String exprValuePlaceholder)
{
return (SetterShort<T>)createSetter(pojoClass, setterExpr, exprObjectPlaceholder, exprValuePlaceholder, short.class,
SetterShort.class);
}
public static <T> SetterInt<T> createSetterInt(Class<? extends T> pojoClass, String setterExpr)
{
return createSetterInt(pojoClass, setterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER, DEFAULT_EXP_VAL_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> SetterInt<T> createSetterInt(Class<? extends T> pojoClass, String setterExpr, String exprObjectPlaceholder, String exprValuePlaceholder)
{
return (SetterInt<T>)createSetter(pojoClass, setterExpr, exprObjectPlaceholder, exprValuePlaceholder, int.class,
SetterInt.class);
}
public static <T> SetterLong<T> createSetterLong(Class<? extends T> pojoClass, String setterExpr)
{
return createSetterLong(pojoClass, setterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER, DEFAULT_EXP_VAL_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> SetterLong<T> createSetterLong(Class<? extends T> pojoClass, String setterExpr, String exprObjectPlaceholder, String exprValuePlaceholder)
{
return (SetterLong<T>)createSetter(pojoClass, setterExpr, exprObjectPlaceholder, exprValuePlaceholder, long.class,
SetterLong.class);
}
public static <T> SetterFloat<T> createSetterFloat(Class<? extends T> pojoClass, String setterExpr)
{
return createSetterFloat(pojoClass, setterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER, DEFAULT_EXP_VAL_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> SetterFloat<T> createSetterFloat(Class<? extends T> pojoClass, String setterExpr, String exprObjectPlaceholder, String exprValuePlaceholder)
{
return (SetterFloat<T>)createSetter(pojoClass, setterExpr, exprObjectPlaceholder, exprValuePlaceholder, float.class,
SetterFloat.class);
}
public static <T> SetterDouble<T> createSetterDouble(Class<? extends T> pojoClass, String setterExpr)
{
return createSetterDouble(pojoClass, setterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER, DEFAULT_EXP_VAL_PLACEHOLDER);
}
@SuppressWarnings("unchecked")
public static <T> SetterDouble<T> createSetterDouble(Class<? extends T> pojoClass, String setterExpr, String exprObjectPlaceholder, String exprValuePlaceholder)
{
return (SetterDouble<T>)createSetter(pojoClass, setterExpr, exprObjectPlaceholder, exprValuePlaceholder,
double.class, SetterDouble.class);
}
public static <T, V> Setter<T, V> createSetter(Class<? extends T> pojoClass, String setterExpr, Class<? extends V> exprClass)
{
return createSetter(pojoClass, setterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER, DEFAULT_EXP_VAL_PLACEHOLDER, exprClass);
}
@SuppressWarnings("unchecked")
public static <T, V> Setter<T, V> createSetter(Class<? extends T> pojoClass, String setterExpr, String exprObjectPlaceholder, String exprValuePlaceholder, Class<? extends V> exprClass)
{
if (primitiveClassToSetterInterface.get(exprClass) != null) {
throw new IllegalArgumentException("createSetter does not allow primitive class \"" + exprClass.getName() +
"\" for exprClass argument. Use createSetter" + upperCaseWord(exprClass.getName()) + " or constructSetter().");
}
return (Setter<T, V>)createSetter(pojoClass, setterExpr, exprObjectPlaceholder, exprValuePlaceholder, exprClass,
Setter.class);
}
public static Object constructSetter(Class<?> pojoClass, String setterExpr, Class<?> exprClass)
{
return constructSetter(pojoClass, setterExpr, DEFAULT_EXP_OBJECT_PLACEHOLDER, DEFAULT_EXP_VAL_PLACEHOLDER, exprClass);
}
/**
*
* @param pojoClass Class object that the setter applies to
* @param setterExpr expression to use for setter
* @param exprClass Class that setter will accept as parameter
* @return instance of a class that implements requested Setter interface
*/
public static Object constructSetter(Class<?> pojoClass, String setterExpr, String exprObjectPlaceholder, String exprValPlaceholder, Class<?> exprClass)
{
Class<?> interfaceToImplement = primitiveClassToSetterInterface.get(exprClass);
if (interfaceToImplement == null) {
interfaceToImplement = Setter.class;
}
return createSetter(pojoClass, setterExpr, exprObjectPlaceholder, exprValPlaceholder, exprClass, interfaceToImplement);
}
private static class JavaStatement
{
private final StringBuilder javaStatement;
private final int capacity;
private JavaStatement()
{
javaStatement = new StringBuilder();
capacity = javaStatement.capacity();
}
private JavaStatement(int length)
{
javaStatement = new StringBuilder(length);
capacity = javaStatement.capacity();
}
@Override
public String toString()
{
return javaStatement.toString();
}
protected JavaStatement append(String string)
{
javaStatement.append(string);
return this;
}
private JavaStatement appendCastToTypeExpr(Class<?> type, String expr)
{
return append("((").append(type.getName()).append(")(").append(expr).append("))");
}
protected String getStatement()
{
if (capacity < javaStatement.length() + 1) {
logger.debug("Java statement capacity {} was not sufficient for the statement length {}. Actual statement {}", capacity, javaStatement.length(), javaStatement);
}
return javaStatement.append(";").toString();
}
}
private static class JavaReturnStatement extends JavaStatement
{
private JavaReturnStatement(Class<?> returnType)
{
super();
append("return (").append(returnType.getName()).append(")");
}
private JavaReturnStatement(int length, Class<?> returnType)
{
super(length);
append("return ((").append(returnType.getName()).append(")");
}
@Override
protected String getStatement()
{
append(")");
return super.getStatement();
}
}
private static String upperCaseWord(String field)
{
Preconditions.checkArgument(!field.isEmpty(), field);
return field.substring(0, 1).toUpperCase() + field.substring(1);
}
/**
* Return the getter expression for the given field.
* <p>
* If the field is a public member, the field name is used else the getter function. If no matching field or getter
* method is found, the expression is returned unmodified.
*
* @param pojoClass class to check for the field
* @param fieldExpression field name expression
* @param exprClass expected field type
* @return java code fragment
*/
private static String getSingleFieldGetterExpression(final Class<?> pojoClass, final String fieldExpression, final Class<?> exprClass)
{
JavaStatement code = new JavaReturnStatement(pojoClass.getName().length() + fieldExpression.length() + exprClass.getName().length() + 32, exprClass);
code.appendCastToTypeExpr(pojoClass, OBJECT).append(".");
try {
final Field field = pojoClass.getField(fieldExpression);
if (ClassUtils.isAssignable(field.getType(), exprClass)) {
return code.append(field.getName()).getStatement();
}
logger.debug("Field {} can not be assigned to {}. Proceeding to locate a getter method.", field, exprClass);
} catch (NoSuchFieldException ex) {
logger.debug("{} does not have field {}. Proceeding to locate a getter method.", pojoClass, fieldExpression);
} catch (SecurityException ex) {
logger.debug("{} does not have field {}. Proceeding to locate a getter method.", pojoClass, fieldExpression);
}
String methodName = GET + upperCaseWord(fieldExpression);
try {
Method method = pojoClass.getMethod(methodName);
if (ClassUtils.isAssignable(method.getReturnType(), exprClass)) {
return code.append(methodName).append("()").getStatement();
}
logger.debug("method {} of the {} returns {} that can not be assigned to {}. Proceeding to locate another getter method.",
pojoClass, methodName, method.getReturnType(), exprClass);
} catch (NoSuchMethodException | SecurityException ex) {
logger.debug("{} does not have method {}. Proceeding to locate another getter method.",
pojoClass, methodName);
}
methodName = IS + upperCaseWord(fieldExpression);
try {
Method method = pojoClass.getMethod(methodName);
if (ClassUtils.isAssignable(method.getReturnType(), exprClass)) {
return code.append(methodName).append("()").getStatement();
}
logger.debug("method {} of the {} returns {} that can not be assigned to {}. Proceeding with the original expression {}.",
pojoClass, methodName, method.getReturnType(), exprClass, fieldExpression);
} catch (NoSuchMethodException | SecurityException ex) {
logger.debug("{} does not have method {}. Proceeding with the original expression {}.",
pojoClass, methodName, fieldExpression);
}
return code.append(fieldExpression).getStatement();
}
@SuppressWarnings("StringEquality")
private static Object createGetter(Class<?> pojoClass, String getterExpr, String exprObjectPlaceholder, Class<?> exprClass, Class<?> getterClass)
{
logger.debug("{} {} {} {}", pojoClass, getterExpr, exprClass, getterClass);
JavaExpressionParser javaExpressionParser = new JavaExpressionParser();
javaExpressionParser.setInputObjectPlaceholder(PojoUtils.DEFAULT_EXPRESSION_OBJ_PLACEHOLDER, PojoUtils.OBJECT);
String code = javaExpressionParser.convertToCompilableExpression(getterExpr, pojoClass, exprClass);
return compileExpression(code, getterClass, new String[] {PojoUtils.OBJECT});
}
private static String getSingleFieldSetterExpression(final Class<?> pojoClass, final String fieldExpression, final Class<?> exprClass)
{
JavaStatement code = new JavaStatement(pojoClass.getName().length() + fieldExpression.length() + exprClass.getName().length() + 32);
/* Construct ((<pojo class name>)pojo). */
code.appendCastToTypeExpr(pojoClass, OBJECT).append(".");
try {
final Field field = pojoClass.getField(fieldExpression);
if (ClassUtils.isAssignable(exprClass, field.getType())) {
/* there is public field on the class, use direct assignment. */
/* append <field name> = (<field type>)val; */
return code.append(field.getName()).append(" = ").appendCastToTypeExpr(exprClass, VAL).getStatement();
}
logger.debug("{} can not be assigned to {}. Proceeding to locate a setter method.", exprClass, field);
} catch (NoSuchFieldException ex) {
logger.debug("{} does not have field {}. Proceeding to locate a setter method.", pojoClass, fieldExpression);
} catch (SecurityException ex) {
logger.debug("{} does not have field {}. Proceeding to locate a setter method.", pojoClass, fieldExpression);
}
final String setMethodName = SET + upperCaseWord(fieldExpression);
Method bestMatchMethod = null;
List<Method> candidates = new ArrayList<Method>();
for (Method method : pojoClass.getMethods()) {
if (setMethodName.equals(method.getName())) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
if (exprClass == parameterTypes[0]) {
bestMatchMethod = method;
break;
} else if (ClassUtils.isAssignable(exprClass, parameterTypes[0])) {
candidates.add(method);
}
}
}
}
if (bestMatchMethod == null) { // We did not find the exact match, use candidates to find the match
if (candidates.size() == 0) {
logger.debug("{} does not have suitable setter method {}. Returning original expression {}.",
pojoClass, setMethodName, fieldExpression);
/* We did not find any match at all, use original expression */
/* append = (<expr type>)val;*/
return code.append(fieldExpression).append(" = ").appendCastToTypeExpr(exprClass, VAL).getStatement();
} else {
// TODO: see if we can find a better match
bestMatchMethod = candidates.get(0);
}
}
/* We found a method that we may use for setter */
/* append <method name>((<expr class)val); */
return code.append(bestMatchMethod.getName()).append("(").appendCastToTypeExpr(exprClass, VAL).append(")").getStatement();
}
@SuppressWarnings("StringEquality")
private static Object createSetter(Class<?> pojoClass, String setterExpr, String exprObjectPlaceholder, String exprValPlaceholder, Class<?> exprClass, Class<?> setterClass)
{
if (setterExpr.startsWith(".")) {
setterExpr = setterExpr.substring(1);
}
if (setterExpr.isEmpty()) {
throw new IllegalArgumentException("The setter expression: \"" + setterExpr + "\" is invalid.");
}
logger.debug("{} {} {} {}", pojoClass, setterExpr, exprClass, setterClass);
String code = StringUtils.replaceEach(setterExpr, new String[]{exprObjectPlaceholder, exprValPlaceholder},
new String[]{new JavaStatement().appendCastToTypeExpr(pojoClass, OBJECT).toString(), new JavaStatement().appendCastToTypeExpr(exprClass, VAL).toString()});
if (code != setterExpr) {
code = new JavaStatement(code.length() + 1).append(code).getStatement();
logger.debug("Original expression {} is a complex expression. Replacing it with {}.", setterExpr, code);
} else {
code = getSingleFieldSetterExpression(pojoClass, setterExpr, exprClass);
}
return compileExpression(code, setterClass, new String[] {PojoUtils.OBJECT, PojoUtils.VAL});
}
/**
* This method takes in expression, compiles the expression to provide a executable form of expression.
* This method uses {@link com.datatorrent.lib.expression.JavaExpressionParser} as expression parser.
*
* @param inputType Type of input object
* @param expr expression to be compiled.
* @param returnType Return type of the expression.
* @return Object of type {@link Expression} which can be directly executed.
*/
public static Expression createExpression(Class<?> inputType, String expr, Class<?> returnType)
{
return createExpression(inputType, expr, returnType, null);
}
/**
* This method takes in expression, compiles the expression to provide a executable form of expression.
* This methods also takes in list of classes and method which can be imported statically in expression.
* <p/>
* This method uses {@link JavaExpressionParser} as expression parser.
*
* @param inputType Type of input object
* @param expr expression to be compiled.
* @param returnType Return type of the expression.
* @param defaultImports List of classes/method which will be statically imported to expression compilation.
* @return Object of type {@link Expression} which can be directly executed.
*/
public static Expression createExpression(Class<?> inputType, String expr, Class<?> returnType,
String[] defaultImports)
{
JavaExpressionParser javaExpressionParser = new JavaExpressionParser();
javaExpressionParser.setInputObjectPlaceholder("$", PojoUtils.OBJECT);
return createExpression(inputType, expr, returnType, defaultImports, javaExpressionParser);
}
/**
* This method takes in expression, compiles the expression to provide a executable form of expression.
* This methods also takes in list of classes and method which can be imported statically in expression.
* <p/>
* Using this method one can override expression parser implementation.
*
* @param inputType Type of input object
* @param expr expression to be compiled.
* @param returnType Return type of the expression.
* @param defaultImports List of classes/method which will be statically imported to expression compilation.
* @param parser Expression parser that should be used to parse expression.
* @return Object of type {@link Expression} which can be directly executed.
* @see {@link JavaExpressionParser} as a example.
*/
public static Expression createExpression(Class<?> inputType, String expr, Class<?> returnType,
String[] defaultImports, Expression.ExpressionParser parser)
{
String code = parser.convertToCompilableExpression(expr, inputType, returnType);
return (Expression)compileExpression(code, Expression.class, new String[] {PojoUtils.OBJECT}, defaultImports);
}
private static Object compileExpression(String code, Class<?> implClass, String[] params)
{
return compileExpression(code, implClass, params, null);
}
private static Object compileExpression(String code, Class<?> implClass, String[] params, String[] defaultImports)
{
List<String> imports = new LinkedList<>();
if (defaultImports != null && defaultImports.length != 0) {
for (String defaultImport : defaultImports) {
if (defaultImport != null) {
if (!defaultImport.startsWith("static")) {
imports.add("static " + defaultImport);
} else {
imports.add(defaultImport);
}
}
}
}
IScriptEvaluator se;
try {
se = CompilerFactoryFactory.getDefaultCompilerFactory().newScriptEvaluator();
se.setDefaultImports(imports.toArray(new String[imports.size()]));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
try {
logger.debug("code: {}", code);
return se.createFastEvaluator(code, implClass, params);
} catch (CompileException ex) {
throw new RuntimeException(ex);
}
}
}