/* =================================================================== * ClassUtils.java * * Created Jul 15, 2008 8:20:38 AM * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 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 Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA * =================================================================== * $Id$ * =================================================================== */ package net.solarnetwork.util; import java.beans.PropertyDescriptor; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanWrapper; import org.springframework.beans.PropertyAccessorFactory; import org.springframework.util.StringUtils; /** * Utility methods for dealing with classes at runtime. * * @author matt * @version $Revision$ $Date$ */ public final class ClassUtils { private static final Set<String> DEFAULT_BEAN_PROP_NAME_IGNORE = new HashSet<String>(Arrays.asList(new String[] {"class"})); private static final Logger LOG = LoggerFactory.getLogger(ClassUtils.class); /* Do not instantiate me. */ private ClassUtils() { super(); } /** * Instantiate a class of a specific interface type. * * @param <T> the desired interface type * @param className the class name that implements the interface * @param type the desired interface * @return new instance of the desired type */ public static <T> T instantiateClass(String className, Class<T> type) { Class<? extends T> clazz = loadClass(className, type); try { T o = clazz.newInstance(); return o; } catch (Exception e) { throw new RuntimeException("Unable to instantiate class [" +className +']', e); } } /** * Load a class of a particular type. * * <p>This uses the {@code type}'s ClassLoader to load the class. If that is * not available, it will use the current thread's context class loader.</p> * * @param <T> the desired interface type * @param className the class name that implements the interface * @param type the desired interface * @return the class */ public static <T> Class<? extends T> loadClass(String className, Class<T> type) { try { ClassLoader loader = type.getClassLoader(); if ( loader == null ) { loader = Thread.currentThread().getContextClassLoader(); } Class<?> clazz = loader.loadClass(className); if ( !type.isAssignableFrom(clazz) ) { throw new RuntimeException("Class [" +clazz +"] is not a [" +type +']'); } return clazz.asSubclass(type); } catch (ClassNotFoundException e) { throw new RuntimeException("Unable to load class [" +className +']', e); } } /** * Set bean property values on an object from a Map. * * @param o the bean to set JavaBean properties on * @param values a Map of JavaBean property names and their corresponding values to set */ public static void setBeanProperties(Object o, Map<String, ?> values) { BeanWrapper bean = PropertyAccessorFactory.forBeanPropertyAccess(o); bean.setPropertyValues(values); } /** * Get a Map of non-null bean properties for an object. * * @param o the object to inspect * @param ignore a set of property names to ignore (optional) * @return Map (never null) */ public static Map<String, Object> getBeanProperties(Object o, Set<String> ignore) { if ( ignore == null ) { ignore = DEFAULT_BEAN_PROP_NAME_IGNORE; } Map<String, Object> result = new LinkedHashMap<String, Object>(); BeanWrapper bean = PropertyAccessorFactory.forBeanPropertyAccess(o); PropertyDescriptor[] props = bean.getPropertyDescriptors(); for ( PropertyDescriptor prop : props ) { if ( prop.getReadMethod() == null ) { continue; } String propName = prop.getName(); if ( ignore != null && ignore.contains(propName) ) { continue; } Object propValue = bean.getPropertyValue(propName); if ( propValue == null ) { continue; } result.put(propName, propValue); } return result; } /** * Copy non-null bean properties from one object to another. * * @param src the object to copy values from * @param dest the object to copy values to * @param ignore a set of property names to ignore (optional) * where <em>null</em> */ public static void copyBeanProperties(Object src, Object dest, Set<String> ignore) { copyBeanProperties(src, dest, ignore, false); } /** * Copy non-null bean properties from one object to another. * * @param src the object to copy values from * @param dest the object to copy values to * @param ignore a set of property names to ignore (optional) * @param emptyStringToNull if <em>true</em> then String values that * are empty or contain only whitespace will be treated as if they * where <em>null</em> */ public static void copyBeanProperties(Object src, Object dest, Set<String> ignore, boolean emptyStringToNull) { if ( ignore == null ) { ignore = DEFAULT_BEAN_PROP_NAME_IGNORE; } BeanWrapper bean = PropertyAccessorFactory.forBeanPropertyAccess(src); BeanWrapper to = PropertyAccessorFactory.forBeanPropertyAccess(dest); PropertyDescriptor[] props = bean.getPropertyDescriptors(); for ( PropertyDescriptor prop : props ) { if ( prop.getReadMethod() == null ) { continue; } String propName = prop.getName(); if ( ignore != null && ignore.contains(propName) ) { continue; } Object propValue = bean.getPropertyValue(propName); if ( propValue == null || (emptyStringToNull && (propValue instanceof String) && !StringUtils.hasText((String)propValue)) ) { continue; } if ( to.isWritableProperty(propName) ) { to.setPropertyValue(propName, propValue); } } } /** * Get a Map of non-null bean properties for an object. * * @param o the object to inspect * @param ignore a set of property names to ignore (optional) * @param serializeIgnore if <em>true</em> test for the {@link SerializeIgnore} * annotation for ignoring properties * @return Map (never null) */ public static Map<String, Object> getBeanProperties(Object o, Set<String> ignore, boolean serializeIgnore) { if ( o == null ) { return null; } if ( ignore == null ) { ignore = DEFAULT_BEAN_PROP_NAME_IGNORE; } Map<String, Object> result = new LinkedHashMap<String, Object>(); BeanWrapper bean = PropertyAccessorFactory.forBeanPropertyAccess(o); PropertyDescriptor[] props = bean.getPropertyDescriptors(); for ( PropertyDescriptor prop : props ) { if ( prop.getReadMethod() == null ) { continue; } String propName = prop.getName(); if ( ignore != null && ignore.contains(propName) ) { continue; } Object propValue = bean.getPropertyValue(propName); if ( propValue == null ) { continue; } if ( serializeIgnore ) { Method getter = prop.getReadMethod(); if ( getter != null && getter.isAnnotationPresent(SerializeIgnore.class) ) { continue; } } result.put(propName, propValue); } return result; } /** * Load a classpath SQL resource into a String. * * @param resourceName the SQL resource to load * @param clazz the Class to load the resource from * @return the String */ public static String getResourceAsString(String resourceName, Class<?> clazz) { InputStream in = clazz.getResourceAsStream(resourceName); if ( in == null ) { throw new RuntimeException("Fesource [" +resourceName +"] not found"); } ByteArrayOutputStream out = new ByteArrayOutputStream(); try { byte[] buffer = new byte[4096]; int bytesRead = -1; while ((bytesRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); } out.flush(); return out.toString(); } catch ( IOException e ) { throw new RuntimeException("Error reading resource [" +resourceName +']', e); } finally { try { in.close(); } catch ( IOException ex ) { LOG.warn("Could not close InputStream", ex); } try { out.close(); } catch ( IOException ex ) { LOG.warn("Could not close OutputStream", ex); } } } }