1 /* 2 * $Id: PropertyResolverImpl.java 471754 2006-11-06 14:55:09Z husted $ 3 * 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 */ 21 22 package org.apache.struts.faces.application; 23 24 25 import java.util.Map; 26 27 import javax.faces.el.PropertyNotFoundException; 28 import javax.faces.el.PropertyResolver; 29 30 import org.apache.commons.beanutils.DynaBean; 31 import org.apache.commons.beanutils.DynaProperty; 32 import org.apache.commons.logging.Log; 33 import org.apache.commons.logging.LogFactory; 34 import org.apache.struts.action.DynaActionForm; 35 36 37 38 /** 39 * <p>Custom <code>PropertyResolver</code> implementation that adds support 40 * for <code>DynaBean</code> property access to the facilities of the default 41 * <code>PropertyResolver</code> provided by JavaServer Faces.</p> 42 * 43 * <p>This class implements the following specific rules:</p> 44 * <ul> 45 * <li>Indexed variants of each call are directly passed through to the 46 * <code>PropertyResolver</code> instance that we are wrapping.</li> 47 * <li>If the specified base object is an instance of 48 * <code>DynaActionForm</code>, and the requested property name is 49 * <code>map</code>, maintain compatibility with the way that JSP and 50 * JSTL expressions can access this property: 51 * <ul> 52 * <li><code>getValue()</code> will return the result of calling 53 * <code>getMap()</code> on the base object.</li> 54 * <li><code>setValue()</code> will throw an exception, because the 55 * map of property values is a read-only property of the 56 * <code>DynaActionForm</code> class.</li> 57 * <li><code>isReadOnly()</code> returns <code>true</code>.</li> 58 * <li><code>getType()</code> returns the <code>Class</code> object 59 * for <code>java.util.Map</code>.</li> 60 * </ul></li> 61 * <li>If the specified base object is an instance of 62 * <code>DynaBean</code>, provide access to its named properties 63 * as follows: 64 * <ul> 65 * <li><code>getValue()</code> will return the result of calling 66 * <code>get()</code> on the base object.</li> 67 * <li><code>setValue()</code> will call <code>set()</code> 68 * on the base object.</li> 69 * <li><code>isReadOnly()</code> returns <code>false</code> (because 70 * the DynaBean APIs provide no mechanism to make this determination, 71 * but most implementations will provide mutable properties).</li> 72 * <li><code>getType()</code> returns the <code>Class</code> object 73 * for the underlying dynamic property.</li> 74 * </ul></li> 75 * <li>Named variant calls with any other type of base object are 76 * passed through to the <code>PropertyResolver</code> that we 77 * are wrapping.</li> 78 * </ul> 79 * 80 * @version $Rev: 471754 $ $Date: 2006-11-06 08:55:09 -0600 (Mon, 06 Nov 2006) $ 81 */ 82 83 public class PropertyResolverImpl extends PropertyResolver { 84 85 86 // ------------------------------------------------------------ Constructor 87 88 89 /** 90 * <p>Construct a new <code>PropertyResolver</code> instance, wrapping the 91 * specified instance using the Decorator pattern such that this class need 92 * implement only the new functionality it supports, and not need to 93 * re-implement the basic facilities. 94 * 95 * @param resolver The original resolver to be wrapped 96 * 97 * @exception NullPointerException if <code>resolver</code> 98 * is <code>null</code> 99 */ 100 public PropertyResolverImpl(PropertyResolver resolver) { 101 102 if (resolver == null) { 103 throw new NullPointerException(); 104 } 105 if (log.isDebugEnabled()) { 106 log.debug("Creating new instance, wrapping resolver " + 107 resolver); 108 } 109 this.resolver = resolver; 110 111 } 112 113 114 // ----------------------------------------------------- Instance Variables 115 116 117 /** 118 * <p>The <code>Log</code> instance for this class.</p> 119 */ 120 private static final Log log = 121 LogFactory.getLog(PropertyResolverImpl.class); 122 123 124 /** 125 * <p>The <code>PropertyResolver</code> instance that we are wrapping, 126 * which will be used to perform operations on beans that are not 127 * recognized by this class.</p> 128 */ 129 private PropertyResolver resolver = null; 130 131 132 // ----------------------------------------------- PropertyResolver Methods 133 134 135 /** 136 * <p>Return the value of the property with the specified name from 137 * the specified base object.</p> 138 * 139 * @param base The base object whose property value is to be returned 140 * @param name Name of the property to be returned 141 * 142 * @exception NullPointerException if <code>base</code> or 143 * <code>name</code> is <code>null</code> 144 * @exception PropertyNotFoundException if the specified property name 145 * does not exist, or is not readable 146 */ 147 public Object getValue(Object base, Object name) 148 throws PropertyNotFoundException { 149 150 if ((base == null) || (name == null)) { 151 throw new NullPointerException(); 152 } else if ((base instanceof DynaActionForm) && 153 ("map".equals(name))) { 154 if (log.isTraceEnabled()) { 155 log.trace("Returning property map for DynaActionForm " + base 156 + "'"); 157 } 158 return (((DynaActionForm) base).getMap()); 159 } else if (base instanceof DynaBean) { 160 if (getDynaProperty((DynaBean) base, name.toString()) == null) { 161 throw new PropertyNotFoundException(name.toString()); 162 } 163 Object value = ((DynaBean) base).get(name.toString()); 164 if (log.isTraceEnabled()) { 165 log.trace("Returning dynamic property '" + name + 166 "' for DynaBean '" + base + "' value '" + 167 value + "'"); 168 } 169 return (value); 170 } else { 171 Object value = resolver.getValue(base, name); 172 if (log.isTraceEnabled()) { 173 log.trace("Delegating get of property '" + name + 174 "' for bean '" + base + "' value '" + 175 value + "'"); 176 } 177 return (value); 178 } 179 180 } 181 182 183 /** 184 * <p>Return the value at the specified index of the specified 185 * base object.</p> 186 * 187 * @param base The base object whose property value is to be returned 188 * @param index Index of the value to return 189 * 190 * @exception IndexOutOfBoundsException if thrown by the underlying 191 * access to the base object 192 * @exception NullPointerException if <code>base</code> 193 * is <code>null</code> 194 * @exception PropertyNotFoundException if some other exception occurs 195 */ 196 public Object getValue(Object base, int index) 197 throws PropertyNotFoundException { 198 199 return (resolver.getValue(base, index)); 200 201 } 202 203 204 /** 205 * <p>Set the specified value of the property with the specified name on 206 * the specified base object.</p> 207 * 208 * @param base The base object whose property value is to be set 209 * @param name Name of the property to be set 210 * @param value Value of the property to be set 211 * 212 * @exception NullPointerException if <code>base</code> or 213 * <code>name</code> is <code>null</code> 214 * @exception PropertyNotFoundException if the specified property name 215 * does not exist, or is not writeable 216 */ 217 public void setValue(Object base, Object name, Object value) 218 throws PropertyNotFoundException { 219 220 if ((base == null) || (name == null)) { 221 throw new NullPointerException(); 222 } else if ((base instanceof DynaActionForm) && 223 ("map".equals(name))) { 224 throw new PropertyNotFoundException(name.toString()); 225 } else if (base instanceof DynaBean) { 226 if (log.isTraceEnabled()) { 227 log.trace("setting dynamic property '" + name + 228 "' for DynaBean '" + base + 229 "' to '" + value + "'"); 230 } 231 if (getDynaProperty((DynaBean) base, name.toString()) == null) { 232 throw new PropertyNotFoundException(name.toString()); 233 } 234 ((DynaBean) base).set(name.toString(), value); 235 } else { 236 if (log.isTraceEnabled()) { 237 log.trace("Delegating set of property '" + name + 238 "' for bean '" + base + "' to value '" + 239 value + "'"); 240 } 241 resolver.setValue(base, name, value); 242 } 243 244 } 245 246 247 /** 248 * <p>Set the value at the specified index of the specified 249 * base object.</p> 250 * 251 * @param base The base object whose property value is to be set 252 * @param index Index of the value to set 253 * @param value Value to be set 254 * 255 * @exception IndexOutOfBoundsException if thrown by the underlying 256 * access to the base object 257 * @exception NullPointerException if <code>base</code> 258 * is <code>null</code> 259 * @exception PropertyNotFoundException if some other exception occurs 260 */ 261 public void setValue(Object base, int index, Object value) 262 throws PropertyNotFoundException { 263 264 resolver.setValue(base, index, value); 265 266 } 267 268 269 /** 270 * <p>Return <code>true</code> if the specified property of the specified 271 * base object is known to be immutable; otherwise, return 272 * <code>false</code>.</p> 273 * 274 * @param base The base object whose property is to analyzed 275 * @param name Name of the property to be analyzed 276 * 277 * @exception NullPointerException if <code>base</code> or 278 * <code>name</code> is <code>null</code> 279 * @exception PropertyNotFoundException if the specified property name 280 * does not exist 281 */ 282 public boolean isReadOnly(Object base, Object name) 283 throws PropertyNotFoundException { 284 285 if ((base == null) || (name == null)) { 286 throw new NullPointerException(); 287 } else if ((base instanceof DynaActionForm) && 288 ("map".equals(name))) { 289 return (true); 290 } else if (base instanceof DynaBean) { 291 if (getDynaProperty((DynaBean) base, name.toString()) == null) { 292 throw new PropertyNotFoundException(name.toString()); 293 } 294 return (false); 295 } else { 296 return (resolver.isReadOnly(base, name.toString())); 297 } 298 299 } 300 301 302 /** 303 * <p>Return <code>true</code> if the value at the specified index of 304 * the specified base object is known to be immutable; otherwise, 305 * return <code>false</code>.</p> 306 * 307 * @param base The base object whose property is to analyzed 308 * @param index Index of the value whose type is to be returned 309 * 310 * @exception IndexOutOfBoundsException if thrown by the underlying 311 * accessed to the indexed property 312 * @exception NullPointerException if <code>base</code> 313 * is <code>null</code> 314 * @exception PropertyNotFoundException if some other exception occurs 315 */ 316 public boolean isReadOnly(Object base, int index) 317 throws PropertyNotFoundException { 318 319 return (resolver.isReadOnly(base, index)); 320 321 } 322 323 324 /** 325 * <p>Return the <code>java.lang.Class</code> representing the type of 326 * the specified property of the specified base object, if it can be 327 * determined; otherwise return <code>null</code>.</p> 328 * 329 * @param base The base object whose property is to analyzed 330 * @param name Name of the property to be analyzed 331 * 332 * @exception NullPointerException if <code>base</code> or 333 * <code>name</code> is <code>null</code> 334 * @exception PropertyNotFoundException if the specified property name 335 * does not exist 336 */ 337 public Class getType(Object base, Object name) 338 throws PropertyNotFoundException { 339 340 if ((base == null) || (name == null)) { 341 throw new NullPointerException(); 342 } else if ((base instanceof DynaActionForm) && 343 ("map".equals(name))) { 344 return (Map.class); 345 } else if (base instanceof DynaBean) { 346 DynaProperty dynaProperty = 347 getDynaProperty((DynaBean) base, name.toString()); 348 if (dynaProperty != null) { 349 return (dynaProperty.getType()); 350 } else { 351 throw new PropertyNotFoundException(name.toString()); 352 } 353 } else { 354 return (resolver.getType(base, name)); 355 } 356 } 357 358 359 /** 360 * <p>Return the <code>java.lang.Class</code> representing the type of 361 * value at the specified index of the specified base object, or 362 * <code>null</code> if this value is <code>null</code>.</p> 363 * 364 * @param base The base object whose property is to analyzed 365 * @param index Index of the value whose type is to be returned 366 * 367 * @exception IndexOutOfBoundsException if thrown by the underlying 368 * accessed to the indexed property 369 * @exception NullPointerException if <code>base</code> 370 * is <code>null</code> 371 * @exception PropertyNotFoundException if some other exception occurs 372 */ 373 public Class getType(Object base, int index) 374 throws PropertyNotFoundException { 375 376 return (resolver.getType(base, index)); 377 378 } 379 380 381 // -------------------------------------------------------- Private Methods 382 383 384 /** 385 * <p>Return the <code>DynaProperty</code> describing the specified 386 * property of the specified <code>DynaBean</code>, or <code>null</code> 387 * if there is no such property defined on the underlying 388 * <code>DynaClass</code>.</p> 389 * 390 * @param bean <code>DynaBean</code> to be checked 391 * @param name Name of the property to be checked 392 */ 393 private DynaProperty getDynaProperty(DynaBean bean, String name) 394 throws PropertyNotFoundException { 395 396 DynaProperty dynaProperty = null; 397 try { 398 dynaProperty = bean.getDynaClass().getDynaProperty(name); 399 } catch (IllegalArgumentException e) { 400 ; 401 } 402 return (dynaProperty); 403 404 } 405 406 407 408 409 }