001: /*
002: * $Id: PropertyResolverImpl.java 471754 2006-11-06 14:55:09Z husted $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: package org.apache.struts.faces.application;
023:
024: import java.util.Map;
025:
026: import javax.faces.el.PropertyNotFoundException;
027: import javax.faces.el.PropertyResolver;
028:
029: import org.apache.commons.beanutils.DynaBean;
030: import org.apache.commons.beanutils.DynaProperty;
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033: import org.apache.struts.action.DynaActionForm;
034:
035: /**
036: * <p>Custom <code>PropertyResolver</code> implementation that adds support
037: * for <code>DynaBean</code> property access to the facilities of the default
038: * <code>PropertyResolver</code> provided by JavaServer Faces.</p>
039: *
040: * <p>This class implements the following specific rules:</p>
041: * <ul>
042: * <li>Indexed variants of each call are directly passed through to the
043: * <code>PropertyResolver</code> instance that we are wrapping.</li>
044: * <li>If the specified base object is an instance of
045: * <code>DynaActionForm</code>, and the requested property name is
046: * <code>map</code>, maintain compatibility with the way that JSP and
047: * JSTL expressions can access this property:
048: * <ul>
049: * <li><code>getValue()</code> will return the result of calling
050: * <code>getMap()</code> on the base object.</li>
051: * <li><code>setValue()</code> will throw an exception, because the
052: * map of property values is a read-only property of the
053: * <code>DynaActionForm</code> class.</li>
054: * <li><code>isReadOnly()</code> returns <code>true</code>.</li>
055: * <li><code>getType()</code> returns the <code>Class</code> object
056: * for <code>java.util.Map</code>.</li>
057: * </ul></li>
058: * <li>If the specified base object is an instance of
059: * <code>DynaBean</code>, provide access to its named properties
060: * as follows:
061: * <ul>
062: * <li><code>getValue()</code> will return the result of calling
063: * <code>get()</code> on the base object.</li>
064: * <li><code>setValue()</code> will call <code>set()</code>
065: * on the base object.</li>
066: * <li><code>isReadOnly()</code> returns <code>false</code> (because
067: * the DynaBean APIs provide no mechanism to make this determination,
068: * but most implementations will provide mutable properties).</li>
069: * <li><code>getType()</code> returns the <code>Class</code> object
070: * for the underlying dynamic property.</li>
071: * </ul></li>
072: * <li>Named variant calls with any other type of base object are
073: * passed through to the <code>PropertyResolver</code> that we
074: * are wrapping.</li>
075: * </ul>
076: *
077: * @version $Rev: 471754 $ $Date: 2006-11-06 08:55:09 -0600 (Mon, 06 Nov 2006) $
078: */
079:
080: public class PropertyResolverImpl extends PropertyResolver {
081:
082: // ------------------------------------------------------------ Constructor
083:
084: /**
085: * <p>Construct a new <code>PropertyResolver</code> instance, wrapping the
086: * specified instance using the Decorator pattern such that this class need
087: * implement only the new functionality it supports, and not need to
088: * re-implement the basic facilities.
089: *
090: * @param resolver The original resolver to be wrapped
091: *
092: * @exception NullPointerException if <code>resolver</code>
093: * is <code>null</code>
094: */
095: public PropertyResolverImpl(PropertyResolver resolver) {
096:
097: if (resolver == null) {
098: throw new NullPointerException();
099: }
100: if (log.isDebugEnabled()) {
101: log.debug("Creating new instance, wrapping resolver "
102: + resolver);
103: }
104: this .resolver = resolver;
105:
106: }
107:
108: // ----------------------------------------------------- Instance Variables
109:
110: /**
111: * <p>The <code>Log</code> instance for this class.</p>
112: */
113: private static final Log log = LogFactory
114: .getLog(PropertyResolverImpl.class);
115:
116: /**
117: * <p>The <code>PropertyResolver</code> instance that we are wrapping,
118: * which will be used to perform operations on beans that are not
119: * recognized by this class.</p>
120: */
121: private PropertyResolver resolver = null;
122:
123: // ----------------------------------------------- PropertyResolver Methods
124:
125: /**
126: * <p>Return the value of the property with the specified name from
127: * the specified base object.</p>
128: *
129: * @param base The base object whose property value is to be returned
130: * @param name Name of the property to be returned
131: *
132: * @exception NullPointerException if <code>base</code> or
133: * <code>name</code> is <code>null</code>
134: * @exception PropertyNotFoundException if the specified property name
135: * does not exist, or is not readable
136: */
137: public Object getValue(Object base, Object name)
138: throws PropertyNotFoundException {
139:
140: if ((base == null) || (name == null)) {
141: throw new NullPointerException();
142: } else if ((base instanceof DynaActionForm)
143: && ("map".equals(name))) {
144: if (log.isTraceEnabled()) {
145: log.trace("Returning property map for DynaActionForm "
146: + base + "'");
147: }
148: return (((DynaActionForm) base).getMap());
149: } else if (base instanceof DynaBean) {
150: if (getDynaProperty((DynaBean) base, name.toString()) == null) {
151: throw new PropertyNotFoundException(name.toString());
152: }
153: Object value = ((DynaBean) base).get(name.toString());
154: if (log.isTraceEnabled()) {
155: log.trace("Returning dynamic property '" + name
156: + "' for DynaBean '" + base + "' value '"
157: + value + "'");
158: }
159: return (value);
160: } else {
161: Object value = resolver.getValue(base, name);
162: if (log.isTraceEnabled()) {
163: log.trace("Delegating get of property '" + name
164: + "' for bean '" + base + "' value '" + value
165: + "'");
166: }
167: return (value);
168: }
169:
170: }
171:
172: /**
173: * <p>Return the value at the specified index of the specified
174: * base object.</p>
175: *
176: * @param base The base object whose property value is to be returned
177: * @param index Index of the value to return
178: *
179: * @exception IndexOutOfBoundsException if thrown by the underlying
180: * access to the base object
181: * @exception NullPointerException if <code>base</code>
182: * is <code>null</code>
183: * @exception PropertyNotFoundException if some other exception occurs
184: */
185: public Object getValue(Object base, int index)
186: throws PropertyNotFoundException {
187:
188: return (resolver.getValue(base, index));
189:
190: }
191:
192: /**
193: * <p>Set the specified value of the property with the specified name on
194: * the specified base object.</p>
195: *
196: * @param base The base object whose property value is to be set
197: * @param name Name of the property to be set
198: * @param value Value of the property to be set
199: *
200: * @exception NullPointerException if <code>base</code> or
201: * <code>name</code> is <code>null</code>
202: * @exception PropertyNotFoundException if the specified property name
203: * does not exist, or is not writeable
204: */
205: public void setValue(Object base, Object name, Object value)
206: throws PropertyNotFoundException {
207:
208: if ((base == null) || (name == null)) {
209: throw new NullPointerException();
210: } else if ((base instanceof DynaActionForm)
211: && ("map".equals(name))) {
212: throw new PropertyNotFoundException(name.toString());
213: } else if (base instanceof DynaBean) {
214: if (log.isTraceEnabled()) {
215: log.trace("setting dynamic property '" + name
216: + "' for DynaBean '" + base + "' to '" + value
217: + "'");
218: }
219: if (getDynaProperty((DynaBean) base, name.toString()) == null) {
220: throw new PropertyNotFoundException(name.toString());
221: }
222: ((DynaBean) base).set(name.toString(), value);
223: } else {
224: if (log.isTraceEnabled()) {
225: log.trace("Delegating set of property '" + name
226: + "' for bean '" + base + "' to value '"
227: + value + "'");
228: }
229: resolver.setValue(base, name, value);
230: }
231:
232: }
233:
234: /**
235: * <p>Set the value at the specified index of the specified
236: * base object.</p>
237: *
238: * @param base The base object whose property value is to be set
239: * @param index Index of the value to set
240: * @param value Value to be set
241: *
242: * @exception IndexOutOfBoundsException if thrown by the underlying
243: * access to the base object
244: * @exception NullPointerException if <code>base</code>
245: * is <code>null</code>
246: * @exception PropertyNotFoundException if some other exception occurs
247: */
248: public void setValue(Object base, int index, Object value)
249: throws PropertyNotFoundException {
250:
251: resolver.setValue(base, index, value);
252:
253: }
254:
255: /**
256: * <p>Return <code>true</code> if the specified property of the specified
257: * base object is known to be immutable; otherwise, return
258: * <code>false</code>.</p>
259: *
260: * @param base The base object whose property is to analyzed
261: * @param name Name of the property to be analyzed
262: *
263: * @exception NullPointerException if <code>base</code> or
264: * <code>name</code> is <code>null</code>
265: * @exception PropertyNotFoundException if the specified property name
266: * does not exist
267: */
268: public boolean isReadOnly(Object base, Object name)
269: throws PropertyNotFoundException {
270:
271: if ((base == null) || (name == null)) {
272: throw new NullPointerException();
273: } else if ((base instanceof DynaActionForm)
274: && ("map".equals(name))) {
275: return (true);
276: } else if (base instanceof DynaBean) {
277: if (getDynaProperty((DynaBean) base, name.toString()) == null) {
278: throw new PropertyNotFoundException(name.toString());
279: }
280: return (false);
281: } else {
282: return (resolver.isReadOnly(base, name.toString()));
283: }
284:
285: }
286:
287: /**
288: * <p>Return <code>true</code> if the value at the specified index of
289: * the specified base object is known to be immutable; otherwise,
290: * return <code>false</code>.</p>
291: *
292: * @param base The base object whose property is to analyzed
293: * @param index Index of the value whose type is to be returned
294: *
295: * @exception IndexOutOfBoundsException if thrown by the underlying
296: * accessed to the indexed property
297: * @exception NullPointerException if <code>base</code>
298: * is <code>null</code>
299: * @exception PropertyNotFoundException if some other exception occurs
300: */
301: public boolean isReadOnly(Object base, int index)
302: throws PropertyNotFoundException {
303:
304: return (resolver.isReadOnly(base, index));
305:
306: }
307:
308: /**
309: * <p>Return the <code>java.lang.Class</code> representing the type of
310: * the specified property of the specified base object, if it can be
311: * determined; otherwise return <code>null</code>.</p>
312: *
313: * @param base The base object whose property is to analyzed
314: * @param name Name of the property to be analyzed
315: *
316: * @exception NullPointerException if <code>base</code> or
317: * <code>name</code> is <code>null</code>
318: * @exception PropertyNotFoundException if the specified property name
319: * does not exist
320: */
321: public Class getType(Object base, Object name)
322: throws PropertyNotFoundException {
323:
324: if ((base == null) || (name == null)) {
325: throw new NullPointerException();
326: } else if ((base instanceof DynaActionForm)
327: && ("map".equals(name))) {
328: return (Map.class);
329: } else if (base instanceof DynaBean) {
330: DynaProperty dynaProperty = getDynaProperty(
331: (DynaBean) base, name.toString());
332: if (dynaProperty != null) {
333: return (dynaProperty.getType());
334: } else {
335: throw new PropertyNotFoundException(name.toString());
336: }
337: } else {
338: return (resolver.getType(base, name));
339: }
340: }
341:
342: /**
343: * <p>Return the <code>java.lang.Class</code> representing the type of
344: * value at the specified index of the specified base object, or
345: * <code>null</code> if this value is <code>null</code>.</p>
346: *
347: * @param base The base object whose property is to analyzed
348: * @param index Index of the value whose type is to be returned
349: *
350: * @exception IndexOutOfBoundsException if thrown by the underlying
351: * accessed to the indexed property
352: * @exception NullPointerException if <code>base</code>
353: * is <code>null</code>
354: * @exception PropertyNotFoundException if some other exception occurs
355: */
356: public Class getType(Object base, int index)
357: throws PropertyNotFoundException {
358:
359: return (resolver.getType(base, index));
360:
361: }
362:
363: // -------------------------------------------------------- Private Methods
364:
365: /**
366: * <p>Return the <code>DynaProperty</code> describing the specified
367: * property of the specified <code>DynaBean</code>, or <code>null</code>
368: * if there is no such property defined on the underlying
369: * <code>DynaClass</code>.</p>
370: *
371: * @param bean <code>DynaBean</code> to be checked
372: * @param name Name of the property to be checked
373: */
374: private DynaProperty getDynaProperty(DynaBean bean, String name)
375: throws PropertyNotFoundException {
376:
377: DynaProperty dynaProperty = null;
378: try {
379: dynaProperty = bean.getDynaClass().getDynaProperty(name);
380: } catch (IllegalArgumentException e) {
381: ;
382: }
383: return (dynaProperty);
384:
385: }
386:
387: }
|