Coverage report

| %line | %branch
--- | --- | ---
org.apache.commons.jelly.tags.core.UseBeanTag |
70% |
99% |

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 |  |
      * &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 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.