KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > jelly > tags > core > UseBeanTag


1 /*
2  * Copyright 2002,2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.commons.jelly.tags.core;
17
18 import java.lang.reflect.InvocationTargetException JavaDoc;
19 import java.util.HashMap JavaDoc;
20 import java.util.HashSet JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.Map JavaDoc;
23 import java.util.Set JavaDoc;
24
25 import org.apache.commons.beanutils.BeanUtils;
26 import org.apache.commons.beanutils.PropertyUtils;
27 import org.apache.commons.jelly.JellyTagException;
28 import org.apache.commons.jelly.MapTagSupport;
29 import org.apache.commons.jelly.MissingAttributeException;
30 import org.apache.commons.jelly.XMLOutput;
31 import org.apache.commons.jelly.impl.BeanSource;
32 import org.apache.commons.jelly.util.ClassLoaderUtils;
33
34 /**
35  * A tag which instantiates an instance of the given class
36  * and then sets the properties on the bean.
37  * The class can be specified via a {@link java.lang.Class} instance or
38  * a String which will be used to load the class using either the current
39  * thread's context class loader or the class loader used to load this
40  * Jelly library.
41  *
42  * This tag can be used it as follows,
43  * <pre>
44  * &lt;j:useBean var="person" class="com.acme.Person" name="James" location="${loc}"/&gt;
45  * &lt;j:useBean var="order" class="${orderClass}" amount="12" price="123.456"/&gt;
46  * </pre>
47  *
48  * @author <a HREF="mailto:jstrachan@apache.org">James Strachan</a>
49  * @version $Revision: 155420 $
50  */

51 public class UseBeanTag extends MapTagSupport implements BeanSource {
52
53     /** the current bean instance */
54     private Object JavaDoc bean;
55
56     /** the default class to use if no Class is specified */
57     private Class JavaDoc defaultClass;
58
59     /**
60      * a Set of Strings of property names to ignore (remove from the
61      * Map of attributes before passing to ConvertUtils)
62      */

63     private Set JavaDoc ignoreProperties;
64
65     /**
66      * If this tag finds an attribute in the XML that's not
67      * ignored by {@link #ignoreProperties} and isn't a
68      * bean property, should it throw an exception?
69      * @see #setIgnoreUnknownProperties(boolean)
70      */

71     private boolean ignoreUnknownProperties = false;
72
73
74     public UseBeanTag() {
75     }
76
77     public UseBeanTag(Class JavaDoc defaultClass) {
78         this.defaultClass = defaultClass;
79     }
80
81     // BeanSource interface
82
//-------------------------------------------------------------------------
83

84     /**
85      * @return the bean that has just been created
86      */

87     public Object JavaDoc getBean() {
88         return bean;
89     }
90
91
92     // Tag interface
93
//-------------------------------------------------------------------------
94
public void doTag(XMLOutput output) throws JellyTagException {
95         Map JavaDoc attributes = getAttributes();
96         String JavaDoc var = (String JavaDoc) attributes.get( "var" );
97         Object JavaDoc classObject = attributes.get( "class" );
98         addIgnoreProperty("class");
99         addIgnoreProperty("var");
100
101         try {
102             // this method could return null in derived classes
103
Class JavaDoc theClass = convertToClass(classObject);
104
105             bean = newInstance(theClass, attributes, output);
106             setBeanProperties(bean, attributes);
107
108             // invoke body which could result in other properties being set
109
invokeBody(output);
110
111             processBean(var, bean);
112         }
113         catch (ClassNotFoundException JavaDoc e) {
114             throw new JellyTagException(e);
115         }
116     }
117
118     // Implementation methods
119
//-------------------------------------------------------------------------
120

121     /**
122      * Allow derived classes to programatically set the bean
123      */

124     protected void setBean(Object JavaDoc bean) {
125         this.bean = bean;
126     }
127
128     /**
129      * Attempts to convert the given object to a Class instance.
130      * If the classObject is already a Class it will be returned
131      * otherwise it will be converted to a String and loaded
132      * using the default class loading mechanism.
133      */

134     protected Class JavaDoc convertToClass(Object JavaDoc classObject)
135     throws MissingAttributeException, ClassNotFoundException JavaDoc {
136         if (classObject instanceof Class JavaDoc) {
137             return (Class JavaDoc) classObject;
138         }
139         else if ( classObject == null ) {
140             Class JavaDoc theClass = getDefaultClass();
141             if (theClass == null) {
142                 throw new MissingAttributeException("class");
143             }
144             return theClass;
145         }
146         else {
147             String JavaDoc className = classObject.toString();
148             return loadClass(className);
149         }
150     }
151
152     /**
153      * Loads the given class using the default class loading mechanism
154      * which is to try use the current Thread's context class loader first
155      * otherise use the class loader which loaded this class.
156      */

157     protected Class JavaDoc loadClass(String JavaDoc className) throws ClassNotFoundException JavaDoc {
158         return ClassLoaderUtils.loadClass(className, getClass());
159     }
160
161     /**
162      * Creates a new instance of the given class, which by default will invoke the
163      * default constructor.
164      * Derived tags could do something different here.
165      */

166     protected Object JavaDoc newInstance(Class JavaDoc theClass, Map JavaDoc attributes, XMLOutput output)
167     throws JellyTagException {
168         try {
169             return theClass.newInstance();
170         } catch (IllegalAccessException JavaDoc e) {
171             throw new JellyTagException(e.toString());
172         } catch (InstantiationException JavaDoc e) {
173             throw new JellyTagException(e.toString());
174         }
175     }
176
177     /**
178      * Sets the properties on the bean. Derived tags could implement some custom
179      * type conversion etc.
180      * <p/>
181      * This method ignores all property names in the Set returned by {@link #getIgnorePropertySet()}.
182      */

183     protected void setBeanProperties(Object JavaDoc bean, Map JavaDoc attributes) throws JellyTagException {
184         Map JavaDoc attrsToUse = new HashMap JavaDoc(attributes);
185         attrsToUse.keySet().removeAll(getIgnorePropertySet());
186
187         validateBeanProperties(bean, attrsToUse);
188
189         try {
190             BeanUtils.populate(bean, attrsToUse);
191         } catch (IllegalAccessException JavaDoc e) {
192             throw new JellyTagException("could not set the properties of the bean",e);
193         } catch (InvocationTargetException JavaDoc e) {
194             throw new JellyTagException("could not set the properties of the bean",e);
195         }
196     }
197
198     /**
199      * If {@link #isIgnoreUnknownProperties()} returns true, make sure that
200      * every non-ignored ({@see #addIgnoreProperty(String)}) property
201      * matches a writable property on the target bean.
202      * @param bean the bean to validate
203      * @param attributes the list of properties to validate
204      * @throws JellyTagException when a property is not writeable
205      */

206     protected void validateBeanProperties(Object JavaDoc bean, Map JavaDoc attributes) throws JellyTagException {
207         if (!isIgnoreUnknownProperties()) {
208             for (Iterator JavaDoc i=attributes.keySet().iterator();i.hasNext();) {
209                 String JavaDoc attrName = (String JavaDoc)i.next();
210                 if (! PropertyUtils.isWriteable(bean, attrName)) {
211                     throw new JellyTagException("No bean property found: " + attrName);
212                 }
213             }
214         }
215     }
216
217     /**
218      * By default this will export the bean using the given variable if it is defined.
219      * This Strategy method allows derived tags to process the beans in different ways
220      * such as to register this bean with its parent tag etc.
221      */

222     protected void processBean(String JavaDoc var, Object JavaDoc bean) throws JellyTagException {
223         if (var != null) {
224             context.setVariable(var, bean);
225         }
226         else {
227             ArgTag parentArg = (ArgTag)(findAncestorWithClass(ArgTag.class));
228             if(null != parentArg) {
229                 parentArg.setValue(bean);
230             }
231         }
232     }
233
234     /**
235      * Allows derived classes to provide a default bean implementation class
236      */

237     protected Class JavaDoc getDefaultClass() {
238         return defaultClass;
239     }
240
241     /**
242      * Adds a name to the Set of property names that will be skipped when setting
243      * bean properties. In other words, names added here won't be set into the bean
244      * if they're present in the attribute Map.
245      * @param name
246      */

247     protected void addIgnoreProperty(String JavaDoc name) {
248         getIgnorePropertySet().add(name);
249     }
250
251     /**
252      * @return the Set of property names that should be ignored when setting the
253      * properties of the bean.
254      */

255     protected Set JavaDoc getIgnorePropertySet() {
256         if (ignoreProperties == null) {
257             ignoreProperties = new HashSet JavaDoc();
258         }
259
260         return ignoreProperties;
261     }
262
263     /**
264      * @see {@link #setIgnoreUnknownProperties(boolean)}
265      * @return
266      */

267     public boolean isIgnoreUnknownProperties() {
268         return ignoreUnknownProperties;
269     }
270
271     /**
272      * If this tag finds an attribute in the XML that's not
273      * ignored by {@link #ignoreProperties} and isn't a
274      * bean property, should it throw an exception?
275      * @param ignoreUnknownProperties Sets {@link #ignoreUnknownProperties}.
276      */

277     public void setIgnoreUnknownProperties(boolean ignoreUnknownProps) {
278         this.ignoreUnknownProperties = ignoreUnknownProps;
279     }
280 }
281
Popular Tags