View Javadoc

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 }