1 package org.apache.commons.digester3;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import static java.lang.String.format;
23 import static org.apache.commons.beanutils.BeanUtils.setProperty;
24 import static org.apache.commons.beanutils.PropertyUtils.getPropertyDescriptor;
25
26 import java.beans.PropertyDescriptor;
27
28 import org.apache.commons.beanutils.DynaBean;
29 import org.apache.commons.beanutils.DynaProperty;
30 import org.xml.sax.Attributes;
31
32 /**
33 * <p>
34 * Rule implements sets a bean property on the top object to the body text.
35 * </p>
36 * <p>
37 * The property set:
38 * </p>
39 * <ul>
40 * <li>can be specified when the rule is created</li>
41 * <li>or can match the current element when the rule is called.</li>
42 * </ul>
43 * <p>
44 * Using the second method and the {@link ExtendedBaseRules} child match pattern, all the child elements can be
45 * automatically mapped to properties on the parent object.
46 * </p>
47 */
48 public class BeanPropertySetterRule
49 extends Rule
50 {
51
52 // ----------------------------------------------------------- Constructors
53
54 /**
55 * <p>
56 * Construct rule that sets the given property from the body text.
57 * </p>
58 *
59 * @param propertyName name of property to set
60 */
61 public BeanPropertySetterRule( String propertyName )
62 {
63 this.propertyName = propertyName;
64 }
65
66 /**
67 * <p>
68 * Construct rule that automatically sets a property from the body text.
69 * <p>
70 * This construct creates a rule that sets the property on the top object named the same as the current element.
71 */
72 public BeanPropertySetterRule()
73 {
74 this( null );
75 }
76
77 // ----------------------------------------------------- Instance Variables
78
79 /**
80 * Set this property on the top object.
81 */
82 private String propertyName;
83
84 /**
85 * Extract the property name from attribute
86 */
87 private String propertyNameFromAttribute;
88
89 /**
90 * The body text used to set the property.
91 */
92 private String bodyText = null;
93
94 // --------------------------------------------------------- Public Methods
95
96 /**
97 * Returns the property name associated to this setter rule.
98 *
99 * @return The property name associated to this setter rule
100 */
101 public String getPropertyName()
102 {
103 return propertyName;
104 }
105
106 /**
107 * Sets the attribute name from which the property name has to be extracted.
108 *
109 * @param propertyNameFromAttribute the attribute name from which the property name has to be extracted.
110 * @since 3.0
111 */
112 public void setPropertyNameFromAttribute( String propertyNameFromAttribute )
113 {
114 this.propertyNameFromAttribute = propertyNameFromAttribute;
115 }
116
117 /**
118 * Returns the body text used to set the property.
119 *
120 * @return The body text used to set the property
121 */
122 protected String getBodyText()
123 {
124 return bodyText;
125 }
126
127 /**
128 * {@inheritDoc}
129 */
130 @Override
131 public void begin( String namespace, String name, Attributes attributes )
132 throws Exception
133 {
134 if ( propertyNameFromAttribute != null )
135 {
136 propertyName = attributes.getValue( propertyNameFromAttribute );
137
138 getDigester().getLogger().warn( format( "[BeanPropertySetterRule]{%s} Attribute '%s' not found in matching element '%s'",
139 getDigester().getMatch(), propertyNameFromAttribute, name ) );
140 }
141 }
142
143 /**
144 * {@inheritDoc}
145 */
146 @Override
147 public void body( String namespace, String name, String text )
148 throws Exception
149 {
150 // log some debugging information
151 if ( getDigester().getLogger().isDebugEnabled() )
152 {
153 getDigester().getLogger().debug( format( "[BeanPropertySetterRule]{%s} Called with text '%s'",
154 getDigester().getMatch(),
155 text ) );
156 }
157
158 bodyText = text.trim();
159 }
160
161 /**
162 * {@inheritDoc}
163 */
164 @Override
165 public void end( String namespace, String name )
166 throws Exception
167 {
168 String property = propertyName;
169
170 if ( property == null )
171 {
172 // If we don't have a specific property name,
173 // use the element name.
174 property = name;
175 }
176
177 // Get a reference to the top object
178 Object top = getDigester().peek();
179
180 // log some debugging information
181 if ( getDigester().getLogger().isDebugEnabled() )
182 {
183 getDigester().getLogger().debug( format( "[BeanPropertySetterRule]{%s} Set %s property %s with text %s",
184 getDigester().getMatch(),
185 top.getClass().getName(),
186 property,
187 bodyText ) );
188 }
189
190 // Force an exception if the property does not exist
191 // (BeanUtils.setProperty() silently returns in this case)
192 if ( top instanceof DynaBean )
193 {
194 DynaProperty desc = ( (DynaBean) top ).getDynaClass().getDynaProperty( property );
195 if ( desc == null )
196 {
197 throw new NoSuchMethodException( "Bean has no property named " + property );
198 }
199 }
200 else
201 /* this is a standard JavaBean */
202 {
203 PropertyDescriptor desc = getPropertyDescriptor( top, property );
204 if ( desc == null )
205 {
206 throw new NoSuchMethodException( "Bean has no property named " + property );
207 }
208 }
209
210 // Set the property (with conversion as necessary)
211 setProperty( top, property, bodyText );
212 }
213
214 /**
215 * {@inheritDoc}
216 */
217 @Override
218 public void finish()
219 throws Exception
220 {
221 bodyText = null;
222 }
223
224 /**
225 * {@inheritDoc}
226 */
227 @Override
228 public String toString()
229 {
230 return format( "BeanPropertySetterRule[propertyName=%s]", propertyName );
231 }
232
233 }