All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.util.ObjectUtil Maven / Gradle / Ivy

There is a newer version: 3.1
Show newest version
/*
 * #%L
 * Nuiton Utils
 * 
 * $Id: ObjectUtil.java 2502 2013-02-23 13:06:57Z bpoussin $
 * $HeadURL: http://svn.nuiton.org/svn/nuiton-utils/tags/nuiton-utils-2.6.10/nuiton-utils/src/main/java/org/nuiton/util/ObjectUtil.java $
 * %%
 * Copyright (C) 2004 - 2010 CodeLutin
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */

/* *
 * ObjectUtil.java
 *
 * Created: 2 nov. 2004
 *
 * @author Benjamin Poussin 
 * @version $Revision: 2502 $
 *
 * Mise a jour: $Date: 2013-02-23 14:06:57 +0100 (Sat, 23 Feb 2013) $
 * par : */

package org.nuiton.util;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import static org.nuiton.i18n.I18n._;

/**
 * Outils pour manipuler des objets. Création d'un objet à partir d'une chaîne
 * le décrivant, conversion d'un objet en Object, récupération de méthodes
 * à partir de leur nom, de constructeurs à partir de leurs paramètres...
 * 

* Created: 4 novembre 2004 * * @author bpoussin * $Id: ObjectUtil.java 2502 2013-02-23 13:06:57Z bpoussin $ */ public class ObjectUtil { // ObjectUtil /** Logger. */ static private Log log = LogFactory.getLog(ObjectUtil.class); /** Used to know what is separator between class and method*/ public static final String CLASS_METHOD_SEPARATOR = "#"; protected static final Integer ZERO = 0; protected static final Character ZEROC = (char) 0; protected static final Float ZEROF = 0f; protected static final Long ZEROL = 0l; protected static final Double ZEROD = 0.; protected static final Byte ZEROB = 0; /** * ObjectUtil constructor * private because of this class is a static class : nobody * can make an instance of this class */ private ObjectUtil() { } /** * Invoke constructor on clazz to create new instance. Try to find argument * for constructor in args parameter. * * @param clazz class of object to instanciate * @param args all possible parameter that constructor can used * @param nullIfMissing if no suitable class or object found in args, * use null value (no exception) * @return new instance * @throws IllegalArgumentException if something is wrong during instanciation */ public static E newInstance(Class clazz, Collection args, boolean nullIfMissing) { Constructor[] constructors = (Constructor[]) clazz.getConstructors(); if (constructors.length != 1) { throw new IllegalArgumentException( _("nuitonutil.error.class.with.more.than.one.constructor", clazz)); } // copy collection into modifiable list to add new object List container = new LinkedList(args); Constructor constructor = constructors[0]; Class[] paramTypes = constructor.getParameterTypes(); Object[] params = new Object[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { Object o = choiceArgument(paramTypes[i], container, nullIfMissing); params[i] = o; } try { E result = constructor.newInstance(params); return result; } catch (Exception eee) { throw new IllegalArgumentException(_( _("nuitonutil.error.cant.instanciate.class", clazz, Arrays.toString(params))), eee); } } /** * Permet de matcher un type d'argument attendu clazz parmi un ensemble * possible de candidat. Les candidats peuvent etre des classes qu'il faudra * instancier pour satisfaire le type demande. * * @param clazz le type recherché * @param args la liste des arguments ou des types * @param nullIfMissing pour retourner nulle si l'argument n'est pas trouvé * @return le type d'argument trouvé */ static protected Object choiceArgument(Class clazz, List args, boolean nullIfMissing) { Object result = null; boolean addResult = false; for (Object o : args) { if (o != null) { if (o instanceof Class && clazz.isAssignableFrom((Class) o)) { // cas on l'on trouve une class dans arg qui une fois // instancier convient result = newInstance((Class) o, args, nullIfMissing); addResult = true; break; } else if (clazz.isInstance(o)) { // cas on l'on retrouve un objet assignable pour ce type result = o; break; } } } if (addResult) { // on ajoute en tete pour qu'il soit retrouve dans les premiers // et non pas reinstancier une nouvelle fois si on en a besoin // a nouveau args.add(0, result); } // si on ne retrouve rien, result est reste a null if (result == null && !nullIfMissing) { throw new IllegalArgumentException(_( _("nuitonutil.error.unfound.assignable.argument", clazz, args))); } return result; } /** * Create new object from string like org.nuiton.Toto(name=machine, int=10) * where machine and int is properties on org.nuiton.Toto object. * Conversion between 10 in string and 10 as integer as automaticaly done *

* For String property you can use ex: *

  • name="my string with , in string" *
  • name='my string with , in string' * * @param classnameAndProperties * @return the instanciated object * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException * @throws NoSuchMethodException * @throws InvocationTargetException */ public static Object create(String classnameAndProperties) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { int p = classnameAndProperties.indexOf('('); int l = classnameAndProperties.lastIndexOf(')'); String[] properties = null; String classname; if (p != -1) { String tmp = classnameAndProperties.substring(p + 1, l); properties = StringUtil.split(tmp, ","); classname = classnameAndProperties.substring(0, p); } else { classname = classnameAndProperties; } ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class clazz = loader.loadClass(classname); Object o = clazz.getConstructor().newInstance(); if (properties != null) { for (String prop : properties) { int e = prop.indexOf('='); String propName = prop.substring(0, e).trim(); String propValue = prop.substring(e + 1).trim(); if (propValue.charAt(0) == '"' && propValue.charAt(propValue.length() - 1) == '"') { propValue = propValue.substring(1, propValue.length() - 1); } else if (propValue.charAt(0) == '\'' && propValue.charAt(propValue.length() - 1) == '\'') { propValue = propValue.substring(1, propValue.length() - 1); } BeanUtils.setProperty(o, propName, propValue); } } return o; } static protected Object convert(String v, Class clazz) { Object t = ConvertUtils.convert(v, clazz); if (t != null && !String.class.getName().equals(clazz.getName()) && String.class.getName().equals(t.getClass().getName())) { throw new IllegalArgumentException(String.format( "Can convert argument to correct type. %s can't be" + " converted from String to %s conversion is done to %s", v, clazz.getName(), t.getClass().getName())); } return t; } /** * Clone object by introspection because Cloneable interface don't permit * to call clone :(. This methode replace next code that don't work :( *

    *

         * if (o instanceof Cloneable) {
         *   Object n = ((Cloneable)o).clone();
         * }
         * 
    * * @param e object to clone * @return new instance of E * @throws CloneNotSupportedException if some error occur during clone */ public static E clone(E e) throws CloneNotSupportedException { try { E result = (E) MethodUtils.invokeExactMethod(e, "clone", null); return result; } catch (Exception eee) { // on est oblige de faire un log, car CloneNotSupportedException // ne prend pas d'exception en arguement :( log.error("Can't clone object", eee); throw new CloneNotSupportedException(); } } /** * Use serialization/deserialization to do deep clone of object * * @param e object to clone * @return new instance of E * @throws CloneNotSupportedException if some error occur during clone */ public static E deepClone(E e) throws CloneNotSupportedException { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); try { oos.writeObject(e); } finally { oos.close(); } ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); E result; ObjectInputStream ois = new ObjectInputStream(bis); try { result = (E) ois.readObject(); } finally { ois.close(); } return result; } catch (Exception eee) { // on est oblige de faire un log, car CloneNotSupportedException // ne prend pas d'exception en arguement :( log.error("Can't clone object", eee); throw new CloneNotSupportedException(); } } /** * Call method m with params as String. Each param is converted to required type for * method with beanutils converter * * @param o object where method must be call * @param m method to call * @param params parameters for method call * @return returned method's value * @throws IllegalAccessException * @throws IllegalArgumentException * @throws InvocationTargetException * @throws InstantiationException */ public static Object call(Object o, Method m, String... params) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { Class[] types = m.getParameterTypes(); if (!m.isVarArgs() && params.length != types.length) { throw new IllegalArgumentException(String.format( "Bad number params we have %1$s parameters and waiting %2$s.", params.length, types.length)); } int last = types.length; if (m.isVarArgs()) { // on traite le dernier differement last--; } Object[] parameters = new Object[types.length]; for (int i = 0; i < last; i++) { String v = params[i]; Class clazz = types[i]; Object t = convert(v, clazz); parameters[i] = t; } if (m.isVarArgs()) { Class clazz = types[last]; // get var args type clazz = clazz.getComponentType(); // get array component type List tmp = new ArrayList(); for (int i = last; i < params.length; i++) { String v = params[i]; Object t = convert(v, clazz); tmp.add(t); } parameters[last] = tmp.toArray((Object[]) Array.newInstance(clazz, tmp.size())); } if (log.isDebugEnabled()) { log.debug(_("nuitonutil.debug.objectutil.invoke", m, Arrays.toString(parameters))); } Object result = m.invoke(o, parameters); return result; } /** * Get all methods with name given in argument without check parameters. * * @param clazz where to search method * @param methodName method name to search * @param ignoreCase if true, ignore difference in method name case * @return list of detected methods */ public static List getMethod(Class clazz, String methodName, boolean ignoreCase) { List result = new ArrayList(); Method[] methods = clazz.getMethods(); for (Method m : methods) { if (ignoreCase && methodName.equalsIgnoreCase(m.getName()) || methodName.equals(m.getName())) { result.add(m); } } return result; } /** * List method that match name, name must be [package.][class][#][method] * if package, class or method missing, exception throw * * @param name name of the method * @param ignoreCase check exact method name if false * @return list of method that match name * @since 2.6.9 */ static public List getMethod(String name, boolean ignoreCase) { Class clazz; String className; String methodName; // looking for method name int sep = name.lastIndexOf(CLASS_METHOD_SEPARATOR); if (sep == -1) { throw new IllegalArgumentException(String.format( "Can't find method in %s", name)); } else { className = name.substring(0, sep); methodName = name.substring(sep + 1); } // looking for class name try { clazz = Class.forName(className); } catch (ClassNotFoundException eee) { throw new IllegalArgumentException(String.format( "Can't find class %s", className)); } List result = ObjectUtil.getMethod(clazz, methodName, ignoreCase); return result; } public static Object newInstance(String constructorWithParams) throws ClassNotFoundException { int p = constructorWithParams.indexOf('('); int l = constructorWithParams.lastIndexOf(')'); String[] params = null; String classname; if (p != -1) { String tmp = constructorWithParams.substring(p + 1, l); params = StringUtil.split(tmp, ","); classname = constructorWithParams.substring(0, p); } else { classname = constructorWithParams; } Class clazz = Thread.currentThread().getContextClassLoader().loadClass(classname); Object result = newInstance(clazz, params); return result; } /** * Create new instance of clazz, call constructor with params as String. * Each param is converted to required type for * constructor with beanutils converter, first constructor that permit * instanciation is used * * @param type to instanciate * @param clazz class to instanciate * @param params parameters for constructor call * @return new instance of clazz * @throws IllegalArgumentException */ public static T newInstance(Class clazz, String... params) throws IllegalArgumentException { if (params == null) { params = StringUtil.EMPTY_STRING_ARRAY; } List> constructors = getConstructor(clazz, params.length); for (Constructor c : constructors) { try { Class[] types = c.getParameterTypes(); int last = types.length; if (c.isVarArgs()) { // on traite le dernier differement last--; } Object[] parameters = new Object[types.length]; for (int i = 0; i < last; i++) { String v = params[i]; Class argClazz = types[i]; Object t = convert(v, argClazz); parameters[i] = t; } if (c.isVarArgs()) { // get var args type Class argClazz = types[last]; // get array component type argClazz = argClazz.getComponentType(); List tmp = new ArrayList(); for (int i = last; i < params.length; i++) { String v = params[i]; Object t = convert(v, argClazz); tmp.add(t); } parameters[last] = tmp.toArray((Object[]) Array.newInstance( argClazz, tmp.size())); } if (log.isDebugEnabled()) { log.debug(_("nuitonutil.debug.objectutil.create", clazz, Arrays.toString(parameters))); } T result = c.newInstance(parameters); return result; } catch (Exception eee) { // this constructors don't work, try next if (log.isDebugEnabled()) { log.debug("Creation failed try with next constructor"); } } } throw new IllegalArgumentException( _("nuitonutil.debug.objectutil.instantiate", clazz, Arrays.toString(params))); } /** * Get all constructors that support paramNumber as parameters numbers. * Varargs is supported * * @param le type de la classe a inspecter * @param clazz la classe sur lequel rechercher le constructeur * @param paramNumber le nombre de parametre souhaite pour le constructeur, * -1 indique que tous les constructeur sont souhaite. * @return list of constructors */ @SuppressWarnings("unchecked") public static List> getConstructor(Class clazz, int paramNumber) { List> result = new ArrayList>(); Constructor[] constructors = (Constructor[]) clazz.getConstructors(); for (Constructor c : constructors) { if (paramNumber < 0 || c.isVarArgs() && c.getParameterTypes().length <= paramNumber - 1 || c.getParameterTypes().length == paramNumber) { result.add(c); } } return result; } /** * Method toObject * * @param o Object to transform * @return the same object * @deprecated since 2.6 (since java 5 boxing-unboxing does it for you) */ @Deprecated public static Object toObject(Object o) { return o; } /** * Method toObject *

    * transform a char to a Character Object * * @param c the char to transform * @return the Charactere object corresponding * @deprecated since 2.6 (since java 5 boxing-unboxing does it for you) */ @Deprecated public static Object toObject(char c) { return new Character(c); } /** * Method toObject *

    * transform a byte to a Byte Object * * @param b the byte to transform * @return the byte object corresponding * @deprecated since 2.6 (since java 5 boxing-unboxing does it for you) */ @Deprecated public static Object toObject(byte b) { return new Byte(b); } /** * Method toObject *

    * transform a short to a Short object * * @param s the short to transform * @return the Short object corresponding * @deprecated since 2.6 (since java 5 boxing-unboxing does it for you) */ @Deprecated public static Object toObject(short s) { return new Short(s); } /** * Method toObject *

    * transform an int to an Integer object * * @param i the int to transform * @return the Integer Object corresponding * @deprecated since 2.6 (since java 5 boxing-unboxing does it for you) */ @Deprecated public static Object toObject(int i) { return new Integer(i); } /** * Method toObject *

    * transform a long to a Long object * * @param l the long to transform * @return the Long Object corresponding * @deprecated since 2.6 (since java 5 boxing-unboxing does it for you) */ @Deprecated public static Object toObject(long l) { return new Long(l); } /** * Method toObject *

    * transform a float to a Float Object * * @param f the float to transform * @return the Float Object corresponding * @deprecated since 2.6 (since java 5 boxing-unboxing does it for you) */ @Deprecated public static Object toObject(float f) { return new Float(f); } /** * Method toObject *

    * transform a double to a Double object * * @param d the double to transform * @return the Double object corresponding * @deprecated since 2.6 (since java 5 boxing-unboxing does it for you) */ @Deprecated public static Object toObject(double d) { return new Double(d); } /** * Method toObject *

    * transform a boolean to a Boolean object * * @param b the boolean to transform * @return the Boolean object corresponding * @deprecated since 2.6 (since java 5 boxing-unboxing does it for you) */ @Deprecated public static Object toObject(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; } /** * Obtains the null value for the given type (works too with primitive * types). * * @param type the type to test * @return the {@code null} value or default value for primitive types * @see com.google.common.base.Defaults * @since 1.1.5 * @deprecated since 2.6 (there is some other library which does it */ @Deprecated public static Object getNullValue(Class type) { if (type == null) { throw new NullPointerException("parameter 'type' can not be null"); } if (type.isPrimitive()) { type = MethodUtils.getPrimitiveWrapper(type); if (Boolean.class.isAssignableFrom(type)) { return Boolean.FALSE; } if (Integer.class.isAssignableFrom(type)) { return ZERO; } if (Character.class.isAssignableFrom(type)) { return ZEROC; } if (Float.class.isAssignableFrom(type)) { return ZEROF; } if (Long.class.isAssignableFrom(type)) { return ZEROL; } if (Double.class.isAssignableFrom(type)) { return ZEROD; } if (Byte.class.isAssignableFrom(type)) { return ZEROB; } } return null; } /** * Tests if the given value is null according to default value for * primitive types if nedded. * * @param value the value to test * @return {@code true} if value is null or default value on a primitive * @since 1.1.5 */ public static boolean isNullValue(Object value) { if (value == null) { return true; } Class type = value.getClass(); //FIXME-TC20100212 : this case can not be, due to auto-boxing mecanism if (type.isPrimitive()) { type = MethodUtils.getPrimitiveWrapper(type); if (Boolean.class.isAssignableFrom(type)) { return Boolean.FALSE.equals(value); } if (Integer.class.isAssignableFrom(type)) { return ZERO.equals(value); } if (Character.class.isAssignableFrom(type)) { return ZEROC.equals(value); } if (Float.class.isAssignableFrom(type)) { return ZEROF.equals(value); } if (Long.class.isAssignableFrom(type)) { return ZEROL.equals(value); } if (Double.class.isAssignableFrom(type)) { return ZEROD.equals(value); } if (Byte.class.isAssignableFrom(type)) { return ZEROB.equals(value); } } return false; } public static boolean isNullValue(boolean value) { return Boolean.FALSE.equals(value); } public static boolean isNullValue(byte value) { return value == ZEROB; } public static boolean isNullValue(int value) { return value == ZEROB; } public static boolean isNullValue(char value) { return value == ZEROC; } public static boolean isNullValue(float value) { return value == ZEROF; } public static boolean isNullValue(double value) { return value == ZEROD; } /** * Verifie si la classe est de type primitif. * * @param clazz nom de la classe a tester * @return vrai si le classe est de type primitif faux sinon */ public static boolean isPrimitive(Class clazz) { return clazz.isPrimitive() || clazz == Boolean.class || clazz == Byte.class || clazz == Character.class || clazz == Short.class || clazz == Integer.class || clazz == Long.class || clazz == Float.class || clazz == Double.class; } } // ObjectUtil