/** * Licensed under the Artistic License; you may not use this file * except in compliance with the License. * You may obtain a copy of the License at * * http://displaytag.sourceforge.net/license.html * * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ package org.displaytag.decorator; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; import javax.servlet.jsp.PageContext; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.lang.BooleanUtils; import org.displaytag.model.TableModel; /** * <p> * This class provides some basic functionality for all objects which serve as decorators for the objects in the List * being displayed. * <p> * <p> * Decorator should never be subclassed directly. Use TableDecorator instead * </p> * @author mraible * @author Fabrizio Giustina * @version $Revision: 8904 $ ($Author: charles $) */ abstract class Decorator { /** * Char used to separate class name and property in the cache key. */ private static final char CLASS_PROPERTY_SEPARATOR = '#'; /** * property info cache contains classname#propertyname Strings as keys and Booleans as values. */ private static Map propertyMap = new HashMap(); /** * page context. */ private PageContext pageContext; /** * decorated object. Usually a List */ private Object decoratedObject; /** * The table model. * @since 1.1 */ protected TableModel tableModel; /** * Initialize the TableTecorator instance. * @param context PageContext * @param decorated decorated object (usually a list) * @deprecated use #init(PageContext, Object, TableModel) * @see #init(PageContext, Object, TableModel) */ public void init(PageContext context, Object decorated) { this.pageContext = context; this.decoratedObject = decorated; } /** * Initialize the TableTecorator instance. * @param context PageContext * @param decorated decorated object (usually a list) * @param tableModel table model */ public void init(PageContext context, Object decorated, TableModel tableModel) { // temporary used for backward (source) compatibility init(pageContext, decorated); // this.pageContext = context; // this.decoratedObject = decorated; this.tableModel = tableModel; } /** * returns the page context. * @return PageContext */ public PageContext getPageContext() { return this.pageContext; } /** * returns the decorated object. * @return Object */ public Object getDecoratedObject() { return this.decoratedObject; } /** * Called at the end of evaluation to clean up instance variable. A subclass of Decorator can override this method * but should always call super.finish() before return */ public void finish() { this.pageContext = null; this.decoratedObject = null; } /** * Check if a getter exists for a given property. Uses cached info if property has already been requested. This * method only check for a simple property, if pPropertyName contains multiple tokens only the first part is * evaluated * @param propertyName name of the property to check * @return boolean true if the decorator has a getter for the given property */ public boolean hasGetterFor(String propertyName) { String simpleProperty = propertyName; // get the simple (not nested) bean property int indexOfDot = simpleProperty.indexOf('.'); if (indexOfDot > 0) { simpleProperty = simpleProperty.substring(0, indexOfDot); } Boolean cachedResult = (Boolean) propertyMap.get(getClass().getName() + CLASS_PROPERTY_SEPARATOR + simpleProperty); if (cachedResult != null) { return cachedResult.booleanValue(); } // not already cached... check boolean hasGetter = searchGetterFor(propertyName); // save in cache propertyMap.put(getClass().getName() + CLASS_PROPERTY_SEPARATOR + simpleProperty, BooleanUtils .toBooleanObject(hasGetter)); // and return return hasGetter; } /** * Looks for a getter for the given property using introspection. * @param propertyName name of the property to check * @return boolean true if the decorator has a getter for the given property */ public boolean searchGetterFor(String propertyName) { Class type = null; try { // using getPropertyType instead of isReadable since isReadable doesn't support mapped properties. // Note that this method usually returns null if a property is not found and doesn't throw any exception // also for non existent properties type = PropertyUtils.getPropertyType(this, propertyName); } catch (IllegalAccessException e) { // ignore } catch (InvocationTargetException e) { // ignore } catch (NoSuchMethodException e) { // ignore } return type != null; } }