View Javadoc

1   package org.apache.commons.digester3;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import static java.lang.String.format;
23  import static org.apache.commons.beanutils.BeanUtils.setProperty;
24  import static org.apache.commons.beanutils.PropertyUtils.getPropertyDescriptor;
25  
26  import java.beans.PropertyDescriptor;
27  
28  import org.apache.commons.beanutils.DynaBean;
29  import org.apache.commons.beanutils.DynaProperty;
30  import org.xml.sax.Attributes;
31  
32  /**
33   * <p>
34   * Rule implements sets a bean property on the top object to the body text.
35   * </p>
36   * <p>
37   * The property set:
38   * </p>
39   * <ul>
40   * <li>can be specified when the rule is created</li>
41   * <li>or can match the current element when the rule is called.</li>
42   * </ul>
43   * <p>
44   * Using the second method and the {@link ExtendedBaseRules} child match pattern, all the child elements can be
45   * automatically mapped to properties on the parent object.
46   * </p>
47   */
48  public class BeanPropertySetterRule
49      extends Rule
50  {
51  
52      // ----------------------------------------------------------- Constructors
53  
54      /**
55       * <p>
56       * Construct rule that sets the given property from the body text.
57       * </p>
58       * 
59       * @param propertyName name of property to set
60       */
61      public BeanPropertySetterRule( String propertyName )
62      {
63          this.propertyName = propertyName;
64      }
65  
66      /**
67       * <p>
68       * Construct rule that automatically sets a property from the body text.
69       * <p>
70       * This construct creates a rule that sets the property on the top object named the same as the current element.
71       */
72      public BeanPropertySetterRule()
73      {
74          this( null );
75      }
76  
77      // ----------------------------------------------------- Instance Variables
78  
79      /**
80       * Set this property on the top object.
81       */
82      private String propertyName;
83  
84      /**
85       * Extract the property name from attribute
86       */
87      private String propertyNameFromAttribute;
88  
89      /**
90       * The body text used to set the property.
91       */
92      private String bodyText = null;
93  
94      // --------------------------------------------------------- Public Methods
95  
96      /**
97       * Returns the property name associated to this setter rule.
98       *
99       * @return The property name associated to this setter rule
100      */
101     public String getPropertyName()
102     {
103         return propertyName;
104     }
105 
106     /**
107      * Sets the attribute name from which the property name has to be extracted.
108      *
109      * @param propertyNameFromAttribute the attribute name from which the property name has to be extracted.
110      * @since 3.0
111      */
112     public void setPropertyNameFromAttribute( String propertyNameFromAttribute )
113     {
114         this.propertyNameFromAttribute = propertyNameFromAttribute;
115     }
116 
117     /**
118      * Returns the body text used to set the property.
119      *
120      * @return The body text used to set the property
121      */
122     protected String getBodyText()
123     {
124         return bodyText;
125     }
126 
127     /**
128      * {@inheritDoc}
129      */
130     @Override
131     public void begin( String namespace, String name, Attributes attributes )
132         throws Exception
133     {
134         if ( propertyNameFromAttribute != null )
135         {
136             propertyName = attributes.getValue( propertyNameFromAttribute );
137 
138             getDigester().getLogger().warn( format( "[BeanPropertySetterRule]{%s} Attribute '%s' not found in matching element '%s'",
139                                                     getDigester().getMatch(), propertyNameFromAttribute, name ) );
140         }
141     }
142 
143     /**
144      * {@inheritDoc}
145      */
146     @Override
147     public void body( String namespace, String name, String text )
148         throws Exception
149     {
150         // log some debugging information
151         if ( getDigester().getLogger().isDebugEnabled() )
152         {
153             getDigester().getLogger().debug( format( "[BeanPropertySetterRule]{%s} Called with text '%s'",
154                                                      getDigester().getMatch(),
155                                                      text ) );
156         }
157 
158         bodyText = text.trim();
159     }
160 
161     /**
162      * {@inheritDoc}
163      */
164     @Override
165     public void end( String namespace, String name )
166         throws Exception
167     {
168         String property = propertyName;
169 
170         if ( property == null )
171         {
172             // If we don't have a specific property name,
173             // use the element name.
174             property = name;
175         }
176 
177         // Get a reference to the top object
178         Object top = getDigester().peek();
179 
180         // log some debugging information
181         if ( getDigester().getLogger().isDebugEnabled() )
182         {
183             getDigester().getLogger().debug( format( "[BeanPropertySetterRule]{%s} Set %s property %s with text %s",
184                                                      getDigester().getMatch(),
185                                                      top.getClass().getName(),
186                                                      property,
187                                                      bodyText ) );
188         }
189 
190         // Force an exception if the property does not exist
191         // (BeanUtils.setProperty() silently returns in this case)
192         if ( top instanceof DynaBean )
193         {
194             DynaProperty desc = ( (DynaBean) top ).getDynaClass().getDynaProperty( property );
195             if ( desc == null )
196             {
197                 throw new NoSuchMethodException( "Bean has no property named " + property );
198             }
199         }
200         else
201         /* this is a standard JavaBean */
202         {
203             PropertyDescriptor desc = getPropertyDescriptor( top, property );
204             if ( desc == null )
205             {
206                 throw new NoSuchMethodException( "Bean has no property named " + property );
207             }
208         }
209 
210         // Set the property (with conversion as necessary)
211         setProperty( top, property, bodyText );
212     }
213 
214     /**
215      * {@inheritDoc}
216      */
217     @Override
218     public void finish()
219         throws Exception
220     {
221         bodyText = null;
222     }
223 
224     /**
225      * {@inheritDoc}
226      */
227     @Override
228     public String toString()
229     {
230         return format( "BeanPropertySetterRule[propertyName=%s]", propertyName );
231     }
232 
233 }