com.noga.njexl.testing.api.ArgConverter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of njexl.testing Show documentation
Show all versions of njexl.testing Show documentation
The Commons Jexl library is an implementation of the JSTL Expression Language with extensions.
This was customized by nmondal.
The newest version!
/**
* Copyright 2015 Nabarun Mondal
* 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 com.noga.njexl.testing.api;
import com.noga.njexl.lang.internal.logging.Log;
import com.noga.njexl.lang.internal.logging.LogFactory;
import com.noga.njexl.testing.Utils;
import com.noga.njexl.testing.dataprovider.DataSource;
import com.noga.njexl.testing.dataprovider.ProviderFactory;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
*
* Standard Spring/Hibernate/xBatis magic
* of converting string to object - also known as DeSerialization
* This does the conversion of method parameters from relational strings
*
*/
public class ArgConverter {
private static Log logger = LogFactory.getLog( ArgConverter.class );
public static String TLN(Class clazz){
return clazz.getName().toLowerCase();
}
public static final String OBJECT = TLN ( Object.class) ;
public static final String CLASS = TLN ( Class.class) ;
public static final String STRING = TLN(String.class);
public static final String DATE = TLN ( Date.class);
// INT TYPES
public static final String LONG_B = TLN (Long.class);
public static final String LONG = "long";
public static final String INT = "int";
public static final String INTEGER = TLN(Integer.class);
public static final String BIG_INTEGER = TLN(BigInteger.class);
public static final String SHORT_B = TLN(Short.class);
public static final String SHORT = "short";
public static final String CHARACTER = TLN( Character.class) ;
public static final String CHAR = "char";
public static final String BYTE = "byte";
public static final String BYTE_B = TLN(Byte.class);
// The Number types
public static final String DOUBLE = "double";
public static final String DOUBLE_B = TLN(Double.class);
public static final String FLOAT = "float";
public static final String FLOAT_B = TLN(Float.class);
public static final String BIG_DECIMAL = TLN(BigDecimal.class);
// The Logic Type
public static final String BOOLEAN = "boolean";
public static final String BOOLEAN_B = TLN(Boolean.class);
// The list Type
public static final String LIST = "list";
public static final String ARRAY = "[L";
// Other Strings
// This is the signature to pass NULL
public static final String NULL_OBJECT= "#null#";
// This is the signature to pass empty set, hash, list, array or String
public static final String EMPTY_CONTAINER = "#empty#";
public static final String SPLIT_STRING = ":";
public static final String LIST_SEP_STRING = ",";
public static final String HASH_SEP_STRING = "=>";
public static final String CALL_CONSTRUCTOR = "#";
public static final String LIST_REDIRECT = "@";
public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy" ) ;
/**
* Checks if the class is a primitive type
*
* @param c The class type
* @return Is the class 'c' primitive?
*/
public static boolean IsPrimitive(Class> c) {
String name = c.getName();
name = name.toLowerCase();
// Enum is a primitive type
if (__IsPrimitive(name) || c.isEnum()) {
return true;
}
return c.isPrimitive();
}
/**
* Checks if the full name of the class belongs to a primitive type
*
* @param name Fully qualified Name of the class
* @return boolean yes/no answer
*/
private static boolean __IsPrimitive(String name) {
if (name.equals(STRING) || name.equals(OBJECT) || name.equals(DATE)
/* Now the Un-Boxed types */
|| name.equals(INT) || name.equals(LONG) || name.equals(BYTE)
|| name.equals(SHORT) || name.equals(CHAR)
|| name.equals(FLOAT) || name.equals(DOUBLE)
|| name.equals(BOOLEAN)
/* The Boxed Types */
|| name.equals(INTEGER) || name.equals(BIG_DECIMAL)
|| name.equals(LONG_B) || name.equals(CHARACTER)
|| name.equals(DOUBLE_B)
|| name.equals(FLOAT_B) || name.equals(BYTE_B)
|| name.equals(SHORT_B) || name.equals(BIG_INTEGER)
|| name.equals(BOOLEAN_B) || name.equals(CLASS)) {
return true;
}
return false;
}
/**
*
* Checks if the class type is a list type or not. Basically tells you if it
* is a List or an array type
*
*
* @param c The class type
* @return A boolean yes/no
*/
public static boolean IsList(Class> c) {
return List.class.isAssignableFrom( c ) ;
}
/**
*
* Checks if the class type is an Array type or not. Basically tells you if it
* is an array type
*
*
* @param c The class type
* @return A boolean yes/no
*/
public static boolean IsArray(Class> c) {
if (c.isArray()) {
return true;
}
return false;
}
/**
*
* Is the class c belongs to :-
* [1] Map
* [2] Set
* [3] Dictionary
*
*
*
* @param c a class to be tested
* @return then return true, else return false
*/
public static boolean IsHash(Class> c) {
if (Map.class.isAssignableFrom(c)) {
return true;
}
if (Set.class.isAssignableFrom(c)) {
return true;
}
if (Dictionary.class.isAssignableFrom(c)) {
return true;
}
return false;
}
/**
* A field map for fields of classes
*/
public static ConcurrentHashMap> cachedFieldMap = new ConcurrentHashMap<>();
/**
* Gets the field given class and field name
* @param clazz the class
* @param fieldName the field
* @return the reflective field, if exists
*/
public static Field getField(Class clazz, String fieldName) {
HashMap map = cachedFieldMap.get(clazz);
if ( map == null ){
map = new HashMap<>();
Field[] fields = clazz.getDeclaredFields();
for( Field f : fields ){
f.setAccessible(true);
map.put(f.getName(),f);
}
cachedFieldMap.put(clazz,map);
}
Field f = map.get(fieldName);
if ( f != null){ return f ; }
// get the super too
f = getField( clazz.getSuperclass(),fieldName);
return f ;
}
Method method;
public Method method(){ return method ; }
Type[] parameterTypes;
Class[] parameterClasses;
protected String formattedMethodSignature = "" ;
/**
* Generates method signature given a method
* @param method the method
* @return the compilable method signature for that method
*/
public static String getMethodSignature(Method method){
String[] cols = method.getGenericReturnType().toString().split(" ");
String retType = cols[cols.length - 1];
String methodName = method.getName();
Type[] typed_params = method.getGenericParameterTypes();
StringBuffer output = new StringBuffer( String.format("%s#%s(", retType,
methodName) );
for (int i = 0; i < typed_params.length; i++) {
cols = typed_params[i].toString().split(" ");
output.append( String.format("%s", cols[cols.length - 1]) );
output.append( "," );
}
String ret = output.substring( 0, output.length() - 1);
ret += ")" ;
logger.info(ret);
System.out.println(ret);
return ret;
}
/**
* Prints the method formatted
*/
private void printMethod() {
String output = getMethodSignature( this.method );
formattedMethodSignature = output ;
logger.debug("Matched Method is :: " + output);
System.out.println("=========matched method==========");
System.out.println(output);
System.out.println("=================================");
}
DataSource dataSource ;
String dataTableName;
/**
* Gets the associated data source
* @return the data source
*/
public DataSource dataSource(){ return dataSource ; }
/**
* Create a call container from the given data source
* @param row the row of the data table
* @return a call container
*/
public CallContainer container(int row){
String[] values = dataSource.tables.get(dataTableName).row(row);
if ( parameterTypes.length > values.length ){
return null;
}
CallContainer container = new CallContainer();
container.parameters = new Object[ parameterTypes.length ] ;
String[] headers = dataSource.tables.get(dataTableName).row(0);
for ( int i = 0 ; i < container.parameters.length; i++ ){
try {
container.parameters[i] = object(values[i], parameterClasses[i], parameterTypes[i], headers[i]);
}catch (Exception e){
logger.error(String.format("Error converting parameter %d : %s", i, parameterTypes[i]), e);
}
}
container.method = method ;
container.rowId = row ;
container.dataTable = dataSource.tables.get(dataTableName) ;
return container;
}
/**
* Gets all the containers from the data table
* @param removeDisable if true, disabled items won't be added
* @return an array of @{CallContainer} objects
*/
public CallContainer[] allContainers(boolean removeDisable){
int size = dataSource.tables.get(dataTableName).length();
ArrayList containers = new ArrayList<>();
for ( int i = 1; i < size; i++ ){
CallContainer cc = container(i);
if ( removeDisable && cc.disabled() ){ continue; }
containers.add(cc);
}
CallContainer[] ccArr = new CallContainer[containers.size()];
containers.toArray( ccArr );
return ccArr ;
}
/**
* Gets all the containers from the data table
* removing the disabled items
* @return an array of @{CallContainer} objects
*/
public CallContainer[] allContainers(){
return allContainers(true);
}
/**
* Creates an object from serialized relational form
* @param value the string value
* @param clazz the object's class
* @param type the object's type
* @param header header ( metadata ) information
* @return instance of an object if it could
* @throws Exception if it can not create one
*/
public Object object(String value, Class clazz, Type type, String header) throws Exception {
if ( value.equalsIgnoreCase(NULL_OBJECT) ){
return null;
}
Object object ;
if ( IsPrimitive(clazz) ){
object = primitive(value, clazz, header);
}
else if ( IsList(clazz) ){
if ( value.equalsIgnoreCase( EMPTY_CONTAINER ) ){
return new ArrayList<>();
}
object = list(value, type, header);
}
else if ( IsHash(clazz)){
if ( value.equalsIgnoreCase( EMPTY_CONTAINER ) ){
if ( Dictionary.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz)) {
return new HashMap<>();
}
return new HashSet<>();
}
object = map(value, clazz, type, header);
}
else {
object = complex(value, clazz);
}
return object ;
}
private Object primitive(String value, Class clazz, String header) throws Exception {
if ( clazz.equals( String.class ) ){
if ( value.equalsIgnoreCase( EMPTY_CONTAINER ) ){
return "";
}
return value ;
}
if ( clazz.equals( Date.class) ){
String[] arr = header.split(SPLIT_STRING);
if ( arr.length > 1 ) {
SimpleDateFormat sdf = new SimpleDateFormat(arr[1].trim()) ;
return sdf.parse( value );
}
return DATE_FORMAT.parse(value);
}
if ( clazz.isEnum() ){
return Enum.valueOf( clazz, value );
}
if ( Boolean.class.isAssignableFrom(clazz) || boolean.class.isAssignableFrom(clazz)){
return Boolean.parseBoolean(value);
}
if ( Byte.class.isAssignableFrom(clazz) || byte.class.isAssignableFrom(clazz) ){
return Byte.parseByte(value);
}
if ( Short.class.isAssignableFrom(clazz) || short.class.isAssignableFrom(clazz)){
return Short.parseShort(value);
}
if ( Integer.class.isAssignableFrom(clazz) || int.class.isAssignableFrom(clazz)){
return Integer.parseInt(value);
}
if ( Float.class.isAssignableFrom(clazz) || float.class.isAssignableFrom(clazz)){
return Float.parseFloat(value);
}
if ( Double.class.isAssignableFrom(clazz) || double.class.isAssignableFrom(clazz)){
return Double.parseDouble(value);
}
if ( BigInteger.class.isAssignableFrom(clazz) ){
return new BigInteger(value,10);
}
if ( BigDecimal.class.isAssignableFrom(clazz) ){
return new BigDecimal(value);
}
logger.error(String.format("Why %s is in here?",clazz));
return null;
}
/**
* Gets constructor of a class from metadata info
* @param clazz the class
* @param headers the metadata information
* @return a matching constructor object
*/
public Constructor constructor(Class clazz, String[] headers){
Constructor[] constructors = clazz.getConstructors();
Constructor c = null;
for ( int i = 0 ; i < constructors.length; i++){
if ( constructors[i].getParameterCount() == headers.length ){
// possible return, but try checking the
c = constructors[i];
boolean match = true;
Parameter[] params = c.getParameters();
for ( int j = 0 ; j < params.length; j++){
match = params[j].getName().equals(headers[j]);
if ( !match ){
break;
}
}
if ( match ){
return c;
}
}
}
return c;
}
private Object complex(String value, Class clazz) {
String[] arr = value.split(SPLIT_STRING);
Object instance = null;
String tableName = arr[0].trim();
int rowNum = Integer.parseInt(arr[1].trim());
boolean constructor = false ;
if ( tableName.startsWith(CALL_CONSTRUCTOR) ){
tableName = tableName.substring(1);
//call constructor
constructor = true ;
}
if ( constructor ){
String[] headers = dataSource.tables.get(tableName).row(0);
String[] values = dataSource.tables.get(tableName).row(rowNum);
if ( values == null ){
logger.error("There is no value in the table with that row 0 based!");
return null;
}
Object[] params = new Object[values.length] ;
boolean findConstructor = false ;
for ( int i = 0 ; i < headers.length ;i++ ){
try {
arr = headers[i].split(SPLIT_STRING) ;
if ( arr.length > 1 ) {
String paramClassName = arr[1].trim();
Class paramType = Class.forName(paramClassName);
params[i] = object(values[i], paramType, paramType, headers[i]);
}else{
findConstructor = true;
break;
}
}catch (Exception e){
logger.error(
String.format("Error setting up constructor parameter value : %s : %s", clazz, headers[i]),
e);
}
}
// am here now
if ( findConstructor ){
Constructor c = constructor(clazz, headers);
if ( c == null){
logger.error( String.format("Error creating object through constructor by intelligence: %s", clazz));
return null;
}
Class[] parameters = c.getParameterTypes();
Type[] types = c.getGenericParameterTypes();
for ( int i = 0 ; i < parameters.length ;i++ ){
try {
params[i] = object(values[i], parameters[i], types[i], headers[i]);
}catch (Exception e){
logger.error(
String.format("Error setting up constructor parameter value : %s : %s", clazz, headers[i]),
e);
}
}
try {
instance = c.newInstance(params);
} catch (Exception e) {
logger.error( String.format("Error creating object through constructor by intelligence: %s", clazz));
}
}
else{
try {
instance = Utils.createInstance(clazz.getName(), params);
} catch (Exception e) {
logger.error(
String.format("Error creating object through constructor by parameter type : %s", clazz),e);
}
}
}
else{
instance = Utils.createInstance(clazz.getName());
String[] headers = dataSource.tables.get(tableName).row(0);
String[] values = dataSource.tables.get(tableName).row(rowNum);
for ( int i = 0 ; i < headers.length ;i++ ){
logger.debug( String.format("Working field with : [%s]",headers[i]));
arr = headers[i].split(SPLIT_STRING) ;
String fieldName = arr[0].trim();
Field field = getField(clazz,fieldName);
if ( field == null ){
logger.error(
String.format("In the column no [%d] : A field with name [%s] does not exist in the class [%s]!",
i, fieldName,clazz.getName())
);
continue;
}
try {
Object fieldValue = object(values[i], field.getType(), field.getGenericType(), headers[i]);
field.set( instance, fieldValue);
}catch (Exception e){
logger.error(String.format("Error setting up field value : %s : %s", clazz, fieldName),e);
}
}
}
return instance;
}
private Object map(String value, Class clazz, Type type, String header) {
if ( Map.class.isAssignableFrom( clazz) || Dictionary.class.isAssignableFrom(clazz) ){
return new HashMap();
}
if ( Set.class.isAssignableFrom(clazz)){
return new HashSet<>();
}
return null;
}
/**
* For container types, tries to get inner type
* @param type the type
* @return inner type, if possible
*/
public static String getInnerType(Type type){
String name = type.getTypeName();
int firstIndex = name.indexOf('<');
int lastIndex = name.lastIndexOf('>');
name = name.substring(firstIndex+1,lastIndex);
return name;
}
private Object list(String value, Type type, String header) {
ArrayList list = new ArrayList();
String innerName = getInnerType(type);
try{
String[] values;
Class innerClass = Class.forName(innerName);
if ( value.startsWith( LIST_REDIRECT ) ){
value = value.substring(1);
String[] arr = value.split(SPLIT_STRING);
String tableName = arr[0].trim();
int rowNum = Integer.parseInt(arr[1].trim());
values = dataSource.tables.get(tableName).row(rowNum);
}else{
values = value.split(LIST_SEP_STRING);
}
for ( int i = 0 ; i < values.length;i++ ){
Object item = object(values[i], innerClass, innerClass, "");
list.add(item);
}
}catch (Exception e){
logger.error(String.format("Failing to find inner class : %s", innerName),e);
}
return list;
}
/**
* Given metadata information creates one instance
* @param methodRunInformation metadata information
*/
public ArgConverter(Annotations.MethodRunInformation methodRunInformation){
method = methodRunInformation.method ;
parameterTypes = method.getGenericParameterTypes();
parameterClasses = method.getParameterTypes();
String dsLoc = methodRunInformation.base + "/" + methodRunInformation.nApi.dataSource();
dataSource = ProviderFactory.dataSource(dsLoc);
dataTableName = methodRunInformation.nApi.dataTable();
}
}