001package org.javasimon.utils.bean; 002 003import org.slf4j.Logger; 004import org.slf4j.LoggerFactory; 005 006import java.lang.reflect.Field; 007import java.lang.reflect.InvocationTargetException; 008import java.lang.reflect.Method; 009import java.util.Map; 010import java.util.Set; 011import java.util.concurrent.ConcurrentHashMap; 012 013/** 014 * Utils for setting properties values in Java bean object. It can convert values that should be set from String 015 * to a type of specified properties. 016 * 017 * @author <a href="mailto:ivan.mushketyk@gmail.com">Ivan Mushketyk</a> 018 */ 019public class SimonBeanUtils { 020 private static final Logger logger = LoggerFactory.getLogger(SimonBeanUtils.class); 021 022 private static final SimonBeanUtils INSTANCE = new SimonBeanUtils(); 023 024 private final Map<Class<?>, Converter> converters = new ConcurrentHashMap<>(); 025 026 public SimonBeanUtils() { 027 converters.put(String.class, new ToStringConverter()); 028 029 converters.put(boolean.class, new ToBooleanConverter()); 030 converters.put(Boolean.class, new ToBooleanConverter()); 031 032 converters.put(byte.class, new ToByteConverter()); 033 converters.put(Byte.class, new ToByteConverter()); 034 035 converters.put(short.class, new ToShortConverter()); 036 converters.put(Short.class, new ToShortConverter()); 037 038 converters.put(int.class, new ToIntegerConverter()); 039 converters.put(Integer.class, new ToIntegerConverter()); 040 041 converters.put(long.class, new ToLongConverter()); 042 converters.put(Long.class, new ToLongConverter()); 043 044 converters.put(float.class, new ToFloatConverter()); 045 converters.put(Float.class, new ToFloatConverter()); 046 047 converters.put(double.class, new ToDoubleConverter()); 048 converters.put(Double.class, new ToDoubleConverter()); 049 050 converters.put(char.class, new ToCharConverter()); 051 converters.put(Character.class, new ToCharConverter()); 052 053 } 054 055 public static SimonBeanUtils getInstance() { 056 return INSTANCE; 057 } 058 059 /** 060 * Set property in object target. If values has type other than String setter method or field 061 * with specified type will be used to set value. If value has String type value will be converted 062 * using available converters. If conversion to all of the types accepted by setters fails, field 063 * with corresponding name will be used 064 * 065 * @param target Java bean where a property will be set 066 * @param property property to be set 067 * @param value value to be set 068 */ 069 public void setProperty(Object target, String property, Object value) { 070 NestedResolver resolver = new NestedResolver(target, property); 071 072 if (value instanceof String) { 073 convertStringValue(resolver.getNestedTarget(), resolver.getProperty(), (String) value); 074 } else { 075 setObjectValue(resolver.getNestedTarget(), resolver.getProperty(), value); 076 } 077 } 078 079 private void convertStringValue(Object target, String property, String strVal) { 080 Set<Method> potentialSetters = ClassUtils.getSetters(target.getClass(), property); 081 boolean converted = false; 082 083 for (Method setter : potentialSetters) { 084 try { 085 Class<?> setterType = ClassUtils.getSetterType(setter); 086 Converter converter = getConverterTo(setterType); 087 if (converter != null) { 088 Object value = converter.convert(setterType, strVal); 089 invokeSetter(target, setter, value); 090 converted = true; 091 break; 092 } else { 093 logger.debug("Failed to find converter for method '{}'", setter); 094 } 095 } catch (ConvertException ex) { 096 logger.debug("Failed to convert value '{}' for method '{}'", strVal, setter); 097 } 098 } 099 100 if (!converted) { 101 Field field = ClassUtils.getField(target.getClass(), property); 102 if (field != null) { 103 Class<?> fieldType = field.getType(); 104 Converter converter = getConverterTo(fieldType); 105 if (converter != null) { 106 Object value = converter.convert(fieldType, strVal); 107 setValueUsingField(field, target, value); 108 return; 109 } 110 } 111 throw new BeanUtilsException( 112 String.format("Failed to find find setter/field for property '%s' and value '%s'", property, strVal)); 113 } 114 } 115 116 private Converter getConverterTo(Class<?> setterType) { 117 return converters.get(setterType); 118 } 119 120 private void setObjectValue(Object target, String property, Object value) { 121 Method setter = ClassUtils.getSetter(target.getClass(), property, value.getClass()); 122 if (setter != null) { 123 invokeSetter(target, setter, value); 124 } else { 125 Field field = ClassUtils.getField(target.getClass(), property); 126 if (field != null) { 127 setValueUsingField(field, target, value); 128 } else { 129 throw new BeanUtilsException("Failed to find field/setter for property " + property); 130 } 131 } 132 } 133 134 private void invokeSetter(Object target, Method setter, Object value) { 135 try { 136 setter.setAccessible(true); 137 setter.invoke(target, value); 138 } catch (IllegalAccessException | InvocationTargetException e) { 139 throw new BeanUtilsException(e); 140 } 141 } 142 143 private void setValueUsingField(Field field, Object target, Object value) { 144 try { 145 field.setAccessible(true); 146 field.set(target, value); 147 } catch (SecurityException | IllegalAccessException ex) { 148 throw new BeanUtilsException(ex); 149 } 150 } 151 152 /** 153 * Register converter for a specified class. 154 * 155 * @param targetClass property type for which a converter will be used 156 * @param converter converter that will be used to convert String values for a specified target class 157 */ 158 public void registerConverter(Class<?> targetClass, Converter converter) { 159 converters.put(targetClass, converter); 160 } 161}