001/* $Id: SetPropertyRule.java 568192 2007-08-21 16:41:16Z bayard $
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;
029import org.xml.sax.Attributes;
030
031
032/**
033 * Rule implementation that sets an individual property on the object at the
034 * top of the stack, based on attributes with specified names.
035 */
036
037public class SetPropertyRule extends Rule {
038
039
040    // ----------------------------------------------------------- Constructors
041
042
043    /**
044     * Construct a "set property" rule with the specified name and value
045     * attributes.
046     *
047     * @param digester The digester with which this rule is associated
048     * @param name Name of the attribute that will contain the name of the
049     *  property to be set
050     * @param value Name of the attribute that will contain the value to which
051     *  the property should be set
052     *
053     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
054     * Use {@link #SetPropertyRule(String name, String value)} instead.
055     */
056    public SetPropertyRule(Digester digester, String name, String value) {
057
058        this(name, value);
059
060    }
061
062    /**
063     * Construct a "set property" rule with the specified name and value
064     * attributes.
065     *
066     * @param name Name of the attribute that will contain the name of the
067     *  property to be set
068     * @param value Name of the attribute that will contain the value to which
069     *  the property should be set
070     */
071    public SetPropertyRule(String name, String value) {
072
073        this.name = name;
074        this.value = value;
075
076    }
077
078    // ----------------------------------------------------- Instance Variables
079
080
081    /**
082     * The attribute that will contain the property name.
083     */
084    protected String name = null;
085
086
087    /**
088     * The attribute that will contain the property value.
089     */
090    protected String value = null;
091
092
093    // --------------------------------------------------------- Public Methods
094
095
096    /**
097     * Process the beginning of this element.
098     *
099     * @param attributes The attribute list of this element
100     *
101     * @exception NoSuchMethodException if the bean does not
102     *  have a writeable property of the specified name
103     */
104    public void begin(Attributes attributes) throws Exception {
105
106        if (attributes.getLength() == 0 ) {
107            return;
108        }
109
110        // Identify the actual property name and value to be used
111        String actualName = null;
112        String actualValue = null;
113        for (int i = 0; i < attributes.getLength(); i++) {
114            String name = attributes.getLocalName(i);
115            if ("".equals(name)) {
116                name = attributes.getQName(i);
117            }
118            String value = attributes.getValue(i);
119            if (name.equals(this.name)) {
120                actualName = value;
121            } else if (name.equals(this.value)) {
122                actualValue = value;
123            }
124        }
125
126        // Get a reference to the top object
127        Object top = digester.peek();
128
129        // Log some debugging information
130        if (digester.log.isDebugEnabled()) {
131            digester.log.debug("[SetPropertyRule]{" + digester.match +
132                    "} Set " + top.getClass().getName() + " property " +
133                    actualName + " to " + actualValue);
134        }
135
136        // Force an exception if the property does not exist
137        // (BeanUtils.setProperty() silently returns in this case)
138        //
139        // This code should probably use PropertyUtils.isWriteable(), 
140        // like SetPropertiesRule does.
141        if (top instanceof DynaBean) {
142            DynaProperty desc =
143                ((DynaBean) top).getDynaClass().getDynaProperty(actualName);
144            if (desc == null) {
145                throw new NoSuchMethodException
146                    ("Bean has no property named " + actualName);
147            }
148        } else /* this is a standard JavaBean */ {
149            PropertyDescriptor desc =
150                PropertyUtils.getPropertyDescriptor(top, actualName);
151            if (desc == null) {
152                throw new NoSuchMethodException
153                    ("Bean has no property named " + actualName);
154            }
155        }
156
157        // Set the property (with conversion as necessary)
158        BeanUtils.setProperty(top, actualName, actualValue);
159
160    }
161
162
163    /**
164     * Render a printable version of this Rule.
165     */
166    public String toString() {
167
168        StringBuffer sb = new StringBuffer("SetPropertyRule[");
169        sb.append("name=");
170        sb.append(name);
171        sb.append(", value=");
172        sb.append(value);
173        sb.append("]");
174        return (sb.toString());
175
176    }
177
178
179}