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    
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        @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    }