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 protected 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.containsKey(name)) 053 if ((method != null) && (props.propLength(name) != 0)) 054 { 055 Object value; 056 057 if (pd.getPropertyType().isArray()) 058 { 059 value = Array.newInstance(pd.getPropertyType().getComponentType(), props.propLength(name)); 060 for (int i = 0; i < props.propLength(name); i++) 061 { 062 Array.set(value, i, parse(props.propGet(name, i), pd.getPropertyType().getComponentType())); 063 } 064 } 065 else 066 { 067 value = parse(props.propGet(name), pd.getPropertyType()); 068 } 069 070 method.invoke(bean, value); 071 } 072 } 073 catch (Exception x) 074 { 075 throw new IllegalArgumentException("Failed to set property: " + pd.getDisplayName(), 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 public Object parse(String value, Class clazz) throws IllegalArgumentException 123 { 124 if (clazz == null) 125 { 126 throw new IllegalArgumentException("null argument"); 127 } 128 129 Object o = null; 130 131 if (value == null) 132 { 133 o = zero(clazz); 134 } 135 else if (clazz.isPrimitive()) 136 { 137 o = parsePrimitiveValue(value, clazz); 138 } 139 else 140 { 141 if (clazz == String.class) 142 { 143 o = value; 144 } 145 else if (clazz == Character.class) 146 { 147 o = new Character(value.charAt(0)); 148 } 149 else 150 { 151 o = parseSpecialValue(value, clazz); 152 } 153 } 154 155 return o; 156 } 157 158 public <T> T proxy(Class<T> clazz, BeanAccess props) 159 { 160 return clazz.cast(Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] { clazz }, 161 new BeanInvocationHandler(props))); 162 } 163 164 public Object zero(Class clazz) 165 { 166 Object o = null; 167 168 if (clazz.isPrimitive()) 169 { 170 if (clazz == Boolean.TYPE) 171 { 172 o = Boolean.FALSE; 173 } 174 else if (clazz == Byte.TYPE) 175 { 176 o = Byte.valueOf((byte) 0); 177 } 178 else if (clazz == Character.TYPE) 179 { 180 o = new Character('\0'); 181 } 182 else if (clazz == Double.TYPE) 183 { 184 o = new Double(0.0); 185 } 186 else if (clazz == Float.TYPE) 187 { 188 o = new Float(0.0f); 189 } 190 else if (clazz == Integer.TYPE) 191 { 192 o = Integer.valueOf(0); 193 } 194 else if (clazz == Long.TYPE) 195 { 196 o = Long.valueOf(0L); 197 } 198 else if (clazz == Short.TYPE) 199 { 200 o = Short.valueOf((short) 0); 201 } 202 } 203 204 return o; 205 } 206 207 @SuppressWarnings(Warnings.UNCHECKED) 208 protected Object parseSpecialValue(String value, Class clazz) throws IllegalArgumentException 209 { 210 Object o; 211 212 try 213 { 214 if (clazz == File.class) 215 { 216 o = new File(value); 217 } 218 else if (clazz == URL.class) 219 { 220 o = new URL(value); 221 } 222 else if (clazz == URI.class) 223 { 224 o = new URI(value); 225 } 226 else if (clazz == Class.class) 227 { 228 o = Class.forName(value); 229 } 230 else if (clazz == TimeZone.class) 231 { 232 o = TimeZone.getTimeZone(value); 233 } 234 else 235 { 236 237 // look for "valueOf" converter method 238 Method parser = clazz.getMethod(PARSE_METHOD, new Class[] { String.class }); 239 240 o = parser.invoke(null, new Object[] { value }); 241 } 242 } 243 catch (Exception x) 244 { 245 throw (IllegalArgumentException) new IllegalArgumentException().initCause(x); 246 } 247 248 return o; 249 } 250 251 private PropertyDescriptor[] getPropertyDescriptors(Class clazz) 252 { 253 try 254 { 255 return Introspector.getBeanInfo(clazz).getPropertyDescriptors(); 256 } 257 catch (IntrospectionException x) 258 { 259 throw new IllegalArgumentException(x); 260 } 261 } 262 263 private Object parsePrimitiveValue(String value, Class clazz) throws IllegalArgumentException 264 { 265 Object o = null; 266 267 try 268 { 269 if (clazz == Boolean.TYPE) 270 { 271 o = Boolean.valueOf(value); 272 } 273 else if (clazz == Byte.TYPE) 274 { 275 o = Byte.valueOf(value); 276 } 277 else if (clazz == Character.TYPE) 278 { 279 o = new Character(value.charAt(0)); 280 } 281 else if (clazz == Double.TYPE) 282 { 283 o = Double.valueOf(value); 284 } 285 else if (clazz == Float.TYPE) 286 { 287 o = Float.valueOf(value); 288 } 289 else if (clazz == Integer.TYPE) 290 { 291 o = Integer.valueOf(value); 292 } 293 else if (clazz == Long.TYPE) 294 { 295 o = Long.valueOf(value); 296 } 297 else if (clazz == Short.TYPE) 298 { 299 o = Short.valueOf(value); 300 } 301 } 302 catch (Exception x) 303 { 304 throw (IllegalArgumentException) new IllegalArgumentException().initCause(x); 305 } 306 307 return o; 308 } 309 310 static class BeanInvocationHandler extends AbstractBeanInvocationHandler 311 { 312 private final BeanAccess _backend; 313 314 BeanInvocationHandler(BeanAccess backend) 315 { 316 _backend = backend; 317 } 318 319 @Override protected Object getPropertySpi(String property, Class<?> clazz) 320 { 321 Object ret = null; 322 323 if (clazz.isArray()) 324 { 325 int length = _backend.propLength(property); 326 327 if (length != 0) 328 { 329 String[] all = new String[length]; 330 331 for (int i = 0; i < all.length; i++) 332 { 333 all[i] = _backend.propGet(property, i); 334 } 335 336 ret = all; 337 } 338 } 339 else 340 { 341 ret = _backend.propGet(property); 342 } 343 344 return ret; 345 } 346 347 @Override protected void setPropertySpi(String property, Object value, Class<?> clazz) 348 { 349 if (clazz.isArray()) 350 { 351 _backend.propDel(property); 352 for (int i = 0; i < Array.getLength(value); i++) 353 { 354 _backend.propAdd(property, Array.get(value, i).toString()); 355 } 356 } 357 else 358 { 359 _backend.propSet(property, value.toString()); 360 } 361 } 362 363 @Override protected boolean hasPropertySpi(String property) 364 { 365 return _backend.propLength(property) != 0; 366 } 367 } 368 }