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 */ 016package org.ini4j.spi; 017 018import java.beans.IntrospectionException; 019import java.beans.Introspector; 020import java.beans.PropertyDescriptor; 021 022import java.io.File; 023 024import java.lang.reflect.Array; 025import java.lang.reflect.Method; 026import java.lang.reflect.Proxy; 027 028import java.net.URI; 029import java.net.URL; 030 031import java.util.TimeZone; 032 033public 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 new IllegalArgumentException("Failed to set property: " + pd.getDisplayName(), x); 075 } 076 } 077 } 078 079 public void inject(BeanAccess props, Object bean) 080 { 081 for (PropertyDescriptor pd : getPropertyDescriptors(bean.getClass())) 082 { 083 try 084 { 085 Method method = pd.getReadMethod(); 086 087 if ((method != null) && !"class".equals(pd.getName())) 088 { 089 Object value = method.invoke(bean, (Object[]) null); 090 091 if (value != null) 092 { 093 if (pd.getPropertyType().isArray()) 094 { 095 for (int i = 0; i < Array.getLength(value); i++) 096 { 097 Object v = Array.get(value, i); 098 099 if ((v != null) && !v.getClass().equals(String.class)) 100 { 101 v = v.toString(); 102 } 103 104 props.propAdd(pd.getName(), (String) v); 105 } 106 } 107 else 108 { 109 props.propSet(pd.getName(), value.toString()); 110 } 111 } 112 } 113 } 114 catch (Exception x) 115 { 116 throw new IllegalArgumentException("Failed to set property: " + pd.getDisplayName(), x); 117 } 118 } 119 } 120 121 @SuppressWarnings("unchecked") 122 public <T> T parse(String value, Class<T> 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 (T) 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 }, new BeanInvocationHandler(props))); 161 } 162 163 @SuppressWarnings("unchecked") 164 public <T> T zero(Class<T> 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 (T) 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 // TODO handle constructor with String arg as converter from String 237 238 // look for "valueOf" converter method 239 Method parser = clazz.getMethod(PARSE_METHOD, new Class[] { String.class }); 240 241 o = parser.invoke(null, new Object[] { value }); 242 } 243 } 244 catch (Exception x) 245 { 246 throw (IllegalArgumentException) new IllegalArgumentException().initCause(x); 247 } 248 249 return o; 250 } 251 252 private PropertyDescriptor[] getPropertyDescriptors(Class clazz) 253 { 254 try 255 { 256 return Introspector.getBeanInfo(clazz).getPropertyDescriptors(); 257 } 258 catch (IntrospectionException x) 259 { 260 throw new IllegalArgumentException(x); 261 } 262 } 263 264 private Object parsePrimitiveValue(String value, Class clazz) throws IllegalArgumentException 265 { 266 Object o = null; 267 268 try 269 { 270 if (clazz == Boolean.TYPE) 271 { 272 o = Boolean.valueOf(value); 273 } 274 else if (clazz == Byte.TYPE) 275 { 276 o = Byte.valueOf(value); 277 } 278 else if (clazz == Character.TYPE) 279 { 280 o = new Character(value.charAt(0)); 281 } 282 else if (clazz == Double.TYPE) 283 { 284 o = Double.valueOf(value); 285 } 286 else if (clazz == Float.TYPE) 287 { 288 o = Float.valueOf(value); 289 } 290 else if (clazz == Integer.TYPE) 291 { 292 o = Integer.valueOf(value); 293 } 294 else if (clazz == Long.TYPE) 295 { 296 o = Long.valueOf(value); 297 } 298 else if (clazz == Short.TYPE) 299 { 300 o = Short.valueOf(value); 301 } 302 } 303 catch (Exception x) 304 { 305 throw (IllegalArgumentException) new IllegalArgumentException().initCause(x); 306 } 307 308 return o; 309 } 310 311 static class BeanInvocationHandler extends AbstractBeanInvocationHandler 312 { 313 private final BeanAccess _backend; 314 315 BeanInvocationHandler(BeanAccess backend) 316 { 317 _backend = backend; 318 } 319 320 @Override protected Object getPropertySpi(String property, Class<?> clazz) 321 { 322 Object ret = null; 323 324 if (clazz.isArray()) 325 { 326 int length = _backend.propLength(property); 327 328 if (length != 0) 329 { 330 String[] all = new String[length]; 331 332 for (int i = 0; i < all.length; i++) 333 { 334 all[i] = _backend.propGet(property, i); 335 } 336 337 ret = all; 338 } 339 } 340 else 341 { 342 ret = _backend.propGet(property); 343 } 344 345 return ret; 346 } 347 348 @Override protected void setPropertySpi(String property, Object value, Class<?> clazz) 349 { 350 if (clazz.isArray()) 351 { 352 _backend.propDel(property); 353 for (int i = 0; i < Array.getLength(value); i++) 354 { 355 _backend.propAdd(property, Array.get(value, i).toString()); 356 } 357 } 358 else 359 { 360 _backend.propSet(property, value.toString()); 361 } 362 } 363 364 @Override protected boolean hasPropertySpi(String property) 365 { 366 return _backend.propLength(property) != 0; 367 } 368 } 369}