##### Coverage Report - org.apache.commons.configuration.beanutils.BeanHelper

Classes in this File | Line Coverage | Branch Coverage | Complexity
BeanHelper |
91% | 60/66
100% | 14/14
| 3.357142857142857;3,357

1 |  |
     /*
2 |  |
      * Licensed to the Apache Software Foundation (ASF) under one or more
3 |  |
      * contributor license agreements.  See the NOTICE file distributed with
4 |  |
      * this work for additional information regarding copyright ownership.
5 |  |
      * The ASF licenses this file to You under the Apache License, Version 2.0
6 |  |
      * (the "License"); you may not use this file except in compliance with
7 |  |
      * the License.  You may obtain a copy of the License at
8 |  |
      *
9 |  |
      *     http://www.apache.org/licenses/LICENSE-2.0
10 |  |
      *
11 |  |
      * Unless required by applicable law or agreed to in writing, software
12 |  |
      * distributed under the License is distributed on an "AS IS" BASIS,
13 |  |
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 |  |
      * See the License for the specific language governing permissions and
15 |  |
      * limitations under the License.
16 |  |
      */
17 |  |
     package org.apache.commons.configuration.beanutils;
18 |  |
19 |  |
     import java.lang.reflect.InvocationTargetException;
20 |  |
     import java.util.Collections;
21 |  |
     import java.util.HashMap;
22 |  |
     import java.util.Iterator;
23 |  |
     import java.util.Map;
24 |  |
     import java.util.Set;
25 |  |
26 |  |
     import org.apache.commons.beanutils.BeanUtils;
27 |  |
     import org.apache.commons.beanutils.PropertyUtils;
28 |  |
     import org.apache.commons.configuration.ConfigurationRuntimeException;
29 |  |
     import org.apache.commons.lang.ClassUtils;
30 |  |
31 |  |
     /**
32 |  |
      * <p>
33 |  |
      * A helper class for creating bean instances that are defined in configuration
34 |  |
      * files.
35 |  |
      * </p>
36 |  |
      * <p>
37 |  |
      * This class provides static utility methods related to bean creation
38 |  |
      * operations. These methods simplify such operations because a client need not
39 |  |
      * deal with all involved interfaces. Usually, if a bean declaration has already
40 |  |
      * been obtained, a single method call is necessary to create a new bean
41 |  |
      * instance.
42 |  |
      * </p>
43 |  |
      * <p>
44 |  |
      * This class also supports the registration of custom bean factories.
45 |  |
      * Implementations of the <code>{@link BeanFactory}</code> interface can be
46 |  |
      * registered under a symbolic name using the <code>registerBeanFactory()</code>
47 |  |
      * method. In the configuration file the name of the bean factory can be
48 |  |
      * specified in the bean declaration. Then this factory will be used to create
49 |  |
      * the bean.
50 |  |
      * </p>
51 |  |
      *
52 |  |
      * @since 1.3
53 |  |
      * @author Oliver Heger
54 |  |
      * @version $Id: BeanHelper.java 570462 2007-08-28 15:56:49Z oheger $
55 |  |
      */
56 |  |
     public class BeanHelper
57 |  |
     {
58 |  |
         /** Stores a map with the registered bean factories. */
59 | 4 |
         private static Map beanFactories = Collections.synchronizedMap(new HashMap());
60 |  |
61 |  |
         /**
62 |  |
          * Stores the default bean factory, which will be used if no other factory
63 |  |
          * is provided.
64 |  |
          */
65 | 4 |
         private static BeanFactory defaultBeanFactory = DefaultBeanFactory.INSTANCE;
66 |  |
67 |  |
         /**
68 |  |
          * Private constructor, so no instances can be created.
69 |  |
          */
70 |  |
         private BeanHelper()
71 | 0 |
         {
72 | 0 |
         }
73 |  |
74 |  |
         /**
75 |  |
          * Register a bean factory under a symbolic name. This factory object can
76 |  |
          * then be specified in bean declarations with the effect that this factory
77 |  |
          * will be used to obtain an instance for the corresponding bean
78 |  |
          * declaration.
79 |  |
          *
80 |  |
          * @param name the name of the factory
81 |  |
          * @param factory the factory to be registered
82 |  |
          */
83 |  |
         public static void registerBeanFactory(String name, BeanFactory factory)
84 |  |
         {
85 | 13 |
             if (name == null)
86 |  |
             {
87 | 1 |
                 throw new IllegalArgumentException(
88 |  |
                         "Name for bean factory must not be null!");
89 |  |
             }
90 | 12 |
             if (factory == null)
91 |  |
             {
92 | 1 |
                 throw new IllegalArgumentException("Bean factory must not be null!");
93 |  |
             }
94 |  |
95 | 11 |
             beanFactories.put(name, factory);
96 | 11 |
         }
97 |  |
98 |  |
         /**
99 |  |
          * Deregisters the bean factory with the given name. After that this factory
100 |  |
          * cannot be used any longer.
101 |  |
          *
102 |  |
          * @param name the name of the factory to be deregistered
103 |  |
          * @return the factory that was registered under this name; <b>null</b> if
104 |  |
          * there was no such factory
105 |  |
          */
106 |  |
         public static BeanFactory deregisterBeanFactory(String name)
107 |  |
         {
108 | 11 |
             return (BeanFactory) beanFactories.remove(name);
109 |  |
         }
110 |  |
111 |  |
         /**
112 |  |
          * Returns a set with the names of all currently registered bean factories.
113 |  |
          *
114 |  |
          * @return a set with the names of the registered bean factories
115 |  |
          */
116 |  |
         public static Set registeredFactoryNames()
117 |  |
         {
118 | 122 |
             return beanFactories.keySet();
119 |  |
         }
120 |  |
121 |  |
         /**
122 |  |
          * Returns the default bean factory.
123 |  |
          *
124 |  |
          * @return the default bean factory
125 |  |
          */
126 |  |
         public static BeanFactory getDefaultBeanFactory()
127 |  |
         {
128 | 71 |
             return defaultBeanFactory;
129 |  |
         }
130 |  |
131 |  |
         /**
132 |  |
          * Sets the default bean factory. This factory will be used for all create
133 |  |
          * operations, for which no special factory is provided in the bean
134 |  |
          * declaration.
135 |  |
          *
136 |  |
          * @param factory the default bean factory (must not be <b>null</b>)
137 |  |
          */
138 |  |
         public static void setDefaultBeanFactory(BeanFactory factory)
139 |  |
         {
140 | 22 |
             if (factory == null)
141 |  |
             {
142 | 1 |
                 throw new IllegalArgumentException(
143 |  |
                         "Default bean factory must not be null!");
144 |  |
             }
145 | 21 |
             defaultBeanFactory = factory;
146 | 21 |
         }
147 |  |
148 |  |
         /**
149 |  |
          * Initializes the passed in bean. This method will obtain all the bean's
150 |  |
          * properties that are defined in the passed in bean declaration. These
151 |  |
          * properties will be set on the bean. If necessary, further beans will be
152 |  |
          * created recursively.
153 |  |
          *
154 |  |
          * @param bean the bean to be initialized
155 |  |
          * @param data the bean declaration
156 |  |
          * @throws ConfigurationRuntimeException if a property cannot be set
157 |  |
          */
158 |  |
         public static void initBean(Object bean, BeanDeclaration data)
159 |  |
                 throws ConfigurationRuntimeException
160 |  |
         {
161 | 114 |
             Map properties = data.getBeanProperties();
162 | 114 |
             if (properties != null)
163 |  |
             {
164 | 113 |
                 for (Iterator it = properties.keySet().iterator(); it.hasNext();)
165 |  |
                 {
166 | 107 |
                     String propName = (String) it.next();
167 | 107 |
                     initProperty(bean, propName, properties.get(propName));
168 | 106 |
                 }
169 |  |
             }
170 |  |
171 | 113 |
             Map nestedBeans = data.getNestedBeanDeclarations();
172 | 113 |
             if (nestedBeans != null)
173 |  |
             {
174 | 105 |
                 for (Iterator it = nestedBeans.keySet().iterator(); it.hasNext();)
175 |  |
                 {
176 | 18 |
                     String propName = (String) it.next();
177 | 18 |
                     initProperty(bean, propName, createBean(
178 |  |
                             (BeanDeclaration) nestedBeans.get(propName), null));
179 | 18 |
                 }
180 |  |
             }
181 | 113 |
         }
182 |  |
183 |  |
         /**
184 |  |
          * Sets a property on the given bean using Common Beanutils.
185 |  |
          *
186 |  |
          * @param bean the bean
187 |  |
          * @param propName the name of the property
188 |  |
          * @param value the property's value
189 |  |
          * @throws ConfigurationRuntimeException if the property is not writeable or
190 |  |
          * an error occurred
191 |  |
          */
192 |  |
         private static void initProperty(Object bean, String propName, Object value)
193 |  |
                 throws ConfigurationRuntimeException
194 |  |
         {
195 | 125 |
             if (!PropertyUtils.isWriteable(bean, propName))
196 |  |
             {
197 | 1 |
                 throw new ConfigurationRuntimeException("Property " + propName
198 |  |
                         + " cannot be set!");
199 |  |
             }
200 |  |
201 |  |
             try
202 |  |
             {
203 | 124 |
                 BeanUtils.setProperty(bean, propName, value);
204 |  |
             }
205 | 0 |
             catch (IllegalAccessException iaex)
206 |  |
             {
207 | 0 |
                 throw new ConfigurationRuntimeException(iaex);
208 |  |
             }
209 | 0 |
             catch (InvocationTargetException itex)
210 |  |
             {
211 | 0 |
                 throw new ConfigurationRuntimeException(itex);
212 | 124 |
             }
213 | 124 |
         }
214 |  |
215 |  |
         /**
216 |  |
          * The main method for creating and initializing beans from a configuration.
217 |  |
          * This method will return an initialized instance of the bean class
218 |  |
          * specified in the passed in bean declaration. If this declaration does not
219 |  |
          * contain the class of the bean, the passed in default class will be used.
220 |  |
          * From the bean declaration the factory to be used for creating the bean is
221 |  |
          * queried. The declaration may here return <b>null</b>, then a default
222 |  |
          * factory is used. This factory is then invoked to perform the create
223 |  |
          * operation.
224 |  |
          *
225 |  |
          * @param data the bean declaration
226 |  |
          * @param defaultClass the default class to use
227 |  |
          * @param param an additional parameter that will be passed to the bean
228 |  |
          * factory; some factories may support parameters and behave different
229 |  |
          * depending on the value passed in here
230 |  |
          * @return the new bean
231 |  |
          * @throws ConfigurationRuntimeException if an error occurs
232 |  |
          */
233 |  |
         public static Object createBean(BeanDeclaration data, Class defaultClass,
234 |  |
                 Object param) throws ConfigurationRuntimeException
235 |  |
         {
236 | 115 |
             if (data == null)
237 |  |
             {
238 | 1 |
                 throw new IllegalArgumentException(
239 |  |
                         "Bean declaration must not be null!");
240 |  |
             }
241 |  |
242 | 114 |
             BeanFactory factory = fetchBeanFactory(data);
243 |  |
             try
244 |  |
             {
245 | 113 |
                 return factory.createBean(fetchBeanClass(data, defaultClass,
246 |  |
                         factory), data, param);
247 |  |
             }
248 | 5 |
             catch (Exception ex)
249 |  |
             {
250 | 5 |
                 throw new ConfigurationRuntimeException(ex);
251 |  |
             }
252 |  |
         }
253 |  |
254 |  |
         /**
255 |  |
          * Returns a bean instance for the specified declaration. This method is a
256 |  |
          * short cut for <code>createBean(data, null, null);</code>.
257 |  |
          *
258 |  |
          * @param data the bean declaration
259 |  |
          * @param defaultClass the class to be used when in the declation no class
260 |  |
          * is specified
261 |  |
          * @return the new bean
262 |  |
          * @throws ConfigurationRuntimeException if an error occurs
263 |  |
          */
264 |  |
         public static Object createBean(BeanDeclaration data, Class defaultClass)
265 |  |
                 throws ConfigurationRuntimeException
266 |  |
         {
267 | 114 |
             return createBean(data, defaultClass, null);
268 |  |
         }
269 |  |
270 |  |
         /**
271 |  |
          * Returns a bean instance for the specified declaration. This method is a
272 |  |
          * short cut for <code>createBean(data, null);</code>.
273 |  |
          *
274 |  |
          * @param data the bean declaration
275 |  |
          * @return the new bean
276 |  |
          * @throws ConfigurationRuntimeException if an error occurs
277 |  |
          */
278 |  |
         public static Object createBean(BeanDeclaration data)
279 |  |
                 throws ConfigurationRuntimeException
280 |  |
         {
281 | 66 |
             return createBean(data, null);
282 |  |
         }
283 |  |
284 |  |
         /**
285 |  |
          * Returns a <code>java.lang.Class</code> object for the specified name.
286 |  |
          * This method and the helper method it invokes are very similar to code
287 |  |
          * extracted from the <code>ClassLoaderUtils</code> class of Commons
288 |  |
          * Jelly. It should be replaced if Commons Lang provides a generic version.
289 |  |
          *
290 |  |
          * @param name the name of the class to be loaded
291 |  |
          * @param callingClass the calling class
292 |  |
          * @return the class object for the specified name
293 |  |
          * @throws ClassNotFoundException if the class cannot be loaded
294 |  |
          */
295 |  |
         static Class loadClass(String name, Class callingClass)
296 |  |
                 throws ClassNotFoundException
297 |  |
         {
298 | 23 |
             return ClassUtils.getClass(name);
299 |  |
         }
300 |  |
301 |  |
         /**
302 |  |
          * Determines the class of the bean to be created. If the bean declaration
303 |  |
          * contains a class name, this class is used. Otherwise it is checked
304 |  |
          * whether a default class is provided. If this is not the case, the
305 |  |
          * factory's default class is used. If this class is undefined, too, an
306 |  |
          * exception is thrown.
307 |  |
          *
308 |  |
          * @param data the bean declaration
309 |  |
          * @param defaultClass the default class
310 |  |
          * @param factory the bean factory to use
311 |  |
          * @return the class of the bean to be created
312 |  |
          * @throws ConfigurationRuntimeException if the class cannot be determined
313 |  |
          */
314 |  |
         private static Class fetchBeanClass(BeanDeclaration data,
315 |  |
                 Class defaultClass, BeanFactory factory)
316 |  |
                 throws ConfigurationRuntimeException
317 |  |
         {
318 | 113 |
             String clsName = data.getBeanClassName();
319 | 113 |
             if (clsName != null)
320 |  |
             {
321 |  |
                 try
322 |  |
                 {
323 | 23 |
                     return loadClass(clsName, factory.getClass());
324 |  |
                 }
325 | 1 |
                 catch (ClassNotFoundException cex)
326 |  |
                 {
327 | 1 |
                     throw new ConfigurationRuntimeException(cex);
328 |  |
                 }
329 |  |
             }
330 |  |
331 | 90 |
             if (defaultClass != null)
332 |  |
             {
333 | 23 |
                 return defaultClass;
334 |  |
             }
335 |  |
336 | 67 |
             Class clazz = factory.getDefaultBeanClass();
337 | 67 |
             if (clazz == null)
338 |  |
             {
339 | 1 |
                 throw new ConfigurationRuntimeException(
340 |  |
                         "Bean class is not specified!");
341 |  |
             }
342 | 66 |
             return clazz;
343 |  |
         }
344 |  |
345 |  |
         /**
346 |  |
          * Obtains the bean factory to use for creating the specified bean. This
347 |  |
          * method will check whether a factory is specified in the bean declaration.
348 |  |
          * If this is not the case, the default bean factory will be used.
349 |  |
          *
350 |  |
          * @param data the bean declaration
351 |  |
          * @return the bean factory to use
352 |  |
          * @throws ConfigurationRuntimeException if the factory cannot be determined
353 |  |
          */
354 |  |
         private static BeanFactory fetchBeanFactory(BeanDeclaration data)
355 |  |
                 throws ConfigurationRuntimeException
356 |  |
         {
357 | 114 |
             String factoryName = data.getBeanFactoryName();
358 | 114 |
             if (factoryName != null)
359 |  |
             {
360 | 73 |
                 BeanFactory factory = (BeanFactory) beanFactories.get(factoryName);
361 | 73 |
                 if (factory == null)
362 |  |
                 {
363 | 1 |
                     throw new ConfigurationRuntimeException(
364 |  |
                             "Unknown bean factory: " + factoryName);
365 |  |
                 }
366 |  |
                 else
367 |  |
                 {
368 | 72 |
                     return factory;
369 |  |
                 }
370 |  |
             }
371 |  |
             else
372 |  |
             {
373 | 41 |
                 return getDefaultBeanFactory();
374 |  |
             }
375 |  |
         }
376 |  |
     }