001 /* 002 * Copyright 2005,2009 Ivan SZKIBA 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.ini4j.spi; 017 018 import java.beans.IntrospectionException; 019 import java.beans.Introspector; 020 import java.beans.PropertyDescriptor; 021 022 import java.io.File; 023 024 import java.lang.reflect.Array; 025 import java.lang.reflect.Method; 026 import java.lang.reflect.Proxy; 027 028 import java.net.URI; 029 import java.net.URL; 030 031 import java.util.TimeZone; 032 033 public class BeanTool 034 { 035 private static final String PARSE_METHOD = "valueOf"; 036 private static final BeanTool INSTANCE = ServiceFinder.findService(BeanTool.class); 037 038 public static final BeanTool getInstance() 039 { 040 return INSTANCE; 041 } 042 043 public void inject(Object bean, BeanAccess props) 044 { 045 for (PropertyDescriptor pd : getPropertyDescriptors(bean.getClass())) 046 { 047 try 048 { 049 Method method = pd.getWriteMethod(); 050 String name = pd.getName(); 051 052 if ((method != null) && (props.propLength(name) != 0)) 053 { 054 Object value; 055 056 if (pd.getPropertyType().isArray()) 057 { 058 value = Array.newInstance(pd.getPropertyType().getComponentType(), props.propLength(name)); 059 for (int i = 0; i < props.propLength(name); i++) 060 { 061 Array.set(value, i, parse(props.propGet(name, i), pd.getPropertyType().getComponentType())); 062 } 063 } 064 else 065 { 066 value = parse(props.propGet(name), pd.getPropertyType()); 067 } 068 069 method.invoke(bean, value); 070 } 071 } 072 catch (Exception x) 073 { 074 throw (IllegalArgumentException) (new IllegalArgumentException("Failed to set property: " + pd.getDisplayName()).initCause( 075 x)); 076 } 077 } 078 } 079 080 public void inject(BeanAccess props, Object bean) 081 { 082 for (PropertyDescriptor pd : getPropertyDescriptors(bean.getClass())) 083 { 084 try 085 { 086 Method method = pd.getReadMethod(); 087 088 if ((method != null) && !"class".equals(pd.getName())) 089 { 090 Object value = method.invoke(bean, (Object[]) null); 091 092 if (value != null) 093 { 094 if (pd.getPropertyType().isArray()) 095 { 096 for (int i = 0; i < Array.getLength(value); i++) 097 { 098 Object v = Array.get(value, i); 099 100 if ((v != null) && !v.getClass().equals(String.class)) 101 { 102 v = v.toString(); 103 } 104 105 props.propAdd(pd.getName(), (String) v); 106 } 107 } 108 else 109 { 110 props.propSet(pd.getName(), value.toString()); 111 } 112 } 113 } 114 } 115 catch (Exception x) 116 { 117 throw new IllegalArgumentException("Failed to set property: " + pd.getDisplayName(), x); 118 } 119 } 120 } 121 122 @SuppressWarnings("unchecked") 123 public <T> T parse(String value, Class<T> clazz) throws IllegalArgumentException 124 { 125 if (clazz == null) 126 { 127 throw new IllegalArgumentException("null argument"); 128 } 129 130 Object o = null; 131 132 if (value == null) 133 { 134 o = zero(clazz); 135 } 136 else if (clazz.isPrimitive()) 137 { 138 o = parsePrimitiveValue(value, clazz); 139 } 140 else 141 { 142 if (clazz == String.class) 143 { 144 o = value; 145 } 146 else if (clazz == Character.class) 147 { 148 o = new Character(value.charAt(0)); 149 } 150 else 151 { 152 o = parseSpecialValue(value, clazz); 153 } 154 } 155 156 return (T) o; 157 } 158 159 public <T> T proxy(Class<T> clazz, BeanAccess props) 160 { 161 return clazz.cast(Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] { clazz }, 162 new BeanInvocationHandler(props))); 163 } 164 165 @SuppressWarnings("unchecked") 166 public <T> T zero(Class<T> clazz) 167 { 168 Object o = null; 169 170 if (clazz.isPrimitive()) 171 { 172 if (clazz == Boolean.TYPE) 173 { 174 o = Boolean.FALSE; 175 } 176 else if (clazz == Byte.TYPE) 177 { 178 o = Byte.valueOf((byte) 0); 179 } 180 else if (clazz == Character.TYPE) 181 { 182 o = new Character('\0'); 183 } 184 else if (clazz == Double.TYPE) 185 { 186 o = new Double(0.0); 187 } 188 else if (clazz == Float.TYPE) 189 { 190 o = new Float(0.0f); 191 } 192 else if (clazz == Integer.TYPE) 193 { 194 o = Integer.valueOf(0); 195 } 196 else if (clazz == Long.TYPE) 197 { 198 o = Long.valueOf(0L); 199 } 200 else if (clazz == Short.TYPE) 201 { 202 o = Short.valueOf((short) 0); 203 } 204 } 205 206 return (T) o; 207 } 208 209 @SuppressWarnings(Warnings.UNCHECKED) 210 protected Object parseSpecialValue(String value, Class clazz) throws IllegalArgumentException 211 { 212 Object o; 213 214 try 215 { 216 if (clazz == File.class) 217 { 218 o = new File(value); 219 } 220 else if (clazz == URL.class) 221 { 222 o = new URL(value); 223 } 224 else if (clazz == URI.class) 225 { 226 o = new URI(value); 227 } 228 else if (clazz == Class.class) 229 { 230 o = Class.forName(value); 231 } 232 else if (clazz == TimeZone.class) 233 { 234 o = TimeZone.getTimeZone(value); 235 } 236 else 237 { 238 239 // TODO handle constructor with String arg as converter from String 240 // look for "valueOf" converter method 241 Method parser = clazz.getMethod(PARSE_METHOD, new Class[] { String.class }); 242 243 o = parser.invoke(null, new Object[] { value }); 244 } 245 } 246 catch (Exception x) 247 { 248 throw (IllegalArgumentException) new IllegalArgumentException().initCause(x); 249 } 250 251 return o; 252 } 253 254 private PropertyDescriptor[] getPropertyDescriptors(Class clazz) 255 { 256 try 257 { 258 return Introspector.getBeanInfo(clazz).getPropertyDescriptors(); 259 } 260 catch (IntrospectionException x) 261 { 262 throw new IllegalArgumentException(x); 263 } 264 } 265 266 private Object parsePrimitiveValue(String value, Class clazz) throws IllegalArgumentException 267 { 268 Object o = null; 269 270 try 271 { 272 if (clazz == Boolean.TYPE) 273 { 274 o = Boolean.valueOf(value); 275 } 276 else if (clazz == Byte.TYPE) 277 { 278 o = Byte.valueOf(value); 279 } 280 else if (clazz == Character.TYPE) 281 { 282 o = new Character(value.charAt(0)); 283 } 284 else if (clazz == Double.TYPE) 285 { 286 o = Double.valueOf(value); 287 } 288 else if (clazz == Float.TYPE) 289 { 290 o = Float.valueOf(value); 291 } 292 else if (clazz == Integer.TYPE) 293 { 294 o = Integer.valueOf(value); 295 } 296 else if (clazz == Long.TYPE) 297 { 298 o = Long.valueOf(value); 299 } 300 else if (clazz == Short.TYPE) 301 { 302 o = Short.valueOf(value); 303 } 304 } 305 catch (Exception x) 306 { 307 throw (IllegalArgumentException) new IllegalArgumentException().initCause(x); 308 } 309 310 return o; 311 } 312 313 static class BeanInvocationHandler extends AbstractBeanInvocationHandler 314 { 315 private final BeanAccess _backend; 316 317 BeanInvocationHandler(BeanAccess backend) 318 { 319 _backend = backend; 320 } 321 322 @Override protected Object getPropertySpi(String property, Class<?> clazz) 323 { 324 Object ret = null; 325 326 if (clazz.isArray()) 327 { 328 int length = _backend.propLength(property); 329 330 if (length != 0) 331 { 332 String[] all = new String[length]; 333 334 for (int i = 0; i < all.length; i++) 335 { 336 all[i] = _backend.propGet(property, i); 337 } 338 339 ret = all; 340 } 341 } 342 else 343 { 344 ret = _backend.propGet(property); 345 } 346 347 return ret; 348 } 349 350 @Override protected void setPropertySpi(String property, Object value, Class<?> clazz) 351 { 352 if (clazz.isArray()) 353 { 354 _backend.propDel(property); 355 for (int i = 0; i < Array.getLength(value); i++) 356 { 357 _backend.propAdd(property, Array.get(value, i).toString()); 358 } 359 } 360 else 361 { 362 _backend.propSet(property, value.toString()); 363 } 364 } 365 366 @Override protected boolean hasPropertySpi(String property) 367 { 368 return _backend.propLength(property) != 0; 369 } 370 } 371 }