##### 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

     }