001 package org.apache.commons.digester3;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import static java.lang.String.format;
023 import static org.apache.commons.beanutils.BeanUtils.setProperty;
024 import static org.apache.commons.beanutils.PropertyUtils.getPropertyDescriptor;
025
026 import java.beans.PropertyDescriptor;
027
028 import org.apache.commons.beanutils.DynaBean;
029 import org.apache.commons.beanutils.DynaProperty;
030 import org.xml.sax.Attributes;
031
032 /**
033 * <p>
034 * Rule implements sets a bean property on the top object to the body text.
035 * </p>
036 * <p>
037 * The property set:
038 * </p>
039 * <ul>
040 * <li>can be specified when the rule is created</li>
041 * <li>or can match the current element when the rule is called.</li>
042 * </ul>
043 * <p>
044 * Using the second method and the {@link ExtendedBaseRules} child match pattern, all the child elements can be
045 * automatically mapped to properties on the parent object.
046 * </p>
047 */
048 public class BeanPropertySetterRule
049 extends Rule
050 {
051
052 // ----------------------------------------------------------- Constructors
053
054 /**
055 * <p>
056 * Construct rule that sets the given property from the body text.
057 * </p>
058 *
059 * @param propertyName name of property to set
060 */
061 public BeanPropertySetterRule( String propertyName )
062 {
063 this.propertyName = propertyName;
064 }
065
066 /**
067 * <p>
068 * Construct rule that automatically sets a property from the body text.
069 * <p>
070 * This construct creates a rule that sets the property on the top object named the same as the current element.
071 */
072 public BeanPropertySetterRule()
073 {
074 this( null );
075 }
076
077 // ----------------------------------------------------- Instance Variables
078
079 /**
080 * Set this property on the top object.
081 */
082 private String propertyName;
083
084 /**
085 * Extract the property name from attribute
086 */
087 private String propertyNameFromAttribute;
088
089 /**
090 * The body text used to set the property.
091 */
092 private String bodyText = null;
093
094 // --------------------------------------------------------- Public Methods
095
096 /**
097 * Returns the property name associated to this setter rule.
098 *
099 * @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 }