All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.struts.faces.application.DynaBeanELResolver Maven / Gradle / Ivy

There is a newer version: 1.5.0-RC2
Show newest version
/*
 * $Id$
 *
 * 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.beans.FeatureDescriptor;
import java.util.Arrays;
import java.util.Iterator;

import javax.el.CompositeELResolver;
import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ELResolver;
import javax.el.PropertyNotFoundException;
import javax.el.PropertyNotWritableException;

import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;



/**
 * Defines property resolution behavior on instances of {@link DynaBean}.
 *
 * 

This resolver handles base objects of type {@code DynaBean}. * It accepts any object as a property and uses that object as a key in * the map. The resulting value is the value in the map that is associated with * that key.

* *

This resolver can be constructed in read-only mode, which means that * {@link #isReadOnly} will always return {@code true} and {@link #setValue} * will always throw {@code PropertyNotWritableException}.

* *

{@code ELResolver}s are combined together using * {@link CompositeELResolver}s, to define rich semantics for evaluating * an expression. See the JavaDocs for {@link ELResolver} for details.

* * @see CompositeELResolver * @see ELResolver * @see DynaBean * @since Struts 1.4.1 */ public class DynaBeanELResolver extends ELResolver { /** * The {@code Log} instance for this class. */ private static final Log LOG = LogFactory.getLog(DynaBeanELResolver.class); /** * Flag if this {@code ELRsolver} is in read-only-mode {@code true}. */ private final boolean readOnly; /** * Creates a new read/write {@code DynaBeanELResolver}. */ public DynaBeanELResolver() { this(false); } /** * Creates a new {@code DynaBeanELResolver} whose read-only status is * determined by the given parameter. * * @param readOnly {@code true} if this resolver cannot modify * properties; {@code false} otherwise. */ public DynaBeanELResolver(boolean readOnly) { if (LOG.isDebugEnabled()) { LOG.debug("Creating new Dyna-Action-From-ELResolver " + "instance with read-only: '" + readOnly + "'"); } this.readOnly = readOnly; } /** * If the base object is a {@code DynaBean}, returns the most general * acceptable type for a value in this map.. * *

If the base is a {@code DynaBean}, the {@code propertyResolved} * property of the {@code ELContext} object must be set to {@code true} * by this resolver, before returning. If this property is not * {@code true} after this method is called, the caller should ignore * the return value.

* *

Assuming the base is a {@code DynaBean}, this method will always * return the Java class representing the data type of the underlying * property value.

* * @param context The context of this evaluation. * @param base The base to analyze. Only bases of type * {@code DynaBean} are handled by this resolver. * @param property The key to return the acceptable type for. * * @return If the {@code propertyResolved} property of {@code ELContext} * was set to {@code true}, then the most general acceptable type; * otherwise undefined. * * @throws NullPointerException if context is {@code null} * @throws PropertyNotFoundException if the given property name is * not found. * @throws ELException if an exception was thrown while performing * the property or variable resolution. The thrown exception * must be included as the cause property of this exception, if * available. */ @Override public Class getType(ELContext context, Object base, Object property) { if (context == null) { throw new NullPointerException(); } if (base instanceof DynaBean) { if (LOG.isTraceEnabled() ) { LOG.trace("Returning property-type '" + property + "' for DynaBean '" + base + "'"); } final DynaBean dynaBean = (DynaBean) base; final String key = property.toString(); final DynaProperty dynaProperty = getDynaProperty(dynaBean, key); if (dynaProperty == null) { throw new PropertyNotFoundException(key); } context.setPropertyResolved(true); return dynaProperty.getType(); } return null; } /** * If the base object is a {@code DynaBean}, returns the value associated * with the given key, as specified by the {@code property} argument. If * the key was not found, {@code PropertyNotFoundException} is thrown. * *

If the base is a {@code DynaBean}, the {@code propertyResolved} * property of the {@code ELContext} object must be set to {@code true} * by this resolver, before returning. If this property is not * {@code true} after this method is called, the caller should ignore * the return value.

* *

Just as in {@link DynaBean#get(String)}, just because {@code null} * is returned doesn't mean there is no mapping for the key; it's also * possible that the method explicitly returns {@code null}.

* * @param context The context of this evaluation. * @param base The base to be analyzed. Only bases of type * {@code DynaBean} are handled by this resolver. * @param property The key whose associated value is to be returned. * * @return If the {@code propertyResolved} property of {@code ELContext} * was set to {@code true}, then the value associated with the given key. * * @throws NullPointerException if context is {@code null}. * @throws PropertyNotFoundException if the given property does not * exists. * @throws ELException if an exception was thrown while performing * the property or variable resolution. The thrown exception * must be included as the cause property of this exception, if * available. */ @Override public Object getValue(ELContext context, Object base, Object property) { if (context == null) { throw new NullPointerException(); } if (base instanceof DynaBean) { if (LOG.isTraceEnabled()) { LOG.trace("Returning dynamic property '" + property + "' for DynaBean '" + base + "'"); } final DynaBean dynaBean = (DynaBean) base; final String key = property.toString(); final DynaProperty dynaProperty = getDynaProperty(dynaBean, key); if (dynaProperty == null) { throw new PropertyNotFoundException(key); } context.setPropertyResolved(true); return dynaBean.get(key); } return null; } /** * If the base is a {@code DynaBean}, attempts to set the value * associated with the given key, as specified by the * {@code property} argument. * *

If the base is a {@code DynaBean}, the {@code propertyResolved} * property of the {@code ELContext} object must be set to {@code true} * by this resolver, before returning. If this property is not * {@code true} after this method is called, the caller can safely * assume no value was set.

* *

If this resolver was constructed in read-only mode, this method will * always throw {@code PropertyNotWritableException}.

* * @param context The context of this evaluation. * @param base The base to be modified. Only bases of type * {@code DynaBean} are handled by this resolver. * @param property The key with which the specified value is to be * associated. * @param value The value to be associated with the specified key. * @throws NullPointerException if context is {@code null} or if * an attempt is made to set a primitive property to {@code null}. * @throws ConversionException if the specified value cannot be * converted to the type required for this property. * @throws PropertyNotFoundException if the given property does not * exists. * @throws PropertyNotWritableException if this resolver was constructed * in read-only mode. * @throws ELException if an exception was thrown while performing * the property or variable resolution. The thrown exception * must be included as the cause property of this exception, if * available. */ @Override public void setValue(ELContext context, Object base, Object property, Object value) { if (context == null) { throw new NullPointerException(); } if (base instanceof DynaBean) { if (LOG.isTraceEnabled()) { LOG.trace("Setting dynamic property '" + property + "' for DynaBean '" + base + "'"); } final DynaBean dynaBean = (DynaBean) base; final String key = property.toString(); final DynaProperty dynaProperty = getDynaProperty(dynaBean, key); if (dynaProperty == null) { throw new PropertyNotFoundException(key); } if (readOnly) { throw new PropertyNotWritableException(); } context.setPropertyResolved(true); dynaBean.set(key, value); } } /** * If the base object is a {@code DynaBean}, returns whether a call to * {@link #setValue} will always fail. * *

If the base is a {@code DynaBean}, the {@code propertyResolved} * property of the {@code ELContext} object must be set to {@code true} * by this resolver, before returning. If this property is not * {@code true} after this method is called, the caller should ignore * the return value.

* *

If this resolver was constructed in read-only mode, this method will * always return {@code true}.

* * @param context The context of this evaluation. * @param base The base to analyze. Only bases of type {@code DynaBean} * are handled by this resolver. * @param property The key to return the read-only status for. * * @return If the {@code propertyResolved} property of {@code ELContext} * was set to {@code true}, then {@code true} if calling the * {@code setValue} method will always fail or {@code false} if it * is possible that such a call may succeed; otherwise undefined. * * @throws NullPointerException if context is {@code null}. * @throws PropertyNotFoundException if the given property does not * exists. * @throws ELException if an exception was thrown while performing * the property or variable resolution. The thrown exception * must be included as the cause property of this exception, if * available. */ @Override public boolean isReadOnly(ELContext context, Object base, Object property) { if (context == null) { throw new NullPointerException(); } if (base instanceof DynaBean) { if (LOG.isTraceEnabled()) { LOG.trace("Return ready-only status for dynamic property '" + property + "' for DynaBean " + base + "'"); } final DynaBean dynaBean = (DynaBean) base; final String key = property.toString(); final DynaProperty dynaProperty = getDynaProperty(dynaBean, key); if (dynaProperty == null) { throw new PropertyNotFoundException(key); } context.setPropertyResolved(true); return readOnly; } return false; } /** * Returns information about the set of variables or properties that * can be resolved for the given {@code base} object. One use for * this method is to assist tools in auto-completion. * *

If the {@code base} parameter is {@code null}, the resolver * must enumerate the list of top-level variables it can resolve.

* *

The {@code Iterator} returned must contain zero or more * instances of {@link FeatureDescriptor}, in no guaranteed * order. Each info object contains information about a property * in the {@code DynaBean}, as obtained by calling the * {@link org.apache.commons.beanutils.DynaClass#getDynaProperties()} * method. The {@code FeatureDescriptor} is initialized using the same * fields as are present in the {@code DynaProperty}, with the * additional required named attributes "{@code type}" and * "{@code resolvableAtDesignTime}" set as follows:

*
    *
  • {@link ELResolver#TYPE} - The runtime type of the property, from * {link org.apache.commons.beanutils.DynaProperty#getType()}.
  • *
  • {@link ELResolver#RESOLVABLE_AT_DESIGN_TIME} - {@code true}.
  • *
* *

The caller should be aware that the {@code Iterator} * returned might iterate through a very large or even infinitely large * set of properties. Care should be taken by the caller to not get * stuck in an infinite loop.

* *

This is a "best-effort" list. Not all {@code ELResolver}s * will return completely accurate results, but all must be callable * at both design-time and runtime (i.e. whether or not * {@link java.beans.Beans#isDesignTime()} returns {@code true}), * without causing errors.

* *

The {@code propertyResolved} property of the * {@code ELContext} is not relevant to this method. * The results of all {@code ELResolver}s are concatenated * in the case of composite resolvers.

* * @param context The context of this evaluation. * @param base The base object whose set of valid properties is to * be enumerated, or {@code null} to enumerate the set of * top-level variables that this resolver can evaluate. * @return An {@code Iterator} containing zero or more (possibly * infinitely more) {@code FeatureDescriptor} objects, or * {@code null} if this resolver does not handle the given * {@code base} object or that the results are too complex to * represent with this method * @see java.beans.FeatureDescriptor */ @Override public Iterator getFeatureDescriptors( ELContext context, Object base) { if (base instanceof DynaBean) { if (LOG.isTraceEnabled()) { LOG.trace("Get Feature-Descriptors for DynaBean '" + base + "'"); } final DynaBean dynaBean = (DynaBean) base; final DynaProperty[] properties = dynaBean.getDynaClass().getDynaProperties(); final int iMax = properties.length; final FeatureDescriptor[] descriptors = new FeatureDescriptor[iMax]; for (int i = 0; i < iMax; i++) { final DynaProperty property = properties[i]; final FeatureDescriptor descriptor = new FeatureDescriptor(); descriptor.setName(property.getName()); descriptor.setDisplayName(property.getName()); descriptor.setExpert(false); descriptor.setHidden(false); descriptor.setPreferred(true); descriptor.setShortDescription(null); descriptor.setValue(TYPE, property.getType()); descriptor.setValue(RESOLVABLE_AT_DESIGN_TIME, Boolean.TRUE); descriptors[i] = descriptor; } return Arrays.asList(descriptors).iterator(); } return null; } /** * If the base object is a {@code DynaBean}, returns the most * general type that this resolver accepts for the {@code property} * argument. Otherwise, returns {@code null}. * *

Assuming the base is a {@code DynaBean}, this method will * always return {@code Object.class}. This is because any object is * accepted as a key and is coerced into a string.

* * @param context The context of this evaluation. * @param base The base to analyze. Only bases of type * {@code DynaBean} are handled by this resolver. * * @return {@code null} if base is not a {@code DynaBean}; otherwise * {@code Object.class}. */ @Override public Class getCommonPropertyType(ELContext context, Object base) { if (base instanceof DynaBean) { if (LOG.isTraceEnabled()) { LOG.trace("Get Common-Property-Type for DynaBean '" + base + "'"); } return Object.class; } return null; } /** * Return the {@code DynaProperty} describing the specified property * of the specified {@code DynaBean}, or {@code null} if there is no * such property defined on the underlying {@code DynaClass}. * * @param bean {@code DynaBean} to be checked * @param property The property to be checked */ private DynaProperty getDynaProperty(DynaBean bean, String property) throws PropertyNotFoundException { DynaProperty dynaProperty = null; try { dynaProperty = bean.getDynaClass().getDynaProperty(property); } catch (IllegalArgumentException e) { if (LOG.isTraceEnabled() ) { LOG.trace("Get Dyna-Property '" + property + "'", e); } } return (dynaProperty); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy