View Javadoc

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