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 }