001/* $Id: BeanPropertySetterRule.java 992060 2010-09-02 19:09:47Z simonetripodi $
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    @Deprecated
060    public BeanPropertySetterRule(Digester digester, String propertyName) {
061
062        this(propertyName);
063
064    }
065
066    /**
067     * <p>Construct rule that automatically sets a property from the body text.
068     *
069     * <p> This construct creates a rule that sets the property
070     * on the top object named the same as the current element.
071     *
072     * @param digester associated <code>Digester</code>
073     *     
074     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
075     * Use {@link #BeanPropertySetterRule()} instead.
076     */
077    @Deprecated
078    public BeanPropertySetterRule(Digester digester) {
079
080        this();
081
082    }
083
084    /**
085     * <p>Construct rule that sets the given property from the body text.</p>
086     *
087     * @param propertyName name of property to set
088     */
089    public BeanPropertySetterRule(String propertyName) {
090
091        this.propertyName = propertyName;
092
093    }
094
095    /**
096     * <p>Construct rule that automatically sets a property from the body text.
097     *
098     * <p> This construct creates a rule that sets the property
099     * on the top object named the same as the current element.
100     */
101    public BeanPropertySetterRule() {
102
103        this((String)null);
104
105    }
106    
107    // ----------------------------------------------------- Instance Variables
108
109
110    /**
111     * Set this property on the top object.
112     */
113    protected String propertyName = null;
114
115
116    /**
117     * The body text used to set the property.
118     */
119    protected String bodyText = null;
120
121
122    // --------------------------------------------------------- Public Methods
123
124
125    /**
126     * Process the body text of this element.
127     *
128     * @param namespace the namespace URI of the matching element, or an 
129     *   empty string if the parser is not namespace aware or the element has
130     *   no namespace
131     * @param name the local name if the parser is namespace aware, or just 
132     *   the element name otherwise
133     * @param text The text of the body of this element
134     */
135    @Override
136    public void body(String namespace, String name, String text)
137        throws Exception {
138
139        // log some debugging information
140        if (digester.log.isDebugEnabled()) {
141            digester.log.debug("[BeanPropertySetterRule]{" +
142                    digester.match + "} Called with text '" + text + "'");
143        }
144
145        bodyText = text.trim();
146
147    }
148
149
150    /**
151     * Process the end of this element.
152     *
153     * @param namespace the namespace URI of the matching element, or an 
154     *   empty string if the parser is not namespace aware or the element has
155     *   no namespace
156     * @param name the local name if the parser is namespace aware, or just 
157     *   the element name otherwise
158     *
159     * @exception NoSuchMethodException if the bean does not
160     *  have a writeable property of the specified name
161     */
162    @Override
163    public void end(String namespace, String name) throws Exception {
164
165        String property = propertyName;
166
167        if (property == null) {
168            // If we don't have a specific property name,
169            // use the element name.
170            property = name;
171        }
172
173        // Get a reference to the top object
174        Object top = digester.peek();
175
176        // log some debugging information
177        if (digester.log.isDebugEnabled()) {
178            digester.log.debug("[BeanPropertySetterRule]{" + digester.match +
179                    "} Set " + top.getClass().getName() + " property " +
180                               property + " with text " + bodyText);
181        }
182
183        // Force an exception if the property does not exist
184        // (BeanUtils.setProperty() silently returns in this case)
185        if (top instanceof DynaBean) {
186            DynaProperty desc =
187                ((DynaBean) top).getDynaClass().getDynaProperty(property);
188            if (desc == null) {
189                throw new NoSuchMethodException
190                    ("Bean has no property named " + property);
191            }
192        } else /* this is a standard JavaBean */ {
193            PropertyDescriptor desc =
194                PropertyUtils.getPropertyDescriptor(top, property);
195            if (desc == null) {
196                throw new NoSuchMethodException
197                    ("Bean has no property named " + property);
198            }
199        }
200
201        // Set the property (with conversion as necessary)
202        BeanUtils.setProperty(top, property, bodyText);
203
204    }
205
206
207    /**
208     * Clean up after parsing is complete.
209     */
210    @Override
211    public void finish() throws Exception {
212
213        bodyText = null;
214
215    }
216
217
218    /**
219     * Render a printable version of this Rule.
220     */
221    @Override
222    public String toString() {
223
224        StringBuffer sb = new StringBuffer("BeanPropertySetterRule[");
225        sb.append("propertyName=");
226        sb.append(propertyName);
227        sb.append("]");
228        return (sb.toString());
229
230    }
231
232}