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 * <p> 034 * Rule implements sets a bean property on the top object to the body text. 035 * </p> 036 * <p> 037 * The property set: 038 * </p> 039 * <ul> 040 * <li>can be specified when the rule is created</li> 041 * <li>or can match the current element when the rule is called.</li> 042 * </ul> 043 * <p> 044 * Using the second method and the {@link ExtendedBaseRules} child match pattern, all the child elements can be 045 * automatically mapped to properties on the parent object. 046 * </p> 047 */ 048 public class BeanPropertySetterRule 049 extends Rule 050 { 051 052 // ----------------------------------------------------------- Constructors 053 054 /** 055 * <p> 056 * Construct rule that sets the given property from the body text. 057 * </p> 058 * 059 * @param propertyName name of property to set 060 */ 061 public BeanPropertySetterRule( String propertyName ) 062 { 063 this.propertyName = propertyName; 064 } 065 066 /** 067 * <p> 068 * Construct rule that automatically sets a property from the body text. 069 * <p> 070 * This construct creates a rule that sets the property on the top object named the same as the current element. 071 */ 072 public BeanPropertySetterRule() 073 { 074 this( null ); 075 } 076 077 // ----------------------------------------------------- Instance Variables 078 079 /** 080 * Set this property on the top object. 081 */ 082 private String propertyName; 083 084 /** 085 * Extract the property name from attribute 086 */ 087 private String propertyNameFromAttribute; 088 089 /** 090 * The body text used to set the property. 091 */ 092 private String bodyText = null; 093 094 // --------------------------------------------------------- Public Methods 095 096 /** 097 * Returns the property name associated to this setter rule. 098 * 099 * @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 }