001 /* $Id: BeanPropertySetterRule.java 471661 2006-11-06 08:09:25Z skitching $ 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 020 package org.apache.commons.digester; 021 022 023 import java.beans.PropertyDescriptor; 024 025 import org.apache.commons.beanutils.BeanUtils; 026 import org.apache.commons.beanutils.DynaBean; 027 import org.apache.commons.beanutils.DynaProperty; 028 import org.apache.commons.beanutils.PropertyUtils; 029 030 031 /** 032 * <p> Rule implements sets a bean property on the top object 033 * to the body text.</p> 034 * 035 * <p> The property set:</p> 036 * <ul><li>can be specified when the rule is created</li> 037 * <li>or can match the current element when the rule is called.</li></ul> 038 * 039 * <p> Using the second method and the {@link ExtendedBaseRules} child match 040 * pattern, all the child elements can be automatically mapped to properties 041 * on the parent object.</p> 042 */ 043 044 public class BeanPropertySetterRule extends Rule { 045 046 047 // ----------------------------------------------------------- Constructors 048 049 050 /** 051 * <p>Construct rule that sets the given property from the body text.</p> 052 * 053 * @param digester associated <code>Digester</code> 054 * @param propertyName name of property to set 055 * 056 * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 057 * Use {@link #BeanPropertySetterRule(String propertyName)} instead. 058 */ 059 public BeanPropertySetterRule(Digester digester, String propertyName) { 060 061 this(propertyName); 062 063 } 064 065 /** 066 * <p>Construct rule that automatically sets a property from the body text. 067 * 068 * <p> This construct creates a rule that sets the property 069 * on the top object named the same as the current element. 070 * 071 * @param digester associated <code>Digester</code> 072 * 073 * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 074 * Use {@link #BeanPropertySetterRule()} instead. 075 */ 076 public BeanPropertySetterRule(Digester digester) { 077 078 this(); 079 080 } 081 082 /** 083 * <p>Construct rule that sets the given property from the body text.</p> 084 * 085 * @param propertyName name of property to set 086 */ 087 public BeanPropertySetterRule(String propertyName) { 088 089 this.propertyName = propertyName; 090 091 } 092 093 /** 094 * <p>Construct rule that automatically sets a property from the body text. 095 * 096 * <p> This construct creates a rule that sets the property 097 * on the top object named the same as the current element. 098 */ 099 public BeanPropertySetterRule() { 100 101 this((String)null); 102 103 } 104 105 // ----------------------------------------------------- Instance Variables 106 107 108 /** 109 * Set this property on the top object. 110 */ 111 protected String propertyName = null; 112 113 114 /** 115 * The body text used to set the property. 116 */ 117 protected String bodyText = null; 118 119 120 // --------------------------------------------------------- Public Methods 121 122 123 /** 124 * Process the body text of this element. 125 * 126 * @param namespace the namespace URI of the matching element, or an 127 * empty string if the parser is not namespace aware or the element has 128 * no namespace 129 * @param name the local name if the parser is namespace aware, or just 130 * the element name otherwise 131 * @param text The text of the body of this element 132 */ 133 public void body(String namespace, String name, String text) 134 throws Exception { 135 136 // log some debugging information 137 if (digester.log.isDebugEnabled()) { 138 digester.log.debug("[BeanPropertySetterRule]{" + 139 digester.match + "} Called with text '" + text + "'"); 140 } 141 142 bodyText = text.trim(); 143 144 } 145 146 147 /** 148 * Process the end of this element. 149 * 150 * @param namespace the namespace URI of the matching element, or an 151 * empty string if the parser is not namespace aware or the element has 152 * no namespace 153 * @param name the local name if the parser is namespace aware, or just 154 * the element name otherwise 155 * 156 * @exception NoSuchMethodException if the bean does not 157 * have a writeable property of the specified name 158 */ 159 public void end(String namespace, String name) throws Exception { 160 161 String property = propertyName; 162 163 if (property == null) { 164 // If we don't have a specific property name, 165 // use the element name. 166 property = name; 167 } 168 169 // Get a reference to the top object 170 Object top = digester.peek(); 171 172 // log some debugging information 173 if (digester.log.isDebugEnabled()) { 174 digester.log.debug("[BeanPropertySetterRule]{" + digester.match + 175 "} Set " + top.getClass().getName() + " property " + 176 property + " with text " + bodyText); 177 } 178 179 // Force an exception if the property does not exist 180 // (BeanUtils.setProperty() silently returns in this case) 181 if (top instanceof DynaBean) { 182 DynaProperty desc = 183 ((DynaBean) top).getDynaClass().getDynaProperty(property); 184 if (desc == null) { 185 throw new NoSuchMethodException 186 ("Bean has no property named " + property); 187 } 188 } else /* this is a standard JavaBean */ { 189 PropertyDescriptor desc = 190 PropertyUtils.getPropertyDescriptor(top, property); 191 if (desc == null) { 192 throw new NoSuchMethodException 193 ("Bean has no property named " + property); 194 } 195 } 196 197 // Set the property (with conversion as necessary) 198 BeanUtils.setProperty(top, property, bodyText); 199 200 } 201 202 203 /** 204 * Clean up after parsing is complete. 205 */ 206 public void finish() throws Exception { 207 208 bodyText = null; 209 210 } 211 212 213 /** 214 * Render a printable version of this Rule. 215 */ 216 public String toString() { 217 218 StringBuffer sb = new StringBuffer("BeanPropertySetterRule["); 219 sb.append("propertyName="); 220 sb.append(propertyName); 221 sb.append("]"); 222 return (sb.toString()); 223 224 } 225 226 }