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 }