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.util;
13
14 import java.lang.reflect.InvocationTargetException;
15 import java.util.List;
16 import java.util.Map;
17
18 import javax.servlet.jsp.PageContext;
19
20 import org.apache.commons.beanutils.NestedNullException;
21 import org.apache.commons.beanutils.PropertyUtils;
22 import org.apache.commons.lang.StringUtils;
23 import org.apache.commons.lang.Validate;
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.displaytag.exception.ObjectLookupException;
27
28
29 /**
30 * Utility class with methods for object and properties retrieving.
31 * @author Fabrizio Giustina
32 * @version $Id: LookupUtil.java 1081 2006-04-03 20:26:34Z fgiust $
33 */
34 public final class LookupUtil
35 {
36
37 /**
38 * logger.
39 */
40 private static Log log = LogFactory.getLog(LookupUtil.class);
41
42 /**
43 * don't instantiate a LookupUtil.
44 */
45 private LookupUtil()
46 {
47 // unused
48 }
49
50 /**
51 * Read an object from the pagecontext with the specified scope and eventually lookup a property in it.
52 * @param pageContext PageContext
53 * @param beanAndPropertyName String expression with bean name and attributes
54 * @param scope One of the following values:
55 * <ul>
56 * <li>PageContext.PAGE_SCOPE</li>
57 * <li>PageContext.REQUEST_SCOPE</li>
58 * <li>PageContext.SESSION_SCOPE</li>
59 * <li>PageContext.APPLICATION_SCOPE</li>
60 * </ul>
61 * @return Object
62 * @throws ObjectLookupException for errors while retrieving a property in the bean
63 */
64 public static Object getBeanValue(PageContext pageContext, String beanAndPropertyName, int scope)
65 throws ObjectLookupException
66 {
67
68 if (beanAndPropertyName.indexOf('.') != -1)
69 {
70 // complex: property from a bean
71 String objectName = StringUtils.substringBefore(beanAndPropertyName, ".");
72 String beanProperty = StringUtils.substringAfter(beanAndPropertyName, ".");
73 Object beanObject;
74
75 if (log.isDebugEnabled())
76 {
77 log.debug("getBeanValue - bean: {" + objectName + "}, property: {" + beanProperty + "}");
78 }
79
80 // get the bean
81 beanObject = pageContext.getAttribute(objectName, scope);
82
83 // if null return
84 if (beanObject == null)
85 {
86 return null;
87 }
88
89 // go get the property
90 return getBeanProperty(beanObject, beanProperty);
91
92 }
93
94 // simple, only the javabean
95 if (log.isDebugEnabled())
96 {
97 log.debug("getBeanValue - bean: {" + beanAndPropertyName + "}");
98 }
99
100 return pageContext.getAttribute(beanAndPropertyName, scope);
101 }
102
103 /**
104 * <p>
105 * Returns the value of a property in the given bean.
106 * </p>
107 * <p>
108 * Handle <code>NestedNullException</code> returning nulls and other exceptions returning
109 * <code>ObjectLookupException</code>.
110 * </p>
111 * @param bean javabean
112 * @param name name of the property to read from the javabean
113 * @return Object
114 * @throws ObjectLookupException for errors while retrieving a property in the bean
115 */
116 public static Object getBeanProperty(Object bean, String name) throws ObjectLookupException
117 {
118
119 Validate.notNull(bean, "No bean specified");
120 Validate.notNull(name, "No name specified");
121
122 if (log.isDebugEnabled())
123 {
124 log.debug("getProperty [" + name + "] on bean " + bean);
125 }
126
127 try
128 {
129 return getProperty(bean, name);
130 }
131 catch (IllegalAccessException e)
132 {
133 throw new ObjectLookupException(LookupUtil.class, bean, name, e);
134 }
135 catch (InvocationTargetException e)
136 {
137 throw new ObjectLookupException(LookupUtil.class, bean, name, e);
138 }
139 catch (NoSuchMethodException e)
140 {
141 throw new ObjectLookupException(LookupUtil.class, bean, name, e);
142 }
143 catch (NestedNullException nne)
144 {
145 // don't throw exceptions for nulls
146 return null;
147 }
148 catch (IllegalArgumentException e)
149 {
150 // don't throw exceptions for nulls; the bean and name have already been checked; this is being thrown when
151 // the bean property value is itself null.
152 log
153 .debug(
154 "Caught IllegalArgumentException from beanutils while looking up " + name + " in bean " + bean,
155 e);
156 return null;
157 }
158 }
159
160 /**
161 * Return the value of the (possibly nested) property of the specified name, for the specified bean, with no type
162 * conversions.
163 * @param bean Bean whose property is to be extracted
164 * @param name Possibly nested name of the property to be extracted
165 * @return Object
166 * @throws NoSuchMethodException
167 * @throws InvocationTargetException
168 * @throws IllegalAccessException
169 * @throws BeanPropertyLookupException in caso di errori nella lettura di propriet? del bean
170 */
171 public static Object getProperty(Object bean, String name) throws IllegalAccessException,
172 InvocationTargetException, NoSuchMethodException
173 {
174 if (log.isDebugEnabled())
175 {
176 log.debug("getProperty [" + name + "] on bean " + bean);
177 }
178
179 Validate.notNull(bean, "No bean specified");
180 Validate.notNull(name, "No name specified");
181
182 Object evalBean = bean;
183 String evalName = name;
184
185 if (evalBean == null)
186 {
187 throw new IllegalArgumentException("No bean specified");
188 }
189 if (evalName == null)
190 {
191 throw new IllegalArgumentException("No name specified");
192 }
193
194 int indexOfINDEXEDDELIM = -1;
195 int indexOfMAPPEDDELIM = -1;
196 int indexOfMAPPEDDELIM2 = -1;
197 int indexOfNESTEDDELIM = -1;
198 while (true)
199 {
200
201 indexOfNESTEDDELIM = evalName.indexOf(PropertyUtils.NESTED_DELIM);
202 indexOfMAPPEDDELIM = evalName.indexOf(PropertyUtils.MAPPED_DELIM);
203 indexOfMAPPEDDELIM2 = evalName.indexOf(PropertyUtils.MAPPED_DELIM2);
204 if (indexOfMAPPEDDELIM2 >= 0
205 && indexOfMAPPEDDELIM >= 0
206 && (indexOfNESTEDDELIM < 0 || indexOfNESTEDDELIM > indexOfMAPPEDDELIM))
207 {
208 indexOfNESTEDDELIM = evalName.indexOf(PropertyUtils.NESTED_DELIM, indexOfMAPPEDDELIM2);
209 }
210 else
211 {
212 indexOfNESTEDDELIM = evalName.indexOf(PropertyUtils.NESTED_DELIM);
213 }
214 if (indexOfNESTEDDELIM < 0)
215 {
216 break;
217 }
218 String next = evalName.substring(0, indexOfNESTEDDELIM);
219 indexOfINDEXEDDELIM = next.indexOf(PropertyUtils.INDEXED_DELIM);
220 indexOfMAPPEDDELIM = next.indexOf(PropertyUtils.MAPPED_DELIM);
221 if (evalBean instanceof Map)
222 {
223 evalBean = ((Map) evalBean).get(next);
224 }
225 else if (indexOfMAPPEDDELIM >= 0)
226 {
227
228 evalBean = PropertyUtils.getMappedProperty(evalBean, next);
229
230 }
231 else if (indexOfINDEXEDDELIM >= 0)
232 {
233 evalBean = getIndexedProperty(evalBean, next);
234 }
235 else
236 {
237 evalBean = PropertyUtils.getSimpleProperty(evalBean, next);
238 }
239
240 if (evalBean == null)
241 {
242 log.debug("Null property value for '" + evalName.substring(0, indexOfNESTEDDELIM) + "'");
243 return null;
244 }
245 evalName = evalName.substring(indexOfNESTEDDELIM + 1);
246
247 }
248
249 indexOfINDEXEDDELIM = evalName.indexOf(PropertyUtils.INDEXED_DELIM);
250 indexOfMAPPEDDELIM = evalName.indexOf(PropertyUtils.MAPPED_DELIM);
251
252 if (evalBean == null)
253 {
254 log.debug("Null property value for '" + evalName.substring(0, indexOfNESTEDDELIM) + "'");
255 return null;
256 }
257 else if (evalBean instanceof Map)
258 {
259 evalBean = ((Map) evalBean).get(evalName);
260 }
261 else if (indexOfMAPPEDDELIM >= 0)
262 {
263 evalBean = PropertyUtils.getMappedProperty(evalBean, evalName);
264 }
265 else if (indexOfINDEXEDDELIM >= 0)
266 {
267 evalBean = getIndexedProperty(evalBean, evalName);
268 }
269 else
270 {
271 evalBean = PropertyUtils.getSimpleProperty(evalBean, evalName);
272 }
273
274 return evalBean;
275
276 }
277
278 /**
279 * Return the value of the specified indexed property of the specified bean, with no type conversions. The
280 * zero-relative index of the required value must be included (in square brackets) as a suffix to the property name,
281 * or <code>IllegalArgumentException</code> will be thrown. In addition to supporting the JavaBeans specification,
282 * this method has been extended to support <code>List</code> objects as well.
283 * @param bean Bean whose property is to be extracted
284 * @param name <code>propertyname[index]</code> of the property value to be extracted
285 * @return Object
286 * @exception IllegalAccessException if the caller does not have access to the property accessor method
287 * @exception InvocationTargetException if the property accessor method throws an exception
288 * @exception NoSuchMethodException if an accessor method for this propety cannot be found
289 */
290 public static Object getIndexedProperty(Object bean, String name) throws IllegalAccessException,
291 InvocationTargetException, NoSuchMethodException
292 {
293
294 Validate.notNull(bean, "No bean specified");
295 Validate.notNull(name, "No name specified");
296
297 String evalName = name;
298
299 // Identify the index of the requested individual property
300 int delim = evalName.indexOf(PropertyUtils.INDEXED_DELIM);
301 int delim2 = evalName.indexOf(PropertyUtils.INDEXED_DELIM2);
302 if ((delim < 0) || (delim2 <= delim))
303 {
304 throw new IllegalArgumentException("Invalid indexed property '" + evalName + "'");
305 }
306 int index = -1;
307 try
308 {
309 String subscript = evalName.substring(delim + 1, delim2);
310 index = Integer.parseInt(subscript);
311 }
312 catch (NumberFormatException e)
313 {
314 throw new IllegalArgumentException("Invalid indexed property '" + evalName + "'");
315 }
316 evalName = evalName.substring(0, delim);
317
318 if (log.isDebugEnabled())
319 {
320 log.debug("getIndexedProperty property name={" + evalName + "} with index " + index);
321
322 }
323
324 // addd support for lists and arrays
325 if (StringUtils.isEmpty(evalName))
326 {
327 if (bean instanceof List)
328 {
329 return ((List) bean).get(index);
330 }
331 else if (bean.getClass().isArray())
332 {
333 return ((Object[]) bean)[index];
334 }
335 }
336 // Request the specified indexed property value
337 return (PropertyUtils.getIndexedProperty(bean, evalName, index));
338
339 }
340
341 }