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