001package 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
022import static java.lang.String.format;
023import static org.apache.commons.beanutils.BeanUtils.setProperty;
024import static org.apache.commons.beanutils.PropertyUtils.getPropertyDescriptor;
025
026import java.beans.PropertyDescriptor;
027
028import org.apache.commons.beanutils.DynaBean;
029import org.apache.commons.beanutils.DynaProperty;
030import 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 */
036public 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}