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    }