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

Classes in this File | Line Coverage | Branch Coverage | Complexity
XMLBeanDeclaration |
100% | 47/47
100% | 9/9
| 1.9285714285714286;1,929

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.util.HashMap;
20 |  |
     import java.util.Iterator;
21 |  |
     import java.util.Map;
22 |  |
23 |  |
     import org.apache.commons.configuration.HierarchicalConfiguration;
24 |  |
     import org.apache.commons.configuration.PropertyConverter;
25 |  |
     import org.apache.commons.configuration.SubnodeConfiguration;
26 |  |
     import org.apache.commons.configuration.tree.ConfigurationNode;
27 |  |
     import org.apache.commons.configuration.tree.DefaultConfigurationNode;
28 |  |
29 |  |
     /**
30 |  |
      * <p>
31 |  |
      * An implementation of the <code>BeanDeclaration</code> interface that is
32 |  |
      * suitable for XML configuration files.
33 |  |
      * </p>
34 |  |
      * <p>
35 |  |
      * This class defines the standard layout of a bean declaration in an XML
36 |  |
      * configuration file. Such a declaration must look like the following example
37 |  |
      * fragement:
38 |  |
      * </p>
39 |  |
      * <p>
40 |  |
      *
41 |  |
      * <pre>
42 |  |
      *   ...
43 |  |
      *   &lt;personBean config-class=&quot;my.model.PersonBean&quot;
44 |  |
      *       lastName=&quot;Doe&quot; firstName=&quot;John&quot;&gt;
45 |  |
      *       &lt;address config-class=&quot;my.model.AddressBean&quot;
46 |  |
      *           street=&quot;21st street 11&quot; zip=&quot;1234&quot;
47 |  |
      *           city=&quot;TestCity&quot;/&gt;
48 |  |
      *   &lt;/personBean&gt;
49 |  |
      * </pre>
50 |  |
      *
51 |  |
      * </p>
52 |  |
      * <p>
53 |  |
      * The bean declaration can be contained in an arbitrary element. Here it is the
54 |  |
      * <code>&lt;personBean&gt;</code> element. In the attributes of this element
55 |  |
      * there can occur some reserved attributes, which have the following meaning:
56 |  |
      * <dl>
57 |  |
      * <dt><code>config-class</code></dt>
58 |  |
      * <dd>Here the full qualified name of the bean's class can be specified. An
59 |  |
      * instance of this class will be created. If this attribute is not specified,
60 |  |
      * the bean class must be provided in another way, e.g. as the
61 |  |
      * <code>defaultClass</code> passed to the <code>BeanHelper</code> class.</dd>
62 |  |
      * <dt><code>config-factory</code></dt>
63 |  |
      * <dd>This attribute can contain the name of the
64 |  |
      * <code>{@link BeanFactory}</code> that should be used for creating the bean.
65 |  |
      * If it is defined, a factory with this name must have been registered at the
66 |  |
      * <code>BeanHelper</code> class. If this attribute is missing, the default
67 |  |
      * bean factory will be used.</dd>
68 |  |
      * <dt><code>config-factoryParam</code></dt>
69 |  |
      * <dd>With this attribute a parameter can be specified that will be passed to
70 |  |
      * the bean factory. This may be useful for custom bean factories.</dd>
71 |  |
      * </dl>
72 |  |
      * </p>
73 |  |
      * <p>
74 |  |
      * All further attributes starting with the <code>config-</code> prefix are
75 |  |
      * considered as meta data and will be ignored. All other attributes are treated
76 |  |
      * as properties of the bean to be created, i.e. corresponding setter methods of
77 |  |
      * the bean will be invoked with the values specified here.
78 |  |
      * </p>
79 |  |
      * <p>
80 |  |
      * If the bean to be created has also some complex properties (which are itself
81 |  |
      * beans), their values cannot be initialized from attributes. For this purpose
82 |  |
      * nested elements can be used. The example listing shows how an address bean
83 |  |
      * can be initialized. This is done in a nested element whose name must match
84 |  |
      * the name of a property of the enclosing bean declaration. The format of this
85 |  |
      * nested element is exactly the same as for the bean declaration itself, i.e.
86 |  |
      * it can have attributes defining meta data or bean properties and even further
87 |  |
      * nested elements for complex bean properties.
88 |  |
      * </p>
89 |  |
      * <p>
90 |  |
      * A <code>XMLBeanDeclaration</code> object is usually created from a
91 |  |
      * <code>HierarchicalConfiguration</code>. From this it will derive a
92 |  |
      * <code>SubnodeConfiguration</code>, which is used to access the needed
93 |  |
      * properties. This subnode configuration can be obtained using the
94 |  |
      * <code>{@link #getConfiguration()}</code> method. All of its properties can
95 |  |
      * be accessed in the usual way. To ensure that the property keys used by this
96 |  |
      * class are understood by the configuration, the default expression engine will
97 |  |
      * be set.
98 |  |
      * </p>
99 |  |
      *
100 |  |
      * @since 1.3
101 |  |
      * @author Oliver Heger
102 |  |
      * @version $Id: XMLBeanDeclaration.java 439648 2006-09-02 20:42:10Z oheger $
103 |  |
      */
104 |  |
     public class XMLBeanDeclaration implements BeanDeclaration
105 |  |
     {
106 |  |
         /** Constant for the prefix of reserved attributes. */
107 |  |
         public static final String RESERVED_PREFIX = "config-";
108 |  |
109 |  |
         /** Constant for the prefix for reserved attributes.*/
110 |  |
         public static final String ATTR_PREFIX = "[@" + RESERVED_PREFIX;
111 |  |
112 |  |
         /** Constant for the bean class attribute. */
113 |  |
         public static final String ATTR_BEAN_CLASS = ATTR_PREFIX + "class]";
114 |  |
115 |  |
         /** Constant for the bean factory attribute. */
116 |  |
         public static final String ATTR_BEAN_FACTORY = ATTR_PREFIX + "factory]";
117 |  |
118 |  |
         /** Constant for the bean factory parameter attribute. */
119 |  |
         public static final String ATTR_FACTORY_PARAM = ATTR_PREFIX
120 |  |
                 + "factoryParam]";
121 |  |
122 |  |
         /** Stores the associated configuration. */
123 |  |
         private SubnodeConfiguration configuration;
124 |  |
125 |  |
         /** Stores the configuration node that contains the bean declaration. */
126 |  |
         private ConfigurationNode node;
127 |  |
128 |  |
         /**
129 |  |
          * Creates a new instance of <code>XMLBeanDeclaration</code> and
130 |  |
          * initializes it from the given configuration. The passed in key points to
131 |  |
          * the bean declaration.
132 |  |
          *
133 |  |
          * @param config the configuration
134 |  |
          * @param key the key to the bean declaration (this key must point to
135 |  |
          * exactly one bean declaration or a <code>IllegalArgumentException</code>
136 |  |
          * exception will be thrown)
137 |  |
          */
138 |  |
         public XMLBeanDeclaration(HierarchicalConfiguration config, String key)
139 |  |
         {
140 | 79 |
             this(config, key, false);
141 | 75 |
         }
142 |  |
143 |  |
         /**
144 |  |
          * Creates a new instance of <code>XMLBeanDeclaration</code> and
145 |  |
          * initializes it from the given configuration. The passed in key points to
146 |  |
          * the bean declaration. If the key does not exist and the boolean argument
147 |  |
          * is <b>true</b>, the declaration is initialized with an empty
148 |  |
          * configuration. It is possible to create objects from such an empty
149 |  |
          * declaration if a default class is provided. If the key on the other hand
150 |  |
          * has multiple values or is undefined and the boolean argument is <b>false</b>,
151 |  |
          * a <code>IllegalArgumentException</code> exception will be thrown.
152 |  |
          *
153 |  |
          * @param config the configuration
154 |  |
          * @param key the key to the bean declaration
155 |  |
          * @param optional a flag whether this declaration is optional; if set to
156 |  |
          * <b>true</b>, no exception will be thrown if the passed in key is
157 |  |
          * undefined
158 |  |
          */
159 |  |
         public XMLBeanDeclaration(HierarchicalConfiguration config, String key,
160 |  |
                 boolean optional)
161 | 97 |
         {
162 | 97 |
             if (config == null)
163 |  |
             {
164 | 2 |
                 throw new IllegalArgumentException(
165 |  |
                         "Configuration must not be null!");
166 |  |
             }
167 |  |
168 |  |
             try
169 |  |
             {
170 | 95 |
                 configuration = config.configurationAt(key);
171 | 78 |
                 node = configuration.getRootNode();
172 | 78 |
             }
173 |  |
             catch (IllegalArgumentException iex)
174 |  |
             {
175 |  |
                 // If we reach this block, the key does not have exactly one value
176 | 17 |
                 if (!optional || config.getMaxIndex(key) > 0)
177 |  |
                 {
178 | 2 |
                     throw iex;
179 |  |
                 }
180 | 15 |
                 configuration = config.configurationAt(null);
181 | 15 |
                 node = new DefaultConfigurationNode();
182 |  |
             }
183 | 93 |
             initSubnodeConfiguration(getConfiguration());
184 | 93 |
         }
185 |  |
186 |  |
         /**
187 |  |
          * Creates a new instance of <code>XMLBeanDeclaration</code> and
188 |  |
          * initializes it from the given configuration. The configuration's root
189 |  |
          * node must contain the bean declaration.
190 |  |
          *
191 |  |
          * @param config the configuration with the bean declaration
192 |  |
          */
193 |  |
         public XMLBeanDeclaration(HierarchicalConfiguration config)
194 |  |
         {
195 | 68 |
             this(config, (String) null);
196 | 67 |
         }
197 |  |
198 |  |
         /**
199 |  |
          * Creates a new instance of <code>XMLBeanDeclaration</code> and
200 |  |
          * initializes it with the configuration node that contains the bean
201 |  |
          * declaration.
202 |  |
          *
203 |  |
          * @param config the configuration
204 |  |
          * @param node the node with the bean declaration.
205 |  |
          */
206 |  |
         public XMLBeanDeclaration(SubnodeConfiguration config,
207 |  |
                 ConfigurationNode node)
208 | 16 |
         {
209 | 16 |
             if (config == null)
210 |  |
             {
211 | 1 |
                 throw new IllegalArgumentException(
212 |  |
                         "Configuration must not be null!");
213 |  |
             }
214 | 15 |
             if (node == null)
215 |  |
             {
216 | 1 |
                 throw new IllegalArgumentException("Node must not be null!");
217 |  |
             }
218 |  |
219 | 14 |
             this.node = node;
220 | 14 |
             configuration = config;
221 | 14 |
             initSubnodeConfiguration(config);
222 | 14 |
         }
223 |  |
224 |  |
         /**
225 |  |
          * Returns the configuration object this bean declaration is based on.
226 |  |
          *
227 |  |
          * @return the associated configuration
228 |  |
          */
229 |  |
         public SubnodeConfiguration getConfiguration()
230 |  |
         {
231 | 416 |
             return configuration;
232 |  |
         }
233 |  |
234 |  |
         /**
235 |  |
          * Returns the node that contains the bean declaration.
236 |  |
          *
237 |  |
          * @return the configuration node this bean declaration is based on
238 |  |
          */
239 |  |
         public ConfigurationNode getNode()
240 |  |
         {
241 | 242 |
             return node;
242 |  |
         }
243 |  |
244 |  |
         /**
245 |  |
          * Returns the name of the bean factory. This information is fetched from
246 |  |
          * the <code>config-factory</code> attribute.
247 |  |
          *
248 |  |
          * @return the name of the bean factory
249 |  |
          */
250 |  |
         public String getBeanFactoryName()
251 |  |
         {
252 | 31 |
             return getConfiguration().getString(ATTR_BEAN_FACTORY);
253 |  |
         }
254 |  |
255 |  |
         /**
256 |  |
          * Returns a parameter for the bean factory. This information is fetched
257 |  |
          * from the <code>config-factoryParam</code> attribute.
258 |  |
          *
259 |  |
          * @return the parameter for the bean factory
260 |  |
          */
261 |  |
         public Object getBeanFactoryParameter()
262 |  |
         {
263 | 2 |
             return getConfiguration().getProperty(ATTR_FACTORY_PARAM);
264 |  |
         }
265 |  |
266 |  |
         /**
267 |  |
          * Returns the name of the class of the bean to be created. This information
268 |  |
          * is obtained from the <code>config-class</code> attribute.
269 |  |
          *
270 |  |
          * @return the name of the bean's class
271 |  |
          */
272 |  |
         public String getBeanClassName()
273 |  |
         {
274 | 34 |
             return getConfiguration().getString(ATTR_BEAN_CLASS);
275 |  |
         }
276 |  |
277 |  |
         /**
278 |  |
          * Returns a map with the bean's (simple) properties. The properties are
279 |  |
          * collected from all attribute nodes, which are not reserved.
280 |  |
          *
281 |  |
          * @return a map with the bean's properties
282 |  |
          */
283 |  |
         public Map getBeanProperties()
284 |  |
         {
285 | 94 |
             Map props = new HashMap();
286 | 331 |
             for (Iterator it = getNode().getAttributes().iterator(); it.hasNext();)
287 |  |
             {
288 | 143 |
                 ConfigurationNode attr = (ConfigurationNode) it.next();
289 | 143 |
                 if (!isReservedNode(attr))
290 |  |
                 {
291 | 98 |
                     props.put(attr.getName(), interpolate(attr .getValue()));
292 |  |
                 }
293 |  |
             }
294 |  |
295 | 94 |
             return props;
296 |  |
         }
297 |  |
298 |  |
         /**
299 |  |
          * Returns a map with bean declarations for the complex properties of the
300 |  |
          * bean to be created. These declarations are obtained from the child nodes
301 |  |
          * of this declaration's root node.
302 |  |
          *
303 |  |
          * @return a map with bean declarations for complex properties
304 |  |
          */
305 |  |
         public Map getNestedBeanDeclarations()
306 |  |
         {
307 | 89 |
             Map nested = new HashMap();
308 | 192 |
             for (Iterator it = getNode().getChildren().iterator(); it.hasNext();)
309 |  |
             {
310 | 14 |
                 ConfigurationNode child = (ConfigurationNode) it.next();
311 | 14 |
                 if (!isReservedNode(child))
312 |  |
                 {
313 | 14 |
                     nested.put(child.getName(), new XMLBeanDeclaration(
314 |  |
                             getConfiguration().configurationAt(child.getName()), child));
315 |  |
                 }
316 |  |
             }
317 |  |
318 | 89 |
             return nested;
319 |  |
         }
320 |  |
321 |  |
         /**
322 |  |
          * Performs interpolation for the specified value. This implementation will
323 |  |
          * interpolate against the current subnode configuration's parent. If sub
324 |  |
          * classes need a different interpolation mechanism, they should override
325 |  |
          * this method.
326 |  |
          *
327 |  |
          * @param value the value that is to be interpolated
328 |  |
          * @return the interpolated value
329 |  |
          */
330 |  |
         protected Object interpolate(Object value)
331 |  |
         {
332 | 41 |
             return PropertyConverter.interpolate(value, getConfiguration()
333 |  |
                     .getParent());
334 |  |
         }
335 |  |
336 |  |
         /**
337 |  |
          * Checks if the specified node is reserved and thus should be ignored. This
338 |  |
          * method is called when the maps for the bean's properties and complex
339 |  |
          * properties are collected. It checks whether the given node is an
340 |  |
          * attribute node and if its name starts with the reserved prefix.
341 |  |
          *
342 |  |
          * @param nd the node to be checked
343 |  |
          * @return a flag whether this node is reserved (and does not point to a
344 |  |
          * property)
345 |  |
          */
346 |  |
         protected boolean isReservedNode(ConfigurationNode nd)
347 |  |
         {
348 | 168 |
             return nd.isAttribute()
349 |  |
                     && (nd.getName() == null || nd.getName().startsWith(
350 |  |
                             RESERVED_PREFIX));
351 |  |
         }
352 |  |
353 |  |
         /**
354 |  |
          * Initializes the internally managed subnode configuration. This method
355 |  |
          * will set some default values for some properties.
356 |  |
          *
357 |  |
          * @param conf the configuration to initialize
358 |  |
          */
359 |  |
         private void initSubnodeConfiguration(SubnodeConfiguration conf)
360 |  |
         {
361 | 107 |
             conf.setThrowExceptionOnMissing(false);
362 | 107 |
             conf.setExpressionEngine(null);
363 | 107 |
         }
364 |  |
     }