%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
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; |
|
19 | import java.util.HashMap; |
|
20 | import java.util.HashSet; |
|
21 | import java.util.Iterator; |
|
22 | import java.util.Map; |
|
23 | import java.util.Set; |
|
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 | * <j:useBean var="person" class="com.acme.Person" name="James" location="${loc}"/> |
|
45 | * <j:useBean var="order" class="${orderClass}" amount="12" price="123.456"/> |
|
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 bean; |
|
55 | ||
56 | /** the default class to use if no Class is specified */ |
|
57 | private Class 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 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 | 91 | private boolean ignoreUnknownProperties = false; |
72 | ||
73 | ||
74 | 91 | public UseBeanTag() { |
75 | 91 | } |
76 | ||
77 | 0 | public UseBeanTag(Class defaultClass) { |
78 | 0 | this.defaultClass = defaultClass; |
79 | 0 | } |
80 | ||
81 | // BeanSource interface |
|
82 | //------------------------------------------------------------------------- |
|
83 | ||
84 | /** |
|
85 | * @return the bean that has just been created |
|
86 | */ |
|
87 | public Object getBean() { |
|
88 | 13 | return bean; |
89 | } |
|
90 | ||
91 | ||
92 | // Tag interface |
|
93 | //------------------------------------------------------------------------- |
|
94 | public void doTag(XMLOutput output) throws JellyTagException { |
|
95 | 91 | Map attributes = getAttributes(); |
96 | 91 | String var = (String) attributes.get( "var" ); |
97 | 91 | Object classObject = attributes.get( "class" ); |
98 | 91 | addIgnoreProperty("class"); |
99 | 91 | addIgnoreProperty("var"); |
100 | ||
101 | try { |
|
102 | // this method could return null in derived classes |
|
103 | 91 | Class theClass = convertToClass(classObject); |
104 | ||
105 | 91 | bean = newInstance(theClass, attributes, output); |
106 | 91 | setBeanProperties(bean, attributes); |
107 | ||
108 | // invoke body which could result in other properties being set |
|
109 | 78 | invokeBody(output); |
110 | ||
111 | 78 | processBean(var, bean); |
112 | } |
|
113 | 0 | catch (ClassNotFoundException e) { |
114 | 0 | throw new JellyTagException(e); |
115 | 78 | } |
116 | 78 | } |
117 | ||
118 | // Implementation methods |
|
119 | //------------------------------------------------------------------------- |
|
120 | ||
121 | /** |
|
122 | * Allow derived classes to programatically set the bean |
|
123 | */ |
|
124 | protected void setBean(Object bean) { |
|
125 | 0 | this.bean = bean; |
126 | 0 | } |
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 convertToClass(Object classObject) |
|
135 | throws MissingAttributeException, ClassNotFoundException { |
|
136 | 91 | if (classObject instanceof Class) { |
137 | 0 | return (Class) classObject; |
138 | } |
|
139 | 91 | else if ( classObject == null ) { |
140 | 0 | Class theClass = getDefaultClass(); |
141 | 0 | if (theClass == null) { |
142 | 0 | throw new MissingAttributeException("class"); |
143 | } |
|
144 | 0 | return theClass; |
145 | } |
|
146 | else { |
|
147 | 91 | String className = classObject.toString(); |
148 | 91 | 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 loadClass(String className) throws ClassNotFoundException { |
|
158 | 91 | 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 newInstance(Class theClass, Map attributes, XMLOutput output) |
|
167 | throws JellyTagException { |
|
168 | try { |
|
169 | 91 | return theClass.newInstance(); |
170 | 0 | } catch (IllegalAccessException e) { |
171 | 0 | throw new JellyTagException(e.toString()); |
172 | 0 | } catch (InstantiationException e) { |
173 | 0 | 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 bean, Map attributes) throws JellyTagException { |
|
184 | 91 | Map attrsToUse = new HashMap(attributes); |
185 | 91 | attrsToUse.keySet().removeAll(getIgnorePropertySet()); |
186 | ||
187 | 91 | validateBeanProperties(bean, attrsToUse); |
188 | ||
189 | try { |
|
190 | 78 | BeanUtils.populate(bean, attrsToUse); |
191 | 0 | } catch (IllegalAccessException e) { |
192 | 0 | throw new JellyTagException("could not set the properties of the bean",e); |
193 | 0 | } catch (InvocationTargetException e) { |
194 | 0 | throw new JellyTagException("could not set the properties of the bean",e); |
195 | 78 | } |
196 | 78 | } |
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 bean, Map attributes) throws JellyTagException { |
|
207 | 91 | if (!isIgnoreUnknownProperties()) { |
208 | 78 | for (Iterator i=attributes.keySet().iterator();i.hasNext();) { |
209 | 130 | String attrName = (String)i.next(); |
210 | 130 | if (! PropertyUtils.isWriteable(bean, attrName)) { |
211 | 13 | throw new JellyTagException("No bean property found: " + attrName); |
212 | } |
|
213 | 117 | } |
214 | } |
|
215 | 78 | } |
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 var, Object bean) throws JellyTagException { |
|
223 | 78 | if (var != null) { |
224 | 65 | context.setVariable(var, bean); |
225 | 65 | } |
226 | else { |
|
227 | 26 | ArgTag parentArg = (ArgTag)(findAncestorWithClass(ArgTag.class)); |
228 | 13 | if(null != parentArg) { |
229 | 13 | parentArg.setValue(bean); |
230 | } |
|
231 | } |
|
232 | 78 | } |
233 | ||
234 | /** |
|
235 | * Allows derived classes to provide a default bean implementation class |
|
236 | */ |
|
237 | protected Class getDefaultClass() { |
|
238 | 0 | 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 name) { |
|
248 | 195 | getIgnorePropertySet().add(name); |
249 | 195 | } |
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 getIgnorePropertySet() { |
|
256 | 286 | if (ignoreProperties == null) { |
257 | 91 | ignoreProperties = new HashSet(); |
258 | } |
|
259 | ||
260 | 286 | return ignoreProperties; |
261 | } |
|
262 | ||
263 | /** |
|
264 | * @see {@link #setIgnoreUnknownProperties(boolean)} |
|
265 | * @return |
|
266 | */ |
|
267 | public boolean isIgnoreUnknownProperties() { |
|
268 | 91 | 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 | 13 | this.ignoreUnknownProperties = ignoreUnknownProps; |
279 | 13 | } |
280 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |