View Javadoc

1   /*
2    * Copyright 2005,2009 Ivan SZKIBA
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.ini4j.spi;
17  
18  import java.beans.IntrospectionException;
19  import java.beans.Introspector;
20  import java.beans.PropertyDescriptor;
21  
22  import java.io.File;
23  
24  import java.lang.reflect.Array;
25  import java.lang.reflect.Method;
26  import java.lang.reflect.Proxy;
27  
28  import java.net.URI;
29  import java.net.URL;
30  
31  import java.util.TimeZone;
32  
33  public class BeanTool
34  {
35      private static final String PARSE_METHOD = "valueOf";
36      private static final BeanTool INSTANCE = ServiceFinder.findService(BeanTool.class);
37  
38      public static final BeanTool getInstance()
39      {
40          return INSTANCE;
41      }
42  
43      public void inject(Object bean, BeanAccess props)
44      {
45          for (PropertyDescriptor pd : getPropertyDescriptors(bean.getClass()))
46          {
47              try
48              {
49                  Method method = pd.getWriteMethod();
50                  String name = pd.getName();
51  
52                  if ((method != null) && (props.propLength(name) != 0))
53                  {
54                      Object value;
55  
56                      if (pd.getPropertyType().isArray())
57                      {
58                          value = Array.newInstance(pd.getPropertyType().getComponentType(), props.propLength(name));
59                          for (int i = 0; i < props.propLength(name); i++)
60                          {
61                              Array.set(value, i, parse(props.propGet(name, i), pd.getPropertyType().getComponentType()));
62                          }
63                      }
64                      else
65                      {
66                          value = parse(props.propGet(name), pd.getPropertyType());
67                      }
68  
69                      method.invoke(bean, value);
70                  }
71              }
72              catch (Exception x)
73              {
74                  throw (IllegalArgumentException) (new IllegalArgumentException("Failed to set property: " + pd.getDisplayName()).initCause(
75                          x));
76              }
77          }
78      }
79  
80      public void inject(BeanAccess props, Object bean)
81      {
82          for (PropertyDescriptor pd : getPropertyDescriptors(bean.getClass()))
83          {
84              try
85              {
86                  Method method = pd.getReadMethod();
87  
88                  if ((method != null) && !"class".equals(pd.getName()))
89                  {
90                      Object value = method.invoke(bean, (Object[]) null);
91  
92                      if (value != null)
93                      {
94                          if (pd.getPropertyType().isArray())
95                          {
96                              for (int i = 0; i < Array.getLength(value); i++)
97                              {
98                                  Object v = Array.get(value, i);
99  
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 }