View Javadoc

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  
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      // ------------------------------------------------------------ Constructor
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     // ----------------------------------------------------- Instance Variables
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     // ----------------------------------------------- PropertyResolver Methods
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     // -------------------------------------------------------- Private Methods
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 }