001/* $Id: BeanPropertySetterRule.java 471661 2006-11-06 08:09:25Z skitching $
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements.  See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * the License.  You may obtain a copy of the License at
009 * 
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 * 
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */ 
018
019
020package org.apache.commons.digester;
021
022
023import java.beans.PropertyDescriptor;
024
025import org.apache.commons.beanutils.BeanUtils;
026import org.apache.commons.beanutils.DynaBean;
027import org.apache.commons.beanutils.DynaProperty;
028import org.apache.commons.beanutils.PropertyUtils;
029
030
031/**
032 * <p> Rule implements sets a bean property on the top object
033 * to the body text.</p>
034 *
035 * <p> The property set:</p>
036 * <ul><li>can be specified when the rule is created</li>
037 * <li>or can match the current element when the rule is called.</li></ul>
038 *
039 * <p> Using the second method and the {@link ExtendedBaseRules} child match
040 * pattern, all the child elements can be automatically mapped to properties
041 * on the parent object.</p>
042 */
043
044public class BeanPropertySetterRule extends Rule {
045
046
047    // ----------------------------------------------------------- Constructors
048
049
050    /**
051     * <p>Construct rule that sets the given property from the body text.</p>
052     *
053     * @param digester associated <code>Digester</code>
054     * @param propertyName name of property to set
055     *
056     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
057     * Use {@link #BeanPropertySetterRule(String propertyName)} instead.
058     */
059    public BeanPropertySetterRule(Digester digester, String propertyName) {
060
061        this(propertyName);
062
063    }
064
065    /**
066     * <p>Construct rule that automatically sets a property from the body text.
067     *
068     * <p> This construct creates a rule that sets the property
069     * on the top object named the same as the current element.
070     *
071     * @param digester associated <code>Digester</code>
072     *     
073     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
074     * Use {@link #BeanPropertySetterRule()} instead.
075     */
076    public BeanPropertySetterRule(Digester digester) {
077
078        this();
079
080    }
081
082    /**
083     * <p>Construct rule that sets the given property from the body text.</p>
084     *
085     * @param propertyName name of property to set
086     */
087    public BeanPropertySetterRule(String propertyName) {
088
089        this.propertyName = propertyName;
090
091    }
092
093    /**
094     * <p>Construct rule that automatically sets a property from the body text.
095     *
096     * <p> This construct creates a rule that sets the property
097     * on the top object named the same as the current element.
098     */
099    public BeanPropertySetterRule() {
100
101        this((String)null);
102
103    }
104    
105    // ----------------------------------------------------- Instance Variables
106
107
108    /**
109     * Set this property on the top object.
110     */
111    protected String propertyName = null;
112
113
114    /**
115     * The body text used to set the property.
116     */
117    protected String bodyText = null;
118
119
120    // --------------------------------------------------------- Public Methods
121
122
123    /**
124     * Process the body text of this element.
125     *
126     * @param namespace the namespace URI of the matching element, or an 
127     *   empty string if the parser is not namespace aware or the element has
128     *   no namespace
129     * @param name the local name if the parser is namespace aware, or just 
130     *   the element name otherwise
131     * @param text The text of the body of this element
132     */
133    public void body(String namespace, String name, String text)
134        throws Exception {
135
136        // log some debugging information
137        if (digester.log.isDebugEnabled()) {
138            digester.log.debug("[BeanPropertySetterRule]{" +
139                    digester.match + "} Called with text '" + text + "'");
140        }
141
142        bodyText = text.trim();
143
144    }
145
146
147    /**
148     * Process the end of this element.
149     *
150     * @param namespace the namespace URI of the matching element, or an 
151     *   empty string if the parser is not namespace aware or the element has
152     *   no namespace
153     * @param name the local name if the parser is namespace aware, or just 
154     *   the element name otherwise
155     *
156     * @exception NoSuchMethodException if the bean does not
157     *  have a writeable property of the specified name
158     */
159    public void end(String namespace, String name) throws Exception {
160
161        String property = propertyName;
162
163        if (property == null) {
164            // If we don't have a specific property name,
165            // use the element name.
166            property = name;
167        }
168
169        // Get a reference to the top object
170        Object top = digester.peek();
171
172        // log some debugging information
173        if (digester.log.isDebugEnabled()) {
174            digester.log.debug("[BeanPropertySetterRule]{" + digester.match +
175                    "} Set " + top.getClass().getName() + " property " +
176                               property + " with text " + bodyText);
177        }
178
179        // Force an exception if the property does not exist
180        // (BeanUtils.setProperty() silently returns in this case)
181        if (top instanceof DynaBean) {
182            DynaProperty desc =
183                ((DynaBean) top).getDynaClass().getDynaProperty(property);
184            if (desc == null) {
185                throw new NoSuchMethodException
186                    ("Bean has no property named " + property);
187            }
188        } else /* this is a standard JavaBean */ {
189            PropertyDescriptor desc =
190                PropertyUtils.getPropertyDescriptor(top, property);
191            if (desc == null) {
192                throw new NoSuchMethodException
193                    ("Bean has no property named " + property);
194            }
195        }
196
197        // Set the property (with conversion as necessary)
198        BeanUtils.setProperty(top, property, bodyText);
199
200    }
201
202
203    /**
204     * Clean up after parsing is complete.
205     */
206    public void finish() throws Exception {
207
208        bodyText = null;
209
210    }
211
212
213    /**
214     * Render a printable version of this Rule.
215     */
216    public String toString() {
217
218        StringBuffer sb = new StringBuffer("BeanPropertySetterRule[");
219        sb.append("propertyName=");
220        sb.append(propertyName);
221        sb.append("]");
222        return (sb.toString());
223
224    }
225
226}