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    }