    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
    020    package org.apache.commons.digester;
    021
    022
    023    import java.beans.PropertyDescriptor;
    024
    025    import org.apache.commons.beanutils.BeanUtils;
    026    import org.apache.commons.beanutils.DynaBean;
    027    import org.apache.commons.beanutils.DynaProperty;
    028    import 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
    044    public 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    }