/*
* Copyright 2013, The Sporting Exchange Limited
*
* 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.betfair.testing.utils.cougar.misc;
import org.json.JSONException;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Reflect implements IReflect {
/*
* (non-Javadoc)
*
* @see
* com.betfair.reflection.IReflect#getPropertyValue(java.util.ArrayList,
* java.lang.Object)
*/
//-----DD/MM/YY hh:mm:ss.S
//-----DD/MM/YYYY hh:mm:ss.S
private static final String timestamPatternString = "(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[012])/((\\d\\d)|((19|20)\\d\\d))\\s([1-9]|([01][0-9])|(2[0-3])):((0[0-9])|([12345][0-9])):((0[0-9])|([12345][0-9])).[0-9]*";
private static final Pattern timestamPattern = Pattern.compile(timestamPatternString);
@SuppressWarnings("unchecked")
public Object getPropertyValue(ArrayList propertyList, Object obj) {
// *** get the name of the property ...
String propertyName = (String) propertyList.get(0);
// *** this split is used to check whether we have
// *** an array to deal with, e.g. "Bet 1" indicates
// *** getBet(0) ...
String[] split = propertyName.split(" ");
String indexStr = null;
// *** if this is an array then
// *** get its index ...
if (split.length == 2) {
indexStr = split[1];
propertyName = split[0];
}
// *** locate the get method ...
Method getMethod = null;
try {
/* Make first letter of property name uppercase */
String convertedPropertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
getMethod = searchForMethod(obj, convertedPropertyName);
// getMethod = obj.getClass().getMethod("get" + convertedPropertyName);
} catch (SecurityException e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new RuntimeException("Error: get" + propertyName + " does not exist in "
+ obj.getClass().toString(), e);
}
Object propertyInstance = null;
try {
propertyInstance = getMethod.invoke(obj);
} catch (Exception e) {
throw new RuntimeException("Error: Invoking get" + propertyName + " on " + "object "
+ obj.getClass().toString(), e);
}
// *** get the return type of the getter ...
Class<?> returnType = getMethod.getReturnType();
// *** if we are not at the actual get method we're looking for
// *** then attempt to go through the nest until we reach it ...
if (propertyList.size() >= 2) {
// *** if this property has not been
// *** set, then set it ...
if (propertyInstance != null) {
// *** if it is an array then we need to
// *** make sure that the item referred to
// *** isn't null + we assign propertyItem
// *** is equal to the item ...
if (returnType.isArray()) {
propertyInstance = getArrayItem(indexStr, propertyInstance, returnType);
}
ArrayList newPropertyList = new ArrayList();
for (int i = 1; i < propertyList.size(); i++) {
newPropertyList.add(propertyList.get(i));
}
propertyInstance = getPropertyValue(newPropertyList, propertyInstance);
}
} else {
if (propertyInstance != null) {
if (returnType.isArray()) {
propertyInstance = getArrayItem(indexStr, propertyInstance, returnType);
}
}
}
return propertyInstance;
}
private Object getArrayItem(String indexStr, Object propertyInstance, Class<?> returnType) {
// *** get the index ...
int index = 0;
try {
index = Integer.parseInt(indexStr) - 1;
} catch (Exception e) {
throw new RuntimeException("Error parsing array index.");
}
// *** get the current array length ...
int currentArrayLength = Array.getLength(propertyInstance);
// *** if the specified index is outside
// *** current array length then create a
// *** new item ...
if (index < currentArrayLength) {
// *** attempt to get item from current
// *** array ...
propertyInstance = Array.get(propertyInstance, index);
} else {
propertyInstance = null;
}
return propertyInstance;
}
public void setValueToProperty(List<?> propertyList, Object value, Object obj) {
// *** get the name of the property ...
String propertyName = (String) propertyList.get(0);
// *** if we are not at the primitive type
// *** yet ...
if (propertyList.size() >= 2) {
// *** this split is used to check whether we have
// *** an array to deal with, e.g. "Bet 1" indicates
// *** getBet(0) ...
String[] split = propertyName.split(" ");
String indexStr = null;
// *** if this is an array then
// *** get its index ...
if (split.length == 2) {
indexStr = split[1];
propertyName = split[0];
}
// *** locate the get method ...
Method getMethod = null;
try {
// getMethod = obj.getClass().getMethod("get" + propertyName);
String convertedPropertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
getMethod = searchForMethod(obj, convertedPropertyName);
} catch (Exception e) {
throw new RuntimeException("Error: locating get" + propertyName + " on " + "object "
+ obj.getClass().toString() + e.getMessage());
}
if (getMethod != null) {
Object propertyInstance = null;
try {
propertyInstance = getMethod.invoke(obj);
} catch (Exception e) {
throw new RuntimeException("Error: Invoking get" + propertyName + " on " + "object "
+ obj.getClass().toString() + e.getMessage());
}
// *** get the return type of the getter ...
Class<?> returnType = getMethod.getReturnType();
// *** if this property has not been
// *** set, then set it ...
if (propertyInstance == null) {
// *** if this property is not an
// *** array, then treat it normally ...
if (!returnType.isArray()) {
// *** create a new instance of the
// *** return type ...
try {
propertyInstance = returnType.getConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Error: Creating an instance of type " + returnType
+ "from get method " + getMethod.getName() + ". " + e.getMessage());
}
// *** get its equivalent setter method ...
Method setMethod = null;
try {
setMethod = obj.getClass().getMethod("set" + propertyName, new Class[] { returnType });
} catch (Exception e) {
throw new RuntimeException("Error: locating set method set" + propertyName
+ "from object " + obj.getClass().toString() + ". " + e.getMessage());
}
try {
// *** set the property ...
setMethod.invoke(obj, propertyInstance);
} catch (Exception e) {
throw new RuntimeException("Error: invoking set method set" + propertyName
+ "from object " + obj.getClass().toString() + ". " + e.getMessage());
}
} else {
// *** get the index we wish to set ...
int index = 0;
try {
index = Integer.parseInt(indexStr.trim()) - 1;
} catch (Exception e) {
throw new RuntimeException("Error: parsing index " + indexStr + ".");
}
if (index < 0) {
throw new RuntimeException("Array index is less " + "than zero: " + index + ".");
}
// *** get the component type ...
Class<?> componentType = returnType.getComponentType();
Object arr = Array.newInstance(componentType, index + 1);
try {
propertyInstance = componentType.getConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Error: creating instance of array item of type "
+ componentType.toString() + ". " + e.getMessage());
}
Array.set(arr, index, propertyInstance);
Method setMethod = null;
try {
setMethod = obj.getClass().getMethod("set" + propertyName, new Class[] { returnType });
} catch (Exception e) {
throw new RuntimeException("Error: locating set method set" + propertyName
+ "from object " + obj.getClass().toString() + ". " + e.getMessage());
}
try {
// *** set the property ...
setMethod.invoke(obj, arr);
} catch (Exception e) {
throw new RuntimeException("Error: invoking set method set" + propertyName
+ "from object " + obj.getClass().toString() + ". " + e.getMessage());
}
}
} else
// *** if it is an array then we need to
// *** make sure that the item referred to
// *** isn't null + we assign propertyItem
// *** is equal to the item ...
if (returnType.isArray()) {
// *** get the index ...
int index = Integer.parseInt(indexStr) - 1;
// *** get the current array length ...
int currentArrayLength = Array.getLength(propertyInstance);
int newArrayLength = currentArrayLength < (index + 1) ? (index + 1) : currentArrayLength;
// *** 1. Create a new array item
// *** 2. Create a new ArrayList
// *** 3. Store current array items in ArrayList
// *** 4. Append new array item to ArrayList
// *** 5. Set new array
// *** 6. Assign propertyInstance to new item
// *** this is the class type of the array item ...
Class<?> componentType = returnType.getComponentType();
// *** this is the array item ...
Object arrItem = null;
// *** if the specified index is outside
// *** current array length then create a
// *** new item ...
if (newArrayLength > currentArrayLength) {
try {
arrItem = componentType.newInstance();
} catch (Exception e) {
throw new RuntimeException("Error: creating new array item instance of type "
+ componentType.toString() + " " + e.getMessage());
}
} else {
// *** attempt to get item from current
// *** array ...
arrItem = Array.get(propertyInstance, index);
// *** if this item is null then create
// *** a new instance of it ...
if (arrItem == null) {
try {
arrItem = componentType.newInstance();
} catch (Exception e) {
throw new RuntimeException("Error: creating new array item instance of type "
+ componentType.toString() + " " + e.getMessage());
}
}
}
Object arr = Array.newInstance(componentType, newArrayLength);
int currentIndex = 0;
// *** add current items to new array instance ...
for (; currentIndex < currentArrayLength; currentIndex++) {
// *** if we are at the index position then
// *** add the new item ...
Object item = null;
if (currentIndex == index) {
item = arrItem;
} else {
item = Array.get(propertyInstance, currentIndex);
}
Array.set(arr, currentIndex, item);
}
for (; currentIndex < newArrayLength; currentIndex++) {
// *** if we are at the index position then
// *** add the new item ...
if (currentIndex == index) {
Array.set(arr, currentIndex, arrItem);
}
}
Method setMethod = null;
try {
setMethod = obj.getClass().getMethod("set" + propertyName, new Class[] { returnType });
} catch (Exception e) {
throw new RuntimeException("Error: locating set method 'set" + propertyName + "' "
+ "from object " + obj.getClass().toString() + ". " + e.getMessage());
}
try {
// *** set the new array with new item ...
setMethod.invoke(obj, arr);
} catch (Exception e) {
throw new RuntimeException("Error: invoking set method 'set" + propertyName + "' "
+ "from object " + obj.getClass().toString() + ". " + e.getMessage());
}
// *** set the new item to the
// *** property instance ...
propertyInstance = arrItem;
}
ArrayList newPropertyList = new ArrayList();
for (int i = 1; i < propertyList.size(); i++) {
newPropertyList.add(propertyList.get(i));
}
setValueToProperty(newPropertyList, value, propertyInstance);
}
} else {
// *** locate get method return type ...
Class<?> returnClassType = null;
Method method = null;
try {
/* Make first letter of property name uppercase */
String convertedPropertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
method = searchForMethod(obj, convertedPropertyName);
returnClassType = method.getReturnType();
}catch (NoSuchMethodException e) {
throw new RuntimeException("Error: locating return type from 'get" + propertyName + "' "
+ "from object " + obj.getClass().toString() + e.getMessage());
}catch (Exception e) {
e.printStackTrace();
}
// *** locate set method ...
Method setMethod = null;
try {
/* Make first letter of property name uppercase */
String convertedPropertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
setMethod = obj.getClass().getMethod("set" + convertedPropertyName, new Class[] { returnClassType });
} catch (Exception e) {
throw new RuntimeException("Error: locating set method 'set" + propertyName + "' "
+ "from object " + obj.getClass().toString() + ". " + e.getMessage());
}
if (isBoxable(returnClassType)) {
Object realValue = getRealProperty(setMethod.getParameterTypes()[0], value);
try {
setMethod.invoke(obj, realValue);
} catch (Exception e) {
throw new RuntimeException("Error: invoking set method 'set" + propertyName + "' "
+ "from object " + obj.getClass().toString() + ". " + e.getMessage());
}
} else { //if (returnClassType == Object.class) {
try {
setMethod.invoke(obj, value);
} catch (Exception e) {
throw new RuntimeException("Error: invoking set method 'set" + propertyName + "' "
+ "from object " + obj.getClass().toString() + ". " + e.getMessage());
}
}
}
}
@SuppressWarnings("unchecked")
// FIXME: Rename to getWrappedValue
public Object getRealProperty(Class clazz, Object value) {
if (value == null) {
return null;
}
if (clazz.equals(int.class)) {
return Integer.parseInt(value.toString());
} else if (clazz.equals(Integer.class)) {
return new Integer(value.toString());
} else if (clazz.equals(String.class)) {
// return value;
return value.toString();
} else if (clazz.equals(double.class)) {
return Double.parseDouble(value.toString());
} else if (clazz.equals(Double.class)) {
return new Double(value.toString());
} else if (clazz.equals(boolean.class)) {
return Boolean.parseBoolean(value.toString());
} else if (clazz.equals(Boolean.class)) {
return new Boolean(value.toString());
} else if (clazz.equals(float.class)) {
return Float.parseFloat(value.toString());
} else if (clazz.equals(Float.class)) {
return new Float(value.toString());
} else if (clazz.equals(byte.class)) {
return Byte.parseByte(value.toString());
} else if (clazz.equals(Byte.class)) {
return new Byte(value.toString());
} else if (clazz.equals(short.class)) {
return Short.parseShort(value.toString());
} else if (clazz.equals(Short.class)) {
return new Short(value.toString());
} else if (clazz.equals(long.class)) {
return Long.parseLong(value.toString());
} else if (clazz.equals(Long.class)) {
return new Long(value.toString());
} else if (clazz.isEnum()) {
return Enum.valueOf(clazz, value.toString());
} else if (clazz.equals(BigDecimal.class)) {
return new BigDecimal(value.toString());
} else if (clazz.equals(org.json.JSONObject.class)) {
try {
return new org.json.JSONObject(value.toString());
} catch (JSONException e) {
throw new RuntimeException("Problem reflecting to JSONObject", e);
}
} else if (clazz.isEnum()) {
Object[] enums = clazz.getEnumConstants();
for (Object enumValue: enums) {
if (enumValue.toString().equalsIgnoreCase(value.toString())) {
return enumValue;
}
}
throw new RuntimeException("Unable to match specified enum value");
} else if (clazz.equals(Timestamp.class)) {
java.util.Date date = getJavaUtilDateFromString(value.toString());
return new Timestamp(date.getTime());
} else if (clazz.equals(java.util.Date.class)) {
return getJavaUtilDateFromString(value.toString());
} else if (clazz.equals(Class.class)) {
try {
return (Class)value;
} catch (ClassCastException e) {
//Do nothing
}
try {
return Class.forName(value.toString());
} catch (ClassNotFoundException e) {
if (value.toString().startsWith("class ")) {
try {
return Class.forName(value.toString().substring(6));
} catch (ClassNotFoundException e1) {
throw new RuntimeException("Problem reflecting to Class object: " + value.toString().substring(6), e);
}
}
throw new RuntimeException("Problem reflecting to Class object: " + value.toString(), e);
}
} else {
Method fromValueMethod;
Object enumValue = null;
try {
fromValueMethod = clazz.getMethod("fromValue", new Class[] { String.class });
try {
enumValue = fromValueMethod.invoke(null, new Object[] { value });
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
} catch (SecurityException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
return enumValue;
}
}
// FIXME: Rename to isBoxable
private boolean isBoxable(Class<?> c) {
boolean primitive = c.equals(int.class) || c.equals(Integer.class) || c.equals(String.class)
|| c.equals(double.class) || c.equals(Double.class) || c.equals(boolean.class)
|| c.equals(Boolean.class) || c.equals(float.class) || c.equals(Float.class) || c.equals(byte.class)
|| c.equals(Byte.class) || c.equals(short.class) || c.equals(Short.class) || c.equals(long.class)
|| c.equals(Long.class) || c.equals(char.class) || c.isEnum();
if (!primitive) {
try {
primitive = c.getMethod("fromValue", new Class[] { String.class }) != null;
} catch (SecurityException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
//throw new RuntimeException("No such method as 'fromValue' on " + c.getName(), e);
return false;
}
}
return primitive;
}
/*
* (non-Javadoc)
*
* @see
* com.betfair.reflection.IReflect#getParameterTypes(java.lang.Object[])
*/
public Class[] getParameterTypes(Object[] parameters) {
ArrayList parameterTypeList = new ArrayList();
Class<?>[] p = null;
if (parameters != null) {
for (int i = 0; i < parameters.length; i++) {
parameterTypeList.add(parameters[i].getClass());
}
// *** create a definition of the types of
// *** parameters ...
p = (Class[]) parameterTypeList.toArray(new Class<?>[parameterTypeList.size()]);
}
return p;
}
public Class matchStringToClass(String classString) {
if ((classString == null) || (classString.equalsIgnoreCase(""))) {
return null;
}
if (int.class.getName().endsWith(classString)) {
return int.class;
} else if (Integer.class.getName().endsWith(classString)) {
return Integer.class;
} else if (String.class.getName().endsWith(classString)) {
return String.class;
} else if (double.class.getName().endsWith(classString)) {
return double.class;
} else if (Double.class.getName().endsWith(classString)) {
return Double.class;
} else if (boolean.class.getName().endsWith(classString)) {
return Boolean.class;
} else if (Boolean.class.getName().endsWith(classString)) {
return Boolean.class;
} else if (float.class.getName().endsWith(classString)) {
return float.class;
} else if (Float.class.getName().endsWith(classString)) {
return Float.class;
} else if (byte.class.getName().endsWith(classString)) {
return byte.class;
} else if (Byte.class.getName().endsWith(classString)) {
return Byte.class;
} else if (short.class.getName().endsWith(classString)) {
return short.class;
} else if (Short.class.getName().endsWith(classString)) {
return Short.class;
} else if (long.class.getName().endsWith(classString)) {
return long.class;
} else if (Long.class.getName().endsWith(classString)) {
return Long.class;
} else if (BigDecimal.class.getName().endsWith(classString)) {
return BigDecimal.class;
} else if (classString.endsWith("Enum")) {
throw new RuntimeException("Currently unable to match class strings to Enums: " + classString);
} else if (Timestamp.class.getName().endsWith(classString)) {
return Timestamp.class;
} else {
throw new RuntimeException("No mapping exists for class string: " + classString);
}
}
private java.util.Date getJavaUtilDateFromString(String dateTimeString) {
String formattedDateString;
try {
formattedDateString = StringHelpers.formatDateString2((String) dateTimeString);
} catch (ParseException e1) {
throw new IllegalStateException("Unable to covert passed value to java.util.date as do not have a matching pattern: " + dateTimeString);
}
Matcher m;
boolean matchFound;
m = timestamPattern.matcher(formattedDateString);
matchFound = m.matches();
if (matchFound) {
DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy H:mm:ss.S");
java.util.Date date;
try {
date = (java.util.Date)formatter.parse(formattedDateString);
} catch (ParseException e) {
throw new RuntimeException(e);
}
return date;
} else {
//maybe a long was passed
try {
Long longval = Long.valueOf(dateTimeString);
return new java.util.Date(longval);
} catch (NumberFormatException e) {
throw new IllegalStateException("Unable to covert passed value to java.util.date as do not have a matching pattern: " + dateTimeString);
}
}
}
private Method searchForMethod(Object obj, String convertedPropertyName) throws NoSuchMethodException{
Method[] methods = obj.getClass().getMethods();
for (Method method : methods) {
//check if get*** exist; otherwise try is*** if the return type is boolean
//Note: if the property is Boolean, it will use get***. If the property is boolean, it will use is***.
if (method.getName().equalsIgnoreCase("get" + convertedPropertyName) ||
(method.getReturnType().getSimpleName().equalsIgnoreCase("boolean") && method.getName().equalsIgnoreCase("is" + convertedPropertyName))) {
return method;
}
}
throw new NoSuchMethodException("Method get" + convertedPropertyName + " can't be found in object " + obj.getClass().getName());
}
/*
* Set the attributes of the passed bean to the values specfied in the passed attributeMap.
*
* In passed map key should equal bean attribute name and value the value attribute to
* be set to. Only pass entries for attributes you want to set.
*
*/
public void setBeanAttributes(Object bean, Map<String, ?> attributeMap) {
for (String key: attributeMap.keySet()) {
ArrayList<String> tempList = new ArrayList<String>();
tempList.add(key);
try {
setValueToProperty(
tempList,
attributeMap.get(key),
bean);
} catch (Exception e) {
throw new RuntimeException("Problem setting bean attribute '" + key + "' to: " + String.valueOf(attributeMap.get(key)), e);
}
}
}
/**
* invokes given method of the given object o via reflection
* @param m method
* @param o object
* @return invocation value
*/
@Override
public Object invokeReflection(Method m, Object o, Object... args) throws RuntimeException {
try {
return m.invoke(o, args);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot access " + m.getClass().getName() + "." + m.getName(), e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Reflection failed for " + m.getClass().getName() + "." + m.getName(), e);
}
}
}