1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.struts.faces.application;
18
19
20 import java.util.Map;
21
22 import javax.faces.el.PropertyNotFoundException;
23 import javax.faces.el.PropertyResolver;
24
25 import org.apache.commons.beanutils.DynaBean;
26 import org.apache.commons.beanutils.DynaProperty;
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.struts.action.DynaActionForm;
30
31
32
33 /***
34 * <p>Custom <code>PropertyResolver</code> implementation that adds support
35 * for <code>DynaBean</code> property access to the facilities of the default
36 * <code>PropertyResolver</code> provided by JavaServer Faces.</p>
37 *
38 * <p>This class implements the following specific rules:</p>
39 * <ul>
40 * <li>Indexed variants of each call are directly passed through to the
41 * <code>PropertyResolver</code> instance that we are wrapping.</li>
42 * <li>If the specified base object is an instance of
43 * <code>DynaActionForm</code>, and the requested property name is
44 * <code>map</code>, maintain compatibility with the way that JSP and
45 * JSTL expressions can access this property:
46 * <ul>
47 * <li><code>getValue()</code> will return the result of calling
48 * <code>getMap()</code> on the base object.</li>
49 * <li><code>setValue()</code> will throw an exception, because the
50 * map of property values is a read-only property of the
51 * <code>DynaActionForm</code> class.</li>
52 * <li><code>isReadOnly()</code> returns <code>true</code>.</li>
53 * <li><code>getType()</code> returns the <code>Class</code> object
54 * for <code>java.util.Map</code>.</li>
55 * </ul></li>
56 * <li>If the specified base object is an instance of
57 * <code>DynaBean</code>, provide access to its named properties
58 * as follows:
59 * <ul>
60 * <li><code>getValue()</code> will return the result of calling
61 * <code>get()</code> on the base object.</li>
62 * <li><code>setValue()</code> will call <code>set()</code>
63 * on the base object.</li>
64 * <li><code>isReadOnly()</code> returns <code>false</code> (because
65 * the DynaBean APIs provide no mechanism to make this determination,
66 * but most implementations will provide mutable properties).</li>
67 * <li><code>getType()</code> returns the <code>Class</code> object
68 * for the underlying dynamic property.</li>
69 * </ul></li>
70 * <li>Named variant calls with any other type of base object are
71 * passed through to the <code>PropertyResolver</code> that we
72 * are wrapping.</li>
73 * </ul>
74 *
75 * @version $Rev: 421138 $ $Date: 2006-07-11 22:41:40 -0700 (Tue, 11 Jul 2006) $
76 */
77
78 public class PropertyResolverImpl extends PropertyResolver {
79
80
81
82
83
84 /***
85 * <p>Construct a new <code>PropertyResolver</code> instance, wrapping the
86 * specified instance using the Decorator pattern such that this class need
87 * implement only the new functionality it supports, and not need to
88 * re-implement the basic facilities.
89 *
90 * @param resolver The original resolver to be wrapped
91 *
92 * @exception NullPointerException if <code>resolver</code>
93 * is <code>null</code>
94 */
95 public PropertyResolverImpl(PropertyResolver resolver) {
96
97 if (resolver == null) {
98 throw new NullPointerException();
99 }
100 if (log.isDebugEnabled()) {
101 log.debug("Creating new instance, wrapping resolver " +
102 resolver);
103 }
104 this.resolver = resolver;
105
106 }
107
108
109
110
111
112 /***
113 * <p>The <code>Log</code> instance for this class.</p>
114 */
115 private static final Log log =
116 LogFactory.getLog(PropertyResolverImpl.class);
117
118
119 /***
120 * <p>The <code>PropertyResolver</code> instance that we are wrapping,
121 * which will be used to perform operations on beans that are not
122 * recognized by this class.</p>
123 */
124 private PropertyResolver resolver = null;
125
126
127
128
129
130 /***
131 * <p>Return the value of the property with the specified name from
132 * the specified base object.</p>
133 *
134 * @param base The base object whose property value is to be returned
135 * @param name Name of the property to be returned
136 *
137 * @exception NullPointerException if <code>base</code> or
138 * <code>name</code> is <code>null</code>
139 * @exception PropertyNotFoundException if the specified property name
140 * does not exist, or is not readable
141 */
142 public Object getValue(Object base, Object name)
143 throws PropertyNotFoundException {
144
145 if ((base == null) || (name == null)) {
146 throw new NullPointerException();
147 } else if ((base instanceof DynaActionForm) &&
148 ("map".equals(name))) {
149 if (log.isTraceEnabled()) {
150 log.trace("Returning property map for DynaActionForm " + base
151 + "'");
152 }
153 return (((DynaActionForm) base).getMap());
154 } else if (base instanceof DynaBean) {
155 if (getDynaProperty((DynaBean) base, name.toString()) == null) {
156 throw new PropertyNotFoundException(name.toString());
157 }
158 Object value = ((DynaBean) base).get(name.toString());
159 if (log.isTraceEnabled()) {
160 log.trace("Returning dynamic property '" + name +
161 "' for DynaBean '" + base + "' value '" +
162 value + "'");
163 }
164 return (value);
165 } else {
166 Object value = resolver.getValue(base, name);
167 if (log.isTraceEnabled()) {
168 log.trace("Delegating get of property '" + name +
169 "' for bean '" + base + "' value '" +
170 value + "'");
171 }
172 return (value);
173 }
174
175 }
176
177
178 /***
179 * <p>Return the value at the specified index of the specified
180 * base object.</p>
181 *
182 * @param base The base object whose property value is to be returned
183 * @param index Index of the value to return
184 *
185 * @exception IndexOutOfBoundsException if thrown by the underlying
186 * access to the base object
187 * @exception NullPointerException if <code>base</code>
188 * is <code>null</code>
189 * @exception PropertyNotFoundException if some other exception occurs
190 */
191 public Object getValue(Object base, int index)
192 throws PropertyNotFoundException {
193
194 return (resolver.getValue(base, index));
195
196 }
197
198
199 /***
200 * <p>Set the specified value of the property with the specified name on
201 * the specified base object.</p>
202 *
203 * @param base The base object whose property value is to be set
204 * @param name Name of the property to be set
205 * @param value Value of the property to be set
206 *
207 * @exception NullPointerException if <code>base</code> or
208 * <code>name</code> is <code>null</code>
209 * @exception PropertyNotFoundException if the specified property name
210 * does not exist, or is not writeable
211 */
212 public void setValue(Object base, Object name, Object value)
213 throws PropertyNotFoundException {
214
215 if ((base == null) || (name == null)) {
216 throw new NullPointerException();
217 } else if ((base instanceof DynaActionForm) &&
218 ("map".equals(name))) {
219 throw new PropertyNotFoundException(name.toString());
220 } else if (base instanceof DynaBean) {
221 if (log.isTraceEnabled()) {
222 log.trace("setting dynamic property '" + name +
223 "' for DynaBean '" + base +
224 "' to '" + value + "'");
225 }
226 if (getDynaProperty((DynaBean) base, name.toString()) == null) {
227 throw new PropertyNotFoundException(name.toString());
228 }
229 ((DynaBean) base).set(name.toString(), value);
230 } else {
231 if (log.isTraceEnabled()) {
232 log.trace("Delegating set of property '" + name +
233 "' for bean '" + base + "' to value '" +
234 value + "'");
235 }
236 resolver.setValue(base, name, value);
237 }
238
239 }
240
241
242 /***
243 * <p>Set the value at the specified index of the specified
244 * base object.</p>
245 *
246 * @param base The base object whose property value is to be set
247 * @param index Index of the value to set
248 * @param value Value to be set
249 *
250 * @exception IndexOutOfBoundsException if thrown by the underlying
251 * access to the base object
252 * @exception NullPointerException if <code>base</code>
253 * is <code>null</code>
254 * @exception PropertyNotFoundException if some other exception occurs
255 */
256 public void setValue(Object base, int index, Object value)
257 throws PropertyNotFoundException {
258
259 resolver.setValue(base, index, value);
260
261 }
262
263
264 /***
265 * <p>Return <code>true</code> if the specified property of the specified
266 * base object is known to be immutable; otherwise, return
267 * <code>false</code>.</p>
268 *
269 * @param base The base object whose property is to analyzed
270 * @param name Name of the property to be analyzed
271 *
272 * @exception NullPointerException if <code>base</code> or
273 * <code>name</code> is <code>null</code>
274 * @exception PropertyNotFoundException if the specified property name
275 * does not exist
276 */
277 public boolean isReadOnly(Object base, Object name)
278 throws PropertyNotFoundException {
279
280 if ((base == null) || (name == null)) {
281 throw new NullPointerException();
282 } else if ((base instanceof DynaActionForm) &&
283 ("map".equals(name))) {
284 return (true);
285 } else if (base instanceof DynaBean) {
286 if (getDynaProperty((DynaBean) base, name.toString()) == null) {
287 throw new PropertyNotFoundException(name.toString());
288 }
289 return (false);
290 } else {
291 return (resolver.isReadOnly(base, name.toString()));
292 }
293
294 }
295
296
297 /***
298 * <p>Return <code>true</code> if the value at the specified index of
299 * the specified base object is known to be immutable; otherwise,
300 * return <code>false</code>.</p>
301 *
302 * @param base The base object whose property is to analyzed
303 * @param index Index of the value whose type is to be returned
304 *
305 * @exception IndexOutOfBoundsException if thrown by the underlying
306 * accessed to the indexed property
307 * @exception NullPointerException if <code>base</code>
308 * is <code>null</code>
309 * @exception PropertyNotFoundException if some other exception occurs
310 */
311 public boolean isReadOnly(Object base, int index)
312 throws PropertyNotFoundException {
313
314 return (resolver.isReadOnly(base, index));
315
316 }
317
318
319 /***
320 * <p>Return the <code>java.lang.Class</code> representing the type of
321 * the specified property of the specified base object, if it can be
322 * determined; otherwise return <code>null</code>.</p>
323 *
324 * @param base The base object whose property is to analyzed
325 * @param name Name of the property to be analyzed
326 *
327 * @exception NullPointerException if <code>base</code> or
328 * <code>name</code> is <code>null</code>
329 * @exception PropertyNotFoundException if the specified property name
330 * does not exist
331 */
332 public Class getType(Object base, Object name)
333 throws PropertyNotFoundException {
334
335 if ((base == null) || (name == null)) {
336 throw new NullPointerException();
337 } else if ((base instanceof DynaActionForm) &&
338 ("map".equals(name))) {
339 return (Map.class);
340 } else if (base instanceof DynaBean) {
341 DynaProperty dynaProperty =
342 getDynaProperty((DynaBean) base, name.toString());
343 if (dynaProperty != null) {
344 return (dynaProperty.getType());
345 } else {
346 throw new PropertyNotFoundException(name.toString());
347 }
348 } else {
349 return (resolver.getType(base, name));
350 }
351 }
352
353
354 /***
355 * <p>Return the <code>java.lang.Class</code> representing the type of
356 * value at the specified index of the specified base object, or
357 * <code>null</code> if this value is <code>null</code>.</p>
358 *
359 * @param base The base object whose property is to analyzed
360 * @param index Index of the value whose type is to be returned
361 *
362 * @exception IndexOutOfBoundsException if thrown by the underlying
363 * accessed to the indexed property
364 * @exception NullPointerException if <code>base</code>
365 * is <code>null</code>
366 * @exception PropertyNotFoundException if some other exception occurs
367 */
368 public Class getType(Object base, int index)
369 throws PropertyNotFoundException {
370
371 return (resolver.getType(base, index));
372
373 }
374
375
376
377
378
379 /***
380 * <p>Return the <code>DynaProperty</code> describing the specified
381 * property of the specified <code>DynaBean</code>, or <code>null</code>
382 * if there is no such property defined on the underlying
383 * <code>DynaClass</code>.</p>
384 *
385 * @param bean <code>DynaBean</code> to be checked
386 * @param name Name of the property to be checked
387 */
388 private DynaProperty getDynaProperty(DynaBean bean, String name)
389 throws PropertyNotFoundException {
390
391 DynaProperty dynaProperty = null;
392 try {
393 dynaProperty = bean.getDynaClass().getDynaProperty(name);
394 } catch (IllegalArgumentException e) {
395 ;
396 }
397 return (dynaProperty);
398
399 }
400
401
402
403
404 }