1 /** 2 * Licensed under the Artistic License; you may not use this file 3 * except in compliance with the License. 4 * You may obtain a copy of the License at 5 * 6 * http://displaytag.sourceforge.net/license.html 7 * 8 * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 10 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 11 */ 12 package org.displaytag.decorator; 13 14 import java.lang.reflect.InvocationTargetException; 15 import java.util.HashMap; 16 import java.util.Map; 17 18 import javax.servlet.jsp.PageContext; 19 20 import org.apache.commons.beanutils.PropertyUtils; 21 import org.apache.commons.lang.BooleanUtils; 22 import org.displaytag.model.TableModel; 23 24 25 /** 26 * <p> 27 * This class provides some basic functionality for all objects which serve as decorators for the objects in the List 28 * being displayed. 29 * <p> 30 * <p> 31 * Decorator should never be subclassed directly. Use TableDecorator instead 32 * </p> 33 * @author mraible 34 * @author Fabrizio Giustina 35 * @version $Revision: 1084 $ ($Author: fgiust $) 36 */ 37 abstract class Decorator 38 { 39 40 /** 41 * Char used to separate class name and property in the cache key. 42 */ 43 private static final char CLASS_PROPERTY_SEPARATOR = '#'; 44 45 /** 46 * property info cache contains classname#propertyname Strings as keys and Booleans as values. 47 */ 48 private static Map propertyMap = new HashMap(); 49 50 /** 51 * page context. 52 */ 53 private PageContext pageContext; 54 55 /** 56 * decorated object. Usually a List 57 */ 58 private Object decoratedObject; 59 60 /** 61 * The table model. 62 * @since 1.1 63 */ 64 protected TableModel tableModel; 65 66 /** 67 * Initialize the TableTecorator instance. 68 * @param pageContext PageContext 69 * @param decorated decorated object (usually a list) 70 * @deprecated use #init(PageContext, Object, TableModel) 71 * @see #init(PageContext, Object, TableModel) 72 */ 73 public void init(PageContext pageContext, Object decorated) 74 { 75 this.pageContext = pageContext; 76 this.decoratedObject = decorated; 77 } 78 79 /** 80 * Initialize the TableTecorator instance. 81 * @param pageContext PageContext 82 * @param decorated decorated object (usually a list) 83 * @param tableModel table model 84 */ 85 public void init(PageContext pageContext, Object decorated, TableModel tableModel) 86 { 87 // temporary used for backward (source) compatibility 88 init(pageContext, decorated); 89 this.tableModel = tableModel; 90 } 91 92 /** 93 * returns the page context. 94 * @return PageContext 95 */ 96 public PageContext getPageContext() 97 { 98 return this.pageContext; 99 } 100 101 /** 102 * returns the decorated object. 103 * @return Object 104 */ 105 public Object getDecoratedObject() 106 { 107 return this.decoratedObject; 108 } 109 110 /** 111 * Called at the end of evaluation to clean up instance variable. A subclass of Decorator can override this method 112 * but should always call super.finish() before return 113 */ 114 public void finish() 115 { 116 this.pageContext = null; 117 this.decoratedObject = null; 118 } 119 120 /** 121 * Check if a getter exists for a given property. Uses cached info if property has already been requested. This 122 * method only check for a simple property, if pPropertyName contains multiple tokens only the first part is 123 * evaluated 124 * @param propertyName name of the property to check 125 * @return boolean true if the decorator has a getter for the given property 126 */ 127 public boolean hasGetterFor(String propertyName) 128 { 129 String simpleProperty = propertyName; 130 131 // get the simple (not nested) bean property 132 int indexOfDot = simpleProperty.indexOf('.'); 133 if (indexOfDot > 0) 134 { 135 simpleProperty = simpleProperty.substring(0, indexOfDot); 136 } 137 138 Boolean cachedResult = (Boolean) propertyMap.get(getClass().getName() 139 + CLASS_PROPERTY_SEPARATOR 140 + simpleProperty); 141 142 if (cachedResult != null) 143 { 144 return cachedResult.booleanValue(); 145 } 146 147 // not already cached... check 148 boolean hasGetter = searchGetterFor(propertyName); 149 150 // save in cache 151 propertyMap.put(getClass().getName() + CLASS_PROPERTY_SEPARATOR + simpleProperty, BooleanUtils 152 .toBooleanObject(hasGetter)); 153 154 // and return 155 return hasGetter; 156 157 } 158 159 /** 160 * Looks for a getter for the given property using introspection. 161 * @param propertyName name of the property to check 162 * @return boolean true if the decorator has a getter for the given property 163 */ 164 public boolean searchGetterFor(String propertyName) 165 { 166 167 Class type = null; 168 169 try 170 { 171 // using getPropertyType instead of isReadable since isReadable doesn't support mapped properties. 172 // Note that this method usually returns null if a property is not found and doesn't throw any exception 173 // also for non existent properties 174 type = PropertyUtils.getPropertyType(this, propertyName); 175 } 176 catch (IllegalAccessException e) 177 { 178 // ignore 179 } 180 catch (InvocationTargetException e) 181 { 182 // ignore 183 } 184 catch (NoSuchMethodException e) 185 { 186 // ignore 187 } 188 189 return type != null; 190 191 } 192 193 }