001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.beanutils; 019 020import java.io.Serializable; 021import java.lang.reflect.InvocationTargetException; 022 023/** 024 * <p>Implements <code>DynaBean</code> to wrap a standard JavaBean 025 * instance, so that DynaBean APIs can be used to access its properties.</p> 026 * 027 * <p> 028 * The most common use cases for this class involve wrapping an existing java bean. 029 * (This makes it different from the typical use cases for other <code>DynaBean</code>'s.) 030 * For example: 031 * </p> 032 * <pre> 033 * Object aJavaBean = ...; 034 * ... 035 * DynaBean db = new WrapDynaBean(aJavaBean); 036 * ... 037 * </pre> 038 * 039 * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation does not 040 * support the <code>contains()</code> and <code>remove()</code> methods.</p> 041 * 042 */ 043public class WrapDynaBean implements DynaBean, Serializable { 044 045 private static final long serialVersionUID = 1L; 046 047 /** 048 * The <code>DynaClass</code> "base class" that this DynaBean 049 * is associated with. 050 */ 051 protected transient WrapDynaClass dynaClass; 052 053 /** 054 * The JavaBean instance wrapped by this WrapDynaBean. 055 */ 056 protected Object instance; 057 058 /** 059 * Construct a new <code>DynaBean</code> associated with the specified 060 * JavaBean instance. 061 * 062 * @param instance JavaBean instance to be wrapped 063 */ 064 public WrapDynaBean(final Object instance) { 065 this(instance, null); 066 } 067 068 /** 069 * Creates a new instance of {@code WrapDynaBean}, associates it with the specified 070 * JavaBean instance, and initializes the bean's {@code DynaClass}. Using this 071 * constructor this {@code WrapDynaBean} instance can be assigned a class which has 072 * been configured externally. If no {@code WrapDynaClass} is provided, a new one is 073 * created using a standard mechanism. 074 * 075 * @param instance JavaBean instance to be wrapped 076 * @param cls the optional {@code WrapDynaClass} to be used for this bean 077 * @since 1.9 078 */ 079 public WrapDynaBean(final Object instance, final WrapDynaClass cls) { 080 this.instance = instance; 081 this.dynaClass = cls != null ? cls : (WrapDynaClass) getDynaClass(); 082 } 083 084 /** 085 * Does the specified mapped property contain a value for the specified 086 * key value? 087 * 088 * @param name Name of the property to check 089 * @param key Name of the key to check 090 * @return <code>true</code> if the mapped property contains a value for 091 * the specified key, otherwise <code>false</code> 092 * 093 * @throws IllegalArgumentException if there is no property 094 * of the specified name 095 */ 096 @Override 097 public boolean contains(final String name, final String key) { 098 throw new UnsupportedOperationException 099 ("WrapDynaBean does not support contains()"); 100 } 101 102 /** 103 * Return the value of a simple property with the specified name. 104 * 105 * @param name Name of the property whose value is to be retrieved 106 * @return The property's value 107 * @throws IllegalArgumentException if there is no property 108 * of the specified name 109 */ 110 @Override 111 public Object get(final String name) { 112 Object value = null; 113 try { 114 value = getPropertyUtils().getSimpleProperty(instance, name); 115 } catch (final InvocationTargetException ite) { 116 final Throwable cause = ite.getTargetException(); 117 throw new IllegalArgumentException 118 ("Error reading property '" + name + 119 "' nested exception - " + cause); 120 } catch (final Throwable t) { 121 throw new IllegalArgumentException 122 ("Error reading property '" + name + 123 "', exception - " + t); 124 } 125 return value; 126 } 127 128 /** 129 * Return the value of an indexed property with the specified name. 130 * 131 * @param name Name of the property whose value is to be retrieved 132 * @param index Index of the value to be retrieved 133 * @return The indexed property's value 134 * @throws IllegalArgumentException if there is no property 135 * of the specified name 136 * @throws IllegalArgumentException if the specified property 137 * exists, but is not indexed 138 * @throws IndexOutOfBoundsException if the specified index 139 * is outside the range of the underlying property 140 * @throws NullPointerException if no array or List has been 141 * initialized for this property 142 */ 143 @Override 144 public Object get(final String name, final int index) { 145 146 Object value = null; 147 try { 148 value = getPropertyUtils().getIndexedProperty(instance, name, index); 149 } catch (final IndexOutOfBoundsException e) { 150 throw e; 151 } catch (final InvocationTargetException ite) { 152 final Throwable cause = ite.getTargetException(); 153 throw new IllegalArgumentException 154 ("Error reading indexed property '" + name + 155 "' nested exception - " + cause); 156 } catch (final Throwable t) { 157 throw new IllegalArgumentException 158 ("Error reading indexed property '" + name + 159 "', exception - " + t); 160 } 161 return value; 162 163 } 164 165 /** 166 * Return the value of a mapped property with the specified name, 167 * or <code>null</code> if there is no value for the specified key. 168 * 169 * @param name Name of the property whose value is to be retrieved 170 * @param key Key of the value to be retrieved 171 * @return The mapped property's value 172 * @throws IllegalArgumentException if there is no property 173 * of the specified name 174 * @throws IllegalArgumentException if the specified property 175 * exists, but is not mapped 176 */ 177 @Override 178 public Object get(final String name, final String key) { 179 180 Object value = null; 181 try { 182 value = getPropertyUtils().getMappedProperty(instance, name, key); 183 } catch (final InvocationTargetException ite) { 184 final Throwable cause = ite.getTargetException(); 185 throw new IllegalArgumentException 186 ("Error reading mapped property '" + name + 187 "' nested exception - " + cause); 188 } catch (final Throwable t) { 189 throw new IllegalArgumentException 190 ("Error reading mapped property '" + name + 191 "', exception - " + t); 192 } 193 return value; 194 195 } 196 197 /** 198 * Return the <code>DynaClass</code> instance that describes the set of 199 * properties available for this DynaBean. 200 * @return The associated DynaClass 201 */ 202 @Override 203 public DynaClass getDynaClass() { 204 205 if (dynaClass == null) { 206 dynaClass = WrapDynaClass.createDynaClass(instance.getClass()); 207 } 208 209 return this.dynaClass; 210 211 } 212 213 /** 214 * Return the property descriptor for the specified property name. 215 * 216 * @param name Name of the property for which to retrieve the descriptor 217 * @return The descriptor for the specified property 218 * @throws IllegalArgumentException if this is not a valid property 219 * name for our DynaClass 220 */ 221 protected DynaProperty getDynaProperty(final String name) { 222 223 final DynaProperty descriptor = getDynaClass().getDynaProperty(name); 224 if (descriptor == null) { 225 throw new IllegalArgumentException 226 ("Invalid property name '" + name + "'"); 227 } 228 return descriptor; 229 230 } 231 232 /** 233 * Gets the bean instance wrapped by this DynaBean. 234 * For most common use cases, 235 * this object should already be known 236 * and this method safely be ignored. 237 * But some creators of frameworks using <code>DynaBean</code>'s may 238 * find this useful. 239 * 240 * @return the java bean Object wrapped by this <code>DynaBean</code> 241 */ 242 public Object getInstance() { 243 return instance; 244 } 245 246 /** 247 * Returns the {@code PropertyUtilsBean} instance to be used for accessing properties. 248 * If available, this object is obtained from the associated {@code WrapDynaClass}. 249 * 250 * @return the associated {@code PropertyUtilsBean} 251 */ 252 private PropertyUtilsBean getPropertyUtils() { 253 254 PropertyUtilsBean propUtils = null; 255 if (dynaClass != null) { 256 propUtils = dynaClass.getPropertyUtilsBean(); 257 } 258 return propUtils != null ? propUtils : PropertyUtilsBean.getInstance(); 259 260 } 261 262 /** 263 * Remove any existing value for the specified key on the 264 * specified mapped property. 265 * 266 * @param name Name of the property for which a value is to 267 * be removed 268 * @param key Key of the value to be removed 269 * @throws IllegalArgumentException if there is no property 270 * of the specified name 271 */ 272 @Override 273 public void remove(final String name, final String key) { 274 throw new UnsupportedOperationException 275 ("WrapDynaBean does not support remove()"); 276 } 277 278 /** 279 * Set the value of an indexed property with the specified name. 280 * 281 * @param name Name of the property whose value is to be set 282 * @param index Index of the property to be set 283 * @param value Value to which this property is to be set 284 * @throws ConversionException if the specified value cannot be 285 * converted to the type required for this property 286 * @throws IllegalArgumentException if there is no property 287 * of the specified name 288 * @throws IllegalArgumentException if the specified property 289 * exists, but is not indexed 290 * @throws IndexOutOfBoundsException if the specified index 291 * is outside the range of the underlying property 292 */ 293 @Override 294 public void set(final String name, final int index, final Object value) { 295 try { 296 getPropertyUtils().setIndexedProperty(instance, name, index, value); 297 } catch (final IndexOutOfBoundsException e) { 298 throw e; 299 } catch (final InvocationTargetException ite) { 300 final Throwable cause = ite.getTargetException(); 301 throw new IllegalArgumentException 302 ("Error setting indexed property '" + name + 303 "' nested exception - " + cause); 304 } catch (final Throwable t) { 305 throw new IllegalArgumentException 306 ("Error setting indexed property '" + name + 307 "', exception - " + t); 308 } 309 } 310 311 /** 312 * Set the value of a simple property with the specified name. 313 * 314 * @param name Name of the property whose value is to be set 315 * @param value Value to which this property is to be set 316 * @throws ConversionException if the specified value cannot be 317 * converted to the type required for this property 318 * @throws IllegalArgumentException if there is no property 319 * of the specified name 320 * @throws NullPointerException if an attempt is made to set a 321 * primitive property to null 322 */ 323 @Override 324 public void set(final String name, final Object value) { 325 326 try { 327 getPropertyUtils().setSimpleProperty(instance, name, value); 328 } catch (final InvocationTargetException ite) { 329 final Throwable cause = ite.getTargetException(); 330 throw new IllegalArgumentException 331 ("Error setting property '" + name + 332 "' nested exception -" + cause); 333 } catch (final Throwable t) { 334 throw new IllegalArgumentException 335 ("Error setting property '" + name + 336 "', exception - " + t); 337 } 338 339 } 340 341 /** 342 * Set the value of a mapped property with the specified name. 343 * 344 * @param name Name of the property whose value is to be set 345 * @param key Key of the property to be set 346 * @param value Value to which this property is to be set 347 * @throws ConversionException if the specified value cannot be 348 * converted to the type required for this property 349 * @throws IllegalArgumentException if there is no property 350 * of the specified name 351 * @throws IllegalArgumentException if the specified property 352 * exists, but is not mapped 353 */ 354 @Override 355 public void set(final String name, final String key, final Object value) { 356 357 try { 358 getPropertyUtils().setMappedProperty(instance, name, key, value); 359 } catch (final InvocationTargetException ite) { 360 final Throwable cause = ite.getTargetException(); 361 throw new IllegalArgumentException 362 ("Error setting mapped property '" + name + 363 "' nested exception - " + cause); 364 } catch (final Throwable t) { 365 throw new IllegalArgumentException 366 ("Error setting mapped property '" + name + 367 "', exception - " + t); 368 } 369 370 } 371}