/*
* $Id: PropertyResolverImpl.java 471754 2006-11-06 14:55:09Z husted $
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.struts.faces.application;
import java.util.Map;
import javax.faces.el.PropertyNotFoundException;
import javax.faces.el.PropertyResolver;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.DynaActionForm;
/**
* <p>Custom <code>PropertyResolver</code> implementation that adds support
* for <code>DynaBean</code> property access to the facilities of the default
* <code>PropertyResolver</code> provided by JavaServer Faces.</p>
*
* <p>This class implements the following specific rules:</p>
* <ul>
* <li>Indexed variants of each call are directly passed through to the
* <code>PropertyResolver</code> instance that we are wrapping.</li>
* <li>If the specified base object is an instance of
* <code>DynaActionForm</code>, and the requested property name is
* <code>map</code>, maintain compatibility with the way that JSP and
* JSTL expressions can access this property:
* <ul>
* <li><code>getValue()</code> will return the result of calling
* <code>getMap()</code> on the base object.</li>
* <li><code>setValue()</code> will throw an exception, because the
* map of property values is a read-only property of the
* <code>DynaActionForm</code> class.</li>
* <li><code>isReadOnly()</code> returns <code>true</code>.</li>
* <li><code>getType()</code> returns the <code>Class</code> object
* for <code>java.util.Map</code>.</li>
* </ul></li>
* <li>If the specified base object is an instance of
* <code>DynaBean</code>, provide access to its named properties
* as follows:
* <ul>
* <li><code>getValue()</code> will return the result of calling
* <code>get()</code> on the base object.</li>
* <li><code>setValue()</code> will call <code>set()</code>
* on the base object.</li>
* <li><code>isReadOnly()</code> returns <code>false</code> (because
* the DynaBean APIs provide no mechanism to make this determination,
* but most implementations will provide mutable properties).</li>
* <li><code>getType()</code> returns the <code>Class</code> object
* for the underlying dynamic property.</li>
* </ul></li>
* <li>Named variant calls with any other type of base object are
* passed through to the <code>PropertyResolver</code> that we
* are wrapping.</li>
* </ul>
*
* @version $Rev: 471754 $ $Date: 2006-11-06 15:55:09 +0100 (Lun, 06 nov 2006) $
*/
public class PropertyResolverImpl extends PropertyResolver {
// ------------------------------------------------------------ Constructor
/**
* <p>Construct a new <code>PropertyResolver</code> instance, wrapping the
* specified instance using the Decorator pattern such that this class need
* implement only the new functionality it supports, and not need to
* re-implement the basic facilities.
*
* @param resolver The original resolver to be wrapped
*
* @exception NullPointerException if <code>resolver</code>
* is <code>null</code>
*/
public PropertyResolverImpl(PropertyResolver resolver) {
if (resolver == null) {
throw new NullPointerException();
}
if (log.isDebugEnabled()) {
log.debug("Creating new instance, wrapping resolver " +
resolver);
}
this.resolver = resolver;
}
// ----------------------------------------------------- Instance Variables
/**
* <p>The <code>Log</code> instance for this class.</p>
*/
private static final Log log =
LogFactory.getLog(PropertyResolverImpl.class);
/**
* <p>The <code>PropertyResolver</code> instance that we are wrapping,
* which will be used to perform operations on beans that are not
* recognized by this class.</p>
*/
private PropertyResolver resolver = null;
// ----------------------------------------------- PropertyResolver Methods
/**
* <p>Return the value of the property with the specified name from
* the specified base object.</p>
*
* @param base The base object whose property value is to be returned
* @param name Name of the property to be returned
*
* @exception NullPointerException if <code>base</code> or
* <code>name</code> is <code>null</code>
* @exception PropertyNotFoundException if the specified property name
* does not exist, or is not readable
*/
public Object getValue(Object base, Object name)
throws PropertyNotFoundException {
if ((base == null) || (name == null)) {
throw new NullPointerException();
} else if ((base instanceof DynaActionForm) &&
("map".equals(name))) {
if (log.isTraceEnabled()) {
log.trace("Returning property map for DynaActionForm " + base
+ "'");
}
return (((DynaActionForm) base).getMap());
} else if (base instanceof DynaBean) {
if (getDynaProperty((DynaBean) base, name.toString()) == null) {
throw new PropertyNotFoundException(name.toString());
}
Object value = ((DynaBean) base).get(name.toString());
if (log.isTraceEnabled()) {
log.trace("Returning dynamic property '" + name +
"' for DynaBean '" + base + "' value '" +
value + "'");
}
return (value);
} else {
Object value = resolver.getValue(base, name);
if (log.isTraceEnabled()) {
log.trace("Delegating get of property '" + name +
"' for bean '" + base + "' value '" +
value + "'");
}
return (value);
}
}
/**
* <p>Return the value at the specified index of the specified
* base object.</p>
*
* @param base The base object whose property value is to be returned
* @param index Index of the value to return
*
* @exception IndexOutOfBoundsException if thrown by the underlying
* access to the base object
* @exception NullPointerException if <code>base</code>
* is <code>null</code>
* @exception PropertyNotFoundException if some other exception occurs
*/
public Object getValue(Object base, int index)
throws PropertyNotFoundException {
return (resolver.getValue(base, index));
}
/**
* <p>Set the specified value of the property with the specified name on
* the specified base object.</p>
*
* @param base The base object whose property value is to be set
* @param name Name of the property to be set
* @param value Value of the property to be set
*
* @exception NullPointerException if <code>base</code> or
* <code>name</code> is <code>null</code>
* @exception PropertyNotFoundException if the specified property name
* does not exist, or is not writeable
*/
public void setValue(Object base, Object name, Object value)
throws PropertyNotFoundException {
if ((base == null) || (name == null)) {
throw new NullPointerException();
} else if ((base instanceof DynaActionForm) &&
("map".equals(name))) {
throw new PropertyNotFoundException(name.toString());
} else if (base instanceof DynaBean) {
if (log.isTraceEnabled()) {
log.trace("setting dynamic property '" + name +
"' for DynaBean '" + base +
"' to '" + value + "'");
}
if (getDynaProperty((DynaBean) base, name.toString()) == null) {
throw new PropertyNotFoundException(name.toString());
}
((DynaBean) base).set(name.toString(), value);
} else {
if (log.isTraceEnabled()) {
log.trace("Delegating set of property '" + name +
"' for bean '" + base + "' to value '" +
value + "'");
}
resolver.setValue(base, name, value);
}
}
/**
* <p>Set the value at the specified index of the specified
* base object.</p>
*
* @param base The base object whose property value is to be set
* @param index Index of the value to set
* @param value Value to be set
*
* @exception IndexOutOfBoundsException if thrown by the underlying
* access to the base object
* @exception NullPointerException if <code>base</code>
* is <code>null</code>
* @exception PropertyNotFoundException if some other exception occurs
*/
public void setValue(Object base, int index, Object value)
throws PropertyNotFoundException {
resolver.setValue(base, index, value);
}
/**
* <p>Return <code>true</code> if the specified property of the specified
* base object is known to be immutable; otherwise, return
* <code>false</code>.</p>
*
* @param base The base object whose property is to analyzed
* @param name Name of the property to be analyzed
*
* @exception NullPointerException if <code>base</code> or
* <code>name</code> is <code>null</code>
* @exception PropertyNotFoundException if the specified property name
* does not exist
*/
public boolean isReadOnly(Object base, Object name)
throws PropertyNotFoundException {
if ((base == null) || (name == null)) {
throw new NullPointerException();
} else if ((base instanceof DynaActionForm) &&
("map".equals(name))) {
return (true);
} else if (base instanceof DynaBean) {
if (getDynaProperty((DynaBean) base, name.toString()) == null) {
throw new PropertyNotFoundException(name.toString());
}
return (false);
} else {
return (resolver.isReadOnly(base, name.toString()));
}
}
/**
* <p>Return <code>true</code> if the value at the specified index of
* the specified base object is known to be immutable; otherwise,
* return <code>false</code>.</p>
*
* @param base The base object whose property is to analyzed
* @param index Index of the value whose type is to be returned
*
* @exception IndexOutOfBoundsException if thrown by the underlying
* accessed to the indexed property
* @exception NullPointerException if <code>base</code>
* is <code>null</code>
* @exception PropertyNotFoundException if some other exception occurs
*/
public boolean isReadOnly(Object base, int index)
throws PropertyNotFoundException {
return (resolver.isReadOnly(base, index));
}
/**
* <p>Return the <code>java.lang.Class</code> representing the type of
* the specified property of the specified base object, if it can be
* determined; otherwise return <code>null</code>.</p>
*
* @param base The base object whose property is to analyzed
* @param name Name of the property to be analyzed
*
* @exception NullPointerException if <code>base</code> or
* <code>name</code> is <code>null</code>
* @exception PropertyNotFoundException if the specified property name
* does not exist
*/
public Class getType(Object base, Object name)
throws PropertyNotFoundException {
if ((base == null) || (name == null)) {
throw new NullPointerException();
} else if ((base instanceof DynaActionForm) &&
("map".equals(name))) {
return (Map.class);
} else if (base instanceof DynaBean) {
DynaProperty dynaProperty =
getDynaProperty((DynaBean) base, name.toString());
if (dynaProperty != null) {
return (dynaProperty.getType());
} else {
throw new PropertyNotFoundException(name.toString());
}
} else {
return (resolver.getType(base, name));
}
}
/**
* <p>Return the <code>java.lang.Class</code> representing the type of
* value at the specified index of the specified base object, or
* <code>null</code> if this value is <code>null</code>.</p>
*
* @param base The base object whose property is to analyzed
* @param index Index of the value whose type is to be returned
*
* @exception IndexOutOfBoundsException if thrown by the underlying
* accessed to the indexed property
* @exception NullPointerException if <code>base</code>
* is <code>null</code>
* @exception PropertyNotFoundException if some other exception occurs
*/
public Class getType(Object base, int index)
throws PropertyNotFoundException {
return (resolver.getType(base, index));
}
// -------------------------------------------------------- Private Methods
/**
* <p>Return the <code>DynaProperty</code> describing the specified
* property of the specified <code>DynaBean</code>, or <code>null</code>
* if there is no such property defined on the underlying
* <code>DynaClass</code>.</p>
*
* @param bean <code>DynaBean</code> to be checked
* @param name Name of the property to be checked
*/
private DynaProperty getDynaProperty(DynaBean bean, String name)
throws PropertyNotFoundException {
DynaProperty dynaProperty = null;
try {
dynaProperty = bean.getDynaClass().getDynaProperty(name);
} catch (IllegalArgumentException e) {
;
}
return (dynaProperty);
}
}