package org.msh.utils;
import org.apache.commons.beanutils.PropertyUtils;
import javax.xml.bind.DatatypeConverter;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.*;
/**
* General utility functions involving an object
*
* Created by rmemoria on 28/1/16.
*/
public class ObjectUtils {
/**
* Since there are just static methods, avoid others of creating this class
*/
private ObjectUtils() {
super();
}
/**
* Read the property value of an object
*
* @param obj the object to have its property read
* @param property the property name to read
* @return the property value, or {@link ObjectAccessException} if any error occurs
*/
public static Object getProperty(Object obj, String property) {
try {
return PropertyUtils.getProperty(obj, property);
} catch (Exception e) {
throw new ObjectAccessException(obj, property, "Error reading property " + property, e);
}
}
/**
* Set a property value of an object
*
* @param obj the object to have its property value set
* @param property the property name to set its value
* @param value the value to set
*/
public static void setProperty(Object obj, String property, Object value) {
try {
PropertyUtils.setProperty(obj, property, value);
} catch (Exception e) {
throw new ObjectAccessException(obj, property,
"Error writing property " + obj + '.' + property + " with value " + value, e);
}
}
/**
* Return property type of the given property in the given object
*
* @param obj the object to have its property type returned
* @param property the name of the property in the object
* @return the property type
*/
public static Class getPropertyType(Object obj, String property) {
try {
return PropertyUtils.getPropertyType(obj, property);
} catch (Exception e) {
throw new ObjectAccessException(obj, property,
"Error getting type of property " + property, e);
}
}
/**
* Create a new instance of a given class. The class must implement a constructor with
* no arguments, otherwise a {@link ObjectAccessException} will be thrown
*
* @param clazz The class to create an instance from
* @return the object instance of the given class
*/
public static E newInstance(Class clazz) {
try {
return clazz.newInstance();
} catch (Exception e) {
throw new ObjectAccessException("Error when trying to create an instance of " + clazz, e);
}
}
/**
* Return the generic type assigned to the given class
*
* @param clazz the class to get the generic type assigned to
* @param typeindex the index of the generic type, when there are more than one, but must be 0
* if there is just one single generic type or you want the first generic type
* @return the generic class type assigned to the class
*/
public static Class getGenericType(Class clazz, int typeindex) {
Type type = clazz.getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) type;
if (paramType.getActualTypeArguments().length > 0) {
return (Class) paramType.getActualTypeArguments()[typeindex];
}
}
return null;
}
/**
* Get a class by its name
*
* @param className the full qualified class name
* @return The class
*/
public static Class forClass(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
throw new ObjectAccessException("Class not found: " + className, e);
}
}
/**
* Return a map containing the object property name and its values
*
* @param obj
* @return
*/
public static Map describeProperties(Object obj) {
Map values;
try {
values = PropertyUtils.describe(obj);
PropertyDescriptor[] props = PropertyUtils.getPropertyDescriptors(obj);
// remove properties that don't have a get or a set
for (PropertyDescriptor prop : props) {
if (prop.getReadMethod() == null || prop.getWriteMethod() == null) {
values.remove(prop.getName());
}
}
} catch (Exception e) {
throw new ObjectAccessException("Error getting object properties", e);
}
return values;
}
/**
* Convert an array of bytes to an UUID object
*
* @param val an array of bytes
* @return instance of UUID
*/
public static UUID bytesToUUID(byte[] val) {
ByteBuffer bb = ByteBuffer.wrap(val);
long high = bb.getLong();
long low = bb.getLong();
return new UUID(high, low);
}
/**
* Convert an UUID instance to an array of bytes
* @param uuid
* @return
*/
public static byte[] uuidAsBytes(UUID uuid) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return bb.array();
}
/**
* Return the generic type declared in the field property of the class
* @param beanClass
* @param fieldName
* @param typeIndex
* @return
*/
public static Class getPropertyGenericType(Class beanClass, String fieldName, int typeIndex) {
Field field = findField(beanClass, fieldName);
if (field == null) {
throw new ObjectAccessException("Class field not found: [" + fieldName + "] in class " + beanClass);
}
return getPropertyGenericType(field, typeIndex);
}
/**
* Return the generic type declared in the field instance
* @param field the field containing the generic type declaration
* @param typeIndex the declaration order of the generic type (zero is the first)
* @return the generic type declaration, or null if no generic type is declared
*/
public static Class getPropertyGenericType(Field field, int typeIndex) {
Type type = field.getGenericType();
if (!(type instanceof ParameterizedType)) {
return null;
}
ParameterizedType ptype = (ParameterizedType)type;
Type[] typeArgs = ptype.getActualTypeArguments();
return (Class)typeArgs[typeIndex];
}
/**
* Search for the instance of the field that declared the property in the given
* class or its super classes
* @param beanClass the bean class name
* @param fieldName the field name to search for
* @return the instance of Field or null if field is not found
*/
public static Field findField(Class beanClass, String fieldName) {
Class clazz = beanClass;
while (clazz != null && clazz != Object.class) {
Field[] fields = clazz.getDeclaredFields();
for (Field field: fields) {
if (field.getName().equals(fieldName)) {
return field;
}
}
clazz = clazz.getSuperclass();
}
return null;
}
/**
* Search for a method by its name in the bean class or, if not found, in the super classes
* @param beanClass the class to search the method
* @param methodName the method name
* @return instance of the method from java reflection, or null if not found
*/
public static Method findMethod(Class beanClass, String methodName) {
Class clazz = beanClass;
while (clazz != null && clazz != Object.class) {
Method[] methods = clazz.getDeclaredMethods();
for (Method method: methods) {
if (method.getName().equals(methodName)) {
return method;
}
}
clazz = clazz.getSuperclass();
}
return null;
}
/**
* Convert a string to an enum value of the given enum class
* @param value the string representation of the enum value
* @param enumClass the enum class
* @param
* @return the enum value
*/
public static E stringToEnum(String value, Class enumClass) {
Object[] vals = enumClass.getEnumConstants();
for (Object val: vals) {
if (val.toString().equals(value)) {
return (E)val;
}
}
return null;
}
/**
* Make a shallow copy of the properties of one object to another
* Source and target objects doesn't have to be of same type, and just shared
* properties available in target are copied. Properties with the same name must
* have the same type, otherwise an exception will be thrown
*
* @param source the source object
* @param target the target object that will receive the property values from source
*/
public static void copyObject(Object source, Object target) {
Map props = describeProperties(source);
Map targProps = describeProperties(target);
for (Map.Entry entry: props.entrySet()) {
String prop = entry.getKey();
if (targProps.containsKey(prop)) {
setProperty(target, prop, entry.getValue());
}
}
}
}