001    package org.apache.commons.digester3;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import static java.lang.String.format;
023    import static org.apache.commons.beanutils.BeanUtils.setProperty;
024    import static org.apache.commons.beanutils.PropertyUtils.getPropertyDescriptor;
025    
026    import java.beans.PropertyDescriptor;
027    
028    import org.apache.commons.beanutils.DynaBean;
029    import org.apache.commons.beanutils.DynaProperty;
030    import org.xml.sax.Attributes;
031    
032    /**
033     * Rule implementation that sets an individual property on the object at the top of the stack, based on attributes with
034     * specified names.
035     */
036    public class SetPropertyRule
037        extends Rule
038    {
039    
040        // ----------------------------------------------------------- Constructors
041    
042        /**
043         * Construct a "set property" rule with the specified name and value attributes.
044         * 
045         * @param name Name of the attribute that will contain the name of the property to be set
046         * @param value Name of the attribute that will contain the value to which the property should be set
047         */
048        public SetPropertyRule( String name, String value )
049        {
050            this.name = name;
051            this.value = value;
052        }
053    
054        // ----------------------------------------------------- Instance Variables
055    
056        /**
057         * The attribute that will contain the property name.
058         */
059        protected String name = null;
060    
061        /**
062         * The attribute that will contain the property value.
063         */
064        protected String value = null;
065    
066        // --------------------------------------------------------- Public Methods
067    
068        /**
069         * {@inheritDoc}
070         */
071        @Override
072        public void begin( String namespace, String name, Attributes attributes )
073            throws Exception
074        {
075            if ( attributes.getLength() == 0 )
076            {
077                return;
078            }
079    
080            // Identify the actual property name and value to be used
081            String actualName = null;
082            String actualValue = null;
083            for ( int i = 0; i < attributes.getLength(); i++ )
084            {
085                String attributeName = attributes.getLocalName( i );
086                if ( "".equals( attributeName ) )
087                {
088                    attributeName = attributes.getQName( i );
089                }
090                String value = attributes.getValue( i );
091                if ( attributeName.equals( this.name ) )
092                {
093                    actualName = value;
094                }
095                else if ( attributeName.equals( this.value ) )
096                {
097                    actualValue = value;
098                }
099            }
100    
101            // Get a reference to the top object
102            Object top = getDigester().peek();
103    
104            // Log some debugging information
105            if ( getDigester().getLogger().isDebugEnabled() )
106            {
107                getDigester().getLogger().debug( format( "[SetPropertiesRule]{%s} Set %s property %s to %s",
108                                                         getDigester().getMatch(),
109                                                         top.getClass().getName(),
110                                                         actualName,
111                                                         actualValue ) );
112            }
113    
114            // Force an exception if the property does not exist
115            // (BeanUtils.setProperty() silently returns in this case)
116            //
117            // This code should probably use PropertyUtils.isWriteable(),
118            // like SetPropertiesRule does.
119            if ( top instanceof DynaBean )
120            {
121                DynaProperty desc = ( (DynaBean) top ).getDynaClass().getDynaProperty( actualName );
122                if ( desc == null )
123                {
124                    throw new NoSuchMethodException( "Bean has no property named " + actualName );
125                }
126            }
127            else
128            /* this is a standard JavaBean */
129            {
130                PropertyDescriptor desc = getPropertyDescriptor( top, actualName );
131                if ( desc == null )
132                {
133                    throw new NoSuchMethodException( "Bean has no property named " + actualName );
134                }
135            }
136    
137            // Set the property (with conversion as necessary)
138            setProperty( top, actualName, actualValue );
139        }
140    
141        /**
142         * {@inheritDoc}
143         */
144        @Override
145        public String toString()
146        {
147            return format( "SetPropertyRule[name=%s, value=%s]", name, value );
148        }
149    
150    }