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 */ 017package org.apache.commons.beanutils; 018 019import java.io.Serializable; 020import java.lang.reflect.Array; 021import java.math.BigDecimal; 022import java.math.BigInteger; 023import java.util.ArrayList; 024import java.util.Date; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031 032/** 033 * <p>DynaBean which automatically adds properties to the <code>DynaClass</code> 034 * and provides <i>Lazy List</i> and <i>Lazy Map</i> features.</p> 035 * 036 * <p>DynaBeans deal with three types of properties - <i>simple</i>, <i>indexed</i> and <i>mapped</i> and 037 * have the following <code>get()</code> and <code>set()</code> methods for 038 * each of these types:</p> 039 * <ul> 040 * <li><i>Simple</i> property methods - <code>get(name)</code> and 041 * <code>set(name, value)</code></li> 042 * <li><i>Indexed</i> property methods - <code>get(name, index)</code> and 043 * <code>set(name, index, value)</code></li> 044 * <li><i>Mapped</i> property methods - <code>get(name, key)</code> and 045 * <code>set(name, key, value)</code></li> 046 * </ul> 047 * 048 * <p><b><u>Getting Property Values</u></b></p> 049 * <p>Calling any of the <code>get()</code> methods, for a property which 050 * doesn't exist, returns <code>null</code> in this implementation.</p> 051 * 052 * <p><b><u>Setting Simple Properties</u></b></p> 053 * <p>The <code>LazyDynaBean</code> will automatically add a property to the <code>DynaClass</code> 054 * if it doesn't exist when the <code>set(name, value)</code> method is called.</p> 055 * 056 * <code>DynaBean myBean = new LazyDynaBean();</code></br> 057 * <code>myBean.set("myProperty", "myValue");</code></br> 058 * 059 * <p><b><u>Setting Indexed Properties</u></b></p> 060 * <p>If the property <b>doesn't</b> exist, the <code>LazyDynaBean</code> will automatically add 061 * a property with an <code>ArrayList</code> type to the <code>DynaClass</code> when 062 * the <code>set(name, index, value)</code> method is called. 063 * It will also instantiate a new <code>ArrayList</code> and automatically <i>grow</i> 064 * the <code>List</code> so that it is big enough to accomodate the index being set. 065 * <code>ArrayList</code> is the default indexed property that LazyDynaBean uses but 066 * this can be easily changed by overriding the <code>defaultIndexedProperty(name)</code> 067 * method.</p> 068 * 069 * <code>DynaBean myBean = new LazyDynaBean();</code></br> 070 * <code>myBean.set("myIndexedProperty", 0, "myValue1");</code></br> 071 * <code>myBean.set("myIndexedProperty", 1, "myValue2");</code></br> 072 * 073 * <p>If the indexed property <b>does</b> exist in the <code>DynaClass</code> but is set to 074 * <code>null</code> in the <code>LazyDynaBean</code>, then it will instantiate a 075 * new <code>List</code> or <code>Array</code> as specified by the property's type 076 * in the <code>DynaClass</code> and automatically <i>grow</i> the <code>List</code> 077 * or <code>Array</code> so that it is big enough to accomodate the index being set.</p> 078 * 079 * <code>DynaBean myBean = new LazyDynaBean();</code></br> 080 * <code>MutableDynaClass myClass = (MutableDynaClass)myBean.getDynaClass();</code></br> 081 * <code>myClass.add("myIndexedProperty", int[].class);</code></br> 082 * <code>myBean.set("myIndexedProperty", 0, new Integer(10));</code></br> 083 * <code>myBean.set("myIndexedProperty", 1, new Integer(20));</code></br> 084 * 085 * <p><b><u>Setting Mapped Properties</u></b></p> 086 * <p>If the property <b>doesn't</b> exist, the <code>LazyDynaBean</code> will automatically add 087 * a property with a <code>HashMap</code> type to the <code>DynaClass</code> and 088 * instantiate a new <code>HashMap</code> in the DynaBean when the 089 * <code>set(name, key, value)</code> method is called. <code>HashMap</code> is the default 090 * mapped property that LazyDynaBean uses but this can be easily changed by overriding 091 * the <code>defaultMappedProperty(name)</code> method.</p> 092 * 093 * <code>DynaBean myBean = new LazyDynaBean();</code></br> 094 * <code>myBean.set("myMappedProperty", "myKey", "myValue");</code></br> 095 * 096 * <p>If the mapped property <b>does</b> exist in the <code>DynaClass</code> but is set to 097 * <code>null</code> in the <code>LazyDynaBean</code>, then it will instantiate a 098 * new <code>Map</code> as specified by the property's type in the <code>DynaClass</code>.</p> 099 * 100 * <code>DynaBean myBean = new LazyDynaBean();</code></br> 101 * <code>MutableDynaClass myClass = (MutableDynaClass)myBean.getDynaClass();</code></br> 102 * <code>myClass.add("myMappedProperty", TreeMap.class);</code></br> 103 * <code>myBean.set("myMappedProperty", "myKey", "myValue");</code></br> 104 * 105 * <p><b><u><i>Restricted</i> DynaClass</u></b></p> 106 * <p><code>MutableDynaClass</code> have a facility to <i>restrict</i> the <code>DynaClass</code> 107 * so that its properties cannot be modified. If the <code>MutableDynaClass</code> is 108 * restricted then calling any of the <code>set()</code> methods for a property which 109 * doesn't exist will result in a <code>IllegalArgumentException</code> being thrown.</p> 110 * 111 * @version $Id: LazyDynaBean.html 893732 2014-01-11 19:35:15Z oheger $ 112 * @see LazyDynaClass 113 */ 114public class LazyDynaBean implements DynaBean, Serializable { 115 116 117 /** 118 * Commons Logging 119 */ 120 private transient Log logger = LogFactory.getLog(LazyDynaBean.class); 121 122 /** BigInteger Zero */ 123 protected static final BigInteger BigInteger_ZERO = new BigInteger("0"); 124 /** BigDecimal Zero */ 125 protected static final BigDecimal BigDecimal_ZERO = new BigDecimal("0"); 126 /** Character Space */ 127 protected static final Character Character_SPACE = new Character(' '); 128 /** Byte Zero */ 129 protected static final Byte Byte_ZERO = new Byte((byte)0); 130 /** Short Zero */ 131 protected static final Short Short_ZERO = new Short((short)0); 132 /** Integer Zero */ 133 protected static final Integer Integer_ZERO = new Integer(0); 134 /** Long Zero */ 135 protected static final Long Long_ZERO = new Long(0); 136 /** Float Zero */ 137 protected static final Float Float_ZERO = new Float((byte)0); 138 /** Double Zero */ 139 protected static final Double Double_ZERO = new Double((byte)0); 140 141 /** 142 * The <code>MutableDynaClass</code> "base class" that this DynaBean 143 * is associated with. 144 */ 145 protected Map<String, Object> values; 146 147 /** Map decorator for this DynaBean */ 148 private transient Map<String, Object> mapDecorator; 149 150 /** 151 * The <code>MutableDynaClass</code> "base class" that this DynaBean 152 * is associated with. 153 */ 154 protected MutableDynaClass dynaClass; 155 156 157 // ------------------- Constructors ---------------------------------- 158 159 /** 160 * Construct a new <code>LazyDynaBean</code> with a <code>LazyDynaClass</code> instance. 161 */ 162 public LazyDynaBean() { 163 this(new LazyDynaClass()); 164 } 165 166 /** 167 * Construct a new <code>LazyDynaBean</code> with a <code>LazyDynaClass</code> instance. 168 * 169 * @param name Name of this DynaBean class 170 */ 171 public LazyDynaBean(String name) { 172 this(new LazyDynaClass(name)); 173 } 174 175 /** 176 * Construct a new <code>DynaBean</code> associated with the specified 177 * <code>DynaClass</code> instance - if its not a <code>MutableDynaClass</code> 178 * then a new <code>LazyDynaClass</code> is created and the properties copied. 179 * 180 * @param dynaClass The DynaClass we are associated with 181 */ 182 public LazyDynaBean(DynaClass dynaClass) { 183 184 values = newMap(); 185 186 if (dynaClass instanceof MutableDynaClass) { 187 this.dynaClass = (MutableDynaClass)dynaClass; 188 } else { 189 this.dynaClass = new LazyDynaClass(dynaClass.getName(), dynaClass.getDynaProperties()); 190 } 191 192 } 193 194 195 // ------------------- Public Methods ---------------------------------- 196 197 /** 198 * Return a Map representation of this DynaBean. 199 * </p> 200 * This, for example, could be used in JSTL in the following way to access 201 * a DynaBean's <code>fooProperty</code>: 202 * <ul><li><code>${myDynaBean.<b>map</b>.fooProperty}</code></li></ul> 203 * 204 * @return a Map representation of this DynaBean 205 */ 206 public Map<String, Object> getMap() { 207 // cache the Map 208 if (mapDecorator == null) { 209 mapDecorator = new DynaBeanPropertyMapDecorator(this); 210 } 211 return mapDecorator; 212 } 213 214 /** 215 * <p>Return the size of an indexed or mapped property.</p> 216 * 217 * @param name Name of the property 218 * @return The indexed or mapped property size 219 * @exception IllegalArgumentException if no property name is specified 220 */ 221 public int size(String name) { 222 223 if (name == null) { 224 throw new IllegalArgumentException("No property name specified"); 225 } 226 227 Object value = values.get(name); 228 if (value == null) { 229 return 0; 230 } 231 232 if (value instanceof Map) { 233 return ((Map<?, ?>)value).size(); 234 } 235 236 if (value instanceof List) { 237 return ((List<?>)value).size(); 238 } 239 240 if ((value.getClass().isArray())) { 241 return Array.getLength(value); 242 } 243 244 return 0; 245 246 } 247 248 // ------------------- DynaBean Methods ---------------------------------- 249 250 /** 251 * Does the specified mapped property contain a value for the specified 252 * key value? 253 * 254 * @param name Name of the property to check 255 * @param key Name of the key to check 256 * @return <code>true</code> if the mapped property contains a value for 257 * the specified key, otherwise <code>false</code> 258 * 259 * @exception IllegalArgumentException if no property name is specified 260 */ 261 public boolean contains(String name, String key) { 262 263 if (name == null) { 264 throw new IllegalArgumentException("No property name specified"); 265 } 266 267 Object value = values.get(name); 268 if (value == null) { 269 return false; 270 } 271 272 if (value instanceof Map) { 273 return (((Map<?, ?>) value).containsKey(key)); 274 } 275 276 return false; 277 278 } 279 280 /** 281 * <p>Return the value of a simple property with the specified name.</p> 282 * 283 * <p><strong>N.B.</strong> Returns <code>null</code> if there is no property 284 * of the specified name.</p> 285 * 286 * @param name Name of the property whose value is to be retrieved. 287 * @return The property's value 288 * @exception IllegalArgumentException if no property name is specified 289 */ 290 public Object get(String name) { 291 292 if (name == null) { 293 throw new IllegalArgumentException("No property name specified"); 294 } 295 296 // Value found 297 Object value = values.get(name); 298 if (value != null) { 299 return value; 300 } 301 302 // Property doesn't exist 303 if (!isDynaProperty(name)) { 304 return null; 305 } 306 307 // Property doesn't exist 308 value = createProperty(name, dynaClass.getDynaProperty(name).getType()); 309 310 if (value != null) { 311 set(name, value); 312 } 313 314 return value; 315 316 } 317 318 /** 319 * <p>Return the value of an indexed property with the specified name.</p> 320 * 321 * <p><strong>N.B.</strong> Returns <code>null</code> if there is no 'indexed' 322 * property of the specified name.</p> 323 * 324 * @param name Name of the property whose value is to be retrieved 325 * @param index Index of the value to be retrieved 326 * @return The indexed property's value 327 * 328 * @exception IllegalArgumentException if the specified property 329 * exists, but is not indexed 330 * @exception IndexOutOfBoundsException if the specified index 331 * is outside the range of the underlying property 332 */ 333 public Object get(String name, int index) { 334 335 // If its not a property, then create default indexed property 336 if (!isDynaProperty(name)) { 337 set(name, defaultIndexedProperty(name)); 338 } 339 340 // Get the indexed property 341 Object indexedProperty = get(name); 342 343 // Check that the property is indexed 344 if (!dynaClass.getDynaProperty(name).isIndexed()) { 345 throw new IllegalArgumentException 346 ("Non-indexed property for '" + name + "[" + index + "]' " 347 + dynaClass.getDynaProperty(name).getName()); 348 } 349 350 // Grow indexed property to appropriate size 351 indexedProperty = growIndexedProperty(name, indexedProperty, index); 352 353 // Return the indexed value 354 if (indexedProperty.getClass().isArray()) { 355 return Array.get(indexedProperty, index); 356 } else if (indexedProperty instanceof List) { 357 return ((List<?>)indexedProperty).get(index); 358 } else { 359 throw new IllegalArgumentException 360 ("Non-indexed property for '" + name + "[" + index + "]' " 361 + indexedProperty.getClass().getName()); 362 } 363 364 } 365 366 /** 367 * <p>Return the value of a mapped property with the specified name.</p> 368 * 369 * <p><strong>N.B.</strong> Returns <code>null</code> if there is no 'mapped' 370 * property of the specified name.</p> 371 * 372 * @param name Name of the property whose value is to be retrieved 373 * @param key Key of the value to be retrieved 374 * @return The mapped property's value 375 * 376 * @exception IllegalArgumentException if the specified property 377 * exists, but is not mapped 378 */ 379 public Object get(String name, String key) { 380 381 // If its not a property, then create default mapped property 382 if (!isDynaProperty(name)) { 383 set(name, defaultMappedProperty(name)); 384 } 385 386 // Get the mapped property 387 Object mappedProperty = get(name); 388 389 // Check that the property is mapped 390 if (!dynaClass.getDynaProperty(name).isMapped()) { 391 throw new IllegalArgumentException 392 ("Non-mapped property for '" + name + "(" + key + ")' " 393 + dynaClass.getDynaProperty(name).getType().getName()); 394 } 395 396 // Get the value from the Map 397 if (mappedProperty instanceof Map) { 398 return (((Map<?, ?>) mappedProperty).get(key)); 399 } else { 400 throw new IllegalArgumentException 401 ("Non-mapped property for '" + name + "(" + key + ")'" 402 + mappedProperty.getClass().getName()); 403 } 404 405 } 406 407 408 /** 409 * Return the <code>DynaClass</code> instance that describes the set of 410 * properties available for this DynaBean. 411 * 412 * @return The associated DynaClass 413 */ 414 public DynaClass getDynaClass() { 415 return dynaClass; 416 } 417 418 /** 419 * Remove any existing value for the specified key on the 420 * specified mapped property. 421 * 422 * @param name Name of the property for which a value is to 423 * be removed 424 * @param key Key of the value to be removed 425 * 426 * @exception IllegalArgumentException if there is no property 427 * of the specified name 428 */ 429 public void remove(String name, String key) { 430 431 if (name == null) { 432 throw new IllegalArgumentException("No property name specified"); 433 } 434 435 Object value = values.get(name); 436 if (value == null) { 437 return; 438 } 439 440 if (value instanceof Map) { 441 ((Map<?, ?>) value).remove(key); 442 } else { 443 throw new IllegalArgumentException 444 ("Non-mapped property for '" + name + "(" + key + ")'" 445 + value.getClass().getName()); 446 } 447 448 } 449 450 /** 451 * Set the value of a simple property with the specified name. 452 * 453 * @param name Name of the property whose value is to be set 454 * @param value Value to which this property is to be set 455 * 456 * @exception IllegalArgumentException if this is not an existing property 457 * name for our DynaClass and the MutableDynaClass is restricted 458 * @exception ConversionException if the specified value cannot be 459 * converted to the type required for this property 460 * @exception NullPointerException if an attempt is made to set a 461 * primitive property to null 462 */ 463 public void set(String name, Object value) { 464 465 // If the property doesn't exist, then add it 466 if (!isDynaProperty(name)) { 467 468 if (dynaClass.isRestricted()) { 469 throw new IllegalArgumentException 470 ("Invalid property name '" + name + "' (DynaClass is restricted)"); 471 } 472 if (value == null) { 473 dynaClass.add(name); 474 } else { 475 dynaClass.add(name, value.getClass()); 476 } 477 478 } 479 480 DynaProperty descriptor = dynaClass.getDynaProperty(name); 481 482 if (value == null) { 483 if (descriptor.getType().isPrimitive()) { 484 throw new NullPointerException 485 ("Primitive value for '" + name + "'"); 486 } 487 } else if (!isAssignable(descriptor.getType(), value.getClass())) { 488 throw new ConversionException 489 ("Cannot assign value of type '" + 490 value.getClass().getName() + 491 "' to property '" + name + "' of type '" + 492 descriptor.getType().getName() + "'"); 493 } 494 495 // Set the property's value 496 values.put(name, value); 497 498 } 499 500 /** 501 * Set the value of an indexed property with the specified name. 502 * 503 * @param name Name of the property whose value is to be set 504 * @param index Index of the property to be set 505 * @param value Value to which this property is to be set 506 * 507 * @exception ConversionException if the specified value cannot be 508 * converted to the type required for this property 509 * @exception IllegalArgumentException if there is no property 510 * of the specified name 511 * @exception IllegalArgumentException if the specified property 512 * exists, but is not indexed 513 * @exception IndexOutOfBoundsException if the specified index 514 * is outside the range of the underlying property 515 */ 516 public void set(String name, int index, Object value) { 517 518 // If its not a property, then create default indexed property 519 if (!isDynaProperty(name)) { 520 set(name, defaultIndexedProperty(name)); 521 } 522 523 // Get the indexed property 524 Object indexedProperty = get(name); 525 526 // Check that the property is indexed 527 if (!dynaClass.getDynaProperty(name).isIndexed()) { 528 throw new IllegalArgumentException 529 ("Non-indexed property for '" + name + "[" + index + "]'" 530 + dynaClass.getDynaProperty(name).getType().getName()); 531 } 532 533 // Grow indexed property to appropriate size 534 indexedProperty = growIndexedProperty(name, indexedProperty, index); 535 536 // Set the value in an array 537 if (indexedProperty.getClass().isArray()) { 538 Array.set(indexedProperty, index, value); 539 } else if (indexedProperty instanceof List) { 540 @SuppressWarnings("unchecked") 541 // Indexed properties are stored in a List<Object> 542 List<Object> values = (List<Object>) indexedProperty; 543 values.set(index, value); 544 } else { 545 throw new IllegalArgumentException 546 ("Non-indexed property for '" + name + "[" + index + "]' " 547 + indexedProperty.getClass().getName()); 548 } 549 550 } 551 552 /** 553 * Set the value of a mapped property with the specified name. 554 * 555 * @param name Name of the property whose value is to be set 556 * @param key Key of the property to be set 557 * @param value Value to which this property is to be set 558 * 559 * @exception ConversionException if the specified value cannot be 560 * converted to the type required for this property 561 * @exception IllegalArgumentException if there is no property 562 * of the specified name 563 * @exception IllegalArgumentException if the specified property 564 * exists, but is not mapped 565 */ 566 public void set(String name, String key, Object value) { 567 568 // If the 'mapped' property doesn't exist, then add it 569 if (!isDynaProperty(name)) { 570 set(name, defaultMappedProperty(name)); 571 } 572 573 // Get the mapped property 574 Object mappedProperty = get(name); 575 576 // Check that the property is mapped 577 if (!dynaClass.getDynaProperty(name).isMapped()) { 578 throw new IllegalArgumentException 579 ("Non-mapped property for '" + name + "(" + key + ")'" 580 + dynaClass.getDynaProperty(name).getType().getName()); 581 } 582 583 // Set the value in the Map 584 @SuppressWarnings("unchecked") 585 // mapped properties are stored in a Map<String, Object> 586 Map<String, Object> valuesMap = (Map<String, Object>) mappedProperty; 587 valuesMap.put(key, value); 588 589 } 590 591 // ------------------- protected Methods ---------------------------------- 592 593 /** 594 * Grow the size of an indexed property 595 * @param name The name of the property 596 * @param indexedProperty The current property value 597 * @param index The indexed value to grow the property to (i.e. one less than 598 * the required size) 599 * @return The new property value (grown to the appropriate size) 600 */ 601 protected Object growIndexedProperty(String name, Object indexedProperty, int index) { 602 603 // Grow a List to the appropriate size 604 if (indexedProperty instanceof List) { 605 606 @SuppressWarnings("unchecked") 607 // Indexed properties are stored as List<Object> 608 List<Object> list = (List<Object>)indexedProperty; 609 while (index >= list.size()) { 610 Class<?> contentType = getDynaClass().getDynaProperty(name).getContentType(); 611 Object value = null; 612 if (contentType != null) { 613 value = createProperty(name+"["+list.size()+"]", contentType); 614 } 615 list.add(value); 616 } 617 618 } 619 620 // Grow an Array to the appropriate size 621 if ((indexedProperty.getClass().isArray())) { 622 623 int length = Array.getLength(indexedProperty); 624 if (index >= length) { 625 Class<?> componentType = indexedProperty.getClass().getComponentType(); 626 Object newArray = Array.newInstance(componentType, (index + 1)); 627 System.arraycopy(indexedProperty, 0, newArray, 0, length); 628 indexedProperty = newArray; 629 set(name, indexedProperty); 630 int newLength = Array.getLength(indexedProperty); 631 for (int i = length; i < newLength; i++) { 632 Array.set(indexedProperty, i, createProperty(name+"["+i+"]", componentType)); 633 } 634 } 635 } 636 637 return indexedProperty; 638 639 } 640 641 /** 642 * Create a new Instance of a Property 643 * @param name The name of the property 644 * @param type The class of the property 645 * @return The new value 646 */ 647 protected Object createProperty(String name, Class<?> type) { 648 if (type == null) { 649 return null; 650 } 651 652 // Create Lists, arrays or DynaBeans 653 if (type.isArray() || List.class.isAssignableFrom(type)) { 654 return createIndexedProperty(name, type); 655 } 656 657 if (Map.class.isAssignableFrom(type)) { 658 return createMappedProperty(name, type); 659 } 660 661 if (DynaBean.class.isAssignableFrom(type)) { 662 return createDynaBeanProperty(name, type); 663 } 664 665 if (type.isPrimitive()) { 666 return createPrimitiveProperty(name, type); 667 } 668 669 if (Number.class.isAssignableFrom(type)) { 670 return createNumberProperty(name, type); 671 } 672 673 return createOtherProperty(name, type); 674 675 } 676 677 /** 678 * Create a new Instance of an 'Indexed' Property 679 * @param name The name of the property 680 * @param type The class of the property 681 * @return The new value 682 */ 683 protected Object createIndexedProperty(String name, Class<?> type) { 684 685 // Create the indexed object 686 Object indexedProperty = null; 687 688 if (type == null) { 689 690 indexedProperty = defaultIndexedProperty(name); 691 692 } else if (type.isArray()) { 693 694 indexedProperty = Array.newInstance(type.getComponentType(), 0); 695 696 } else if (List.class.isAssignableFrom(type)) { 697 if (type.isInterface()) { 698 indexedProperty = defaultIndexedProperty(name); 699 } else { 700 try { 701 indexedProperty = type.newInstance(); 702 } 703 catch (Exception ex) { 704 throw new IllegalArgumentException 705 ("Error instantiating indexed property of type '" + 706 type.getName() + "' for '" + name + "' " + ex); 707 } 708 } 709 } else { 710 711 throw new IllegalArgumentException 712 ("Non-indexed property of type '" + type.getName() + "' for '" + name + "'"); 713 } 714 715 return indexedProperty; 716 717 } 718 719 /** 720 * Create a new Instance of a 'Mapped' Property 721 * @param name The name of the property 722 * @param type The class of the property 723 * @return The new value 724 */ 725 protected Object createMappedProperty(String name, Class<?> type) { 726 727 // Create the mapped object 728 Object mappedProperty = null; 729 730 if (type == null) { 731 732 mappedProperty = defaultMappedProperty(name); 733 734 } else if (type.isInterface()) { 735 736 mappedProperty = defaultMappedProperty(name); 737 738 } else if (Map.class.isAssignableFrom(type)) { 739 try { 740 mappedProperty = type.newInstance(); 741 } 742 catch (Exception ex) { 743 throw new IllegalArgumentException 744 ("Error instantiating mapped property of type '" + 745 type.getName() + "' for '" + name + "' " + ex); 746 } 747 } else { 748 749 throw new IllegalArgumentException 750 ("Non-mapped property of type '" + type.getName() + "' for '" + name + "'"); 751 } 752 753 return mappedProperty; 754 755 } 756 757 /** 758 * Create a new Instance of a 'DynaBean' Property. 759 * @param name The name of the property 760 * @param type The class of the property 761 * @return The new value 762 */ 763 protected Object createDynaBeanProperty(String name, Class<?> type) { 764 try { 765 return type.newInstance(); 766 } 767 catch (Exception ex) { 768 if (logger().isWarnEnabled()) { 769 logger().warn("Error instantiating DynaBean property of type '" + 770 type.getName() + "' for '" + name + "' " + ex); 771 } 772 return null; 773 } 774 } 775 776 /** 777 * Create a new Instance of a 'Primitive' Property. 778 * @param name The name of the property 779 * @param type The class of the property 780 * @return The new value 781 */ 782 protected Object createPrimitiveProperty(String name, Class<?> type) { 783 784 if (type == Boolean.TYPE) { 785 return Boolean.FALSE; 786 } else if (type == Integer.TYPE) { 787 return Integer_ZERO; 788 } else if (type == Long.TYPE) { 789 return Long_ZERO; 790 } else if (type == Double.TYPE) { 791 return Double_ZERO; 792 } else if (type == Float.TYPE) { 793 return Float_ZERO; 794 } else if (type == Byte.TYPE) { 795 return Byte_ZERO; 796 } else if (type == Short.TYPE) { 797 return Short_ZERO; 798 } else if (type == Character.TYPE) { 799 return Character_SPACE; 800 } else { 801 return null; 802 } 803 804 } 805 806 /** 807 * Create a new Instance of a <code>java.lang.Number</code> Property. 808 * @param name The name of the property 809 * @param type The class of the property 810 * @return The new value 811 */ 812 protected Object createNumberProperty(String name, Class<?> type) { 813 814 return null; 815 816 } 817 818 /** 819 * Create a new Instance of other Property types 820 * @param name The name of the property 821 * @param type The class of the property 822 * @return The new value 823 */ 824 protected Object createOtherProperty(String name, Class<?> type) { 825 826 if (type == Object.class || 827 type == String.class || 828 type == Boolean.class || 829 type == Character.class || 830 Date.class.isAssignableFrom(type)) { 831 832 return null; 833 834 } 835 836 try { 837 return type.newInstance(); 838 } 839 catch (Exception ex) { 840 if (logger().isWarnEnabled()) { 841 logger().warn("Error instantiating property of type '" + type.getName() + "' for '" + name + "' " + ex); 842 } 843 return null; 844 } 845 } 846 847 /** 848 * <p>Creates a new <code>ArrayList</code> for an 'indexed' property 849 * which doesn't exist.</p> 850 * 851 * <p>This method should be overridden if an alternative <code>List</code> 852 * or <code>Array</code> implementation is required for 'indexed' properties.</p> 853 * 854 * @param name Name of the 'indexed property. 855 * @return The default value for an indexed property (java.util.ArrayList) 856 */ 857 protected Object defaultIndexedProperty(String name) { 858 return new ArrayList<Object>(); 859 } 860 861 /** 862 * <p>Creates a new <code>HashMap</code> for a 'mapped' property 863 * which doesn't exist.</p> 864 * 865 * <p>This method can be overridden if an alternative <code>Map</code> 866 * implementation is required for 'mapped' properties.</p> 867 * 868 * @param name Name of the 'mapped property. 869 * @return The default value for a mapped property (java.util.HashMap) 870 */ 871 protected Map<String, Object> defaultMappedProperty(String name) { 872 return new HashMap<String, Object>(); 873 } 874 875 /** 876 * Indicates if there is a property with the specified name. 877 * @param name The name of the property to check 878 * @return <code>true</code> if there is a property of the 879 * specified name, otherwise <code>false</code> 880 */ 881 protected boolean isDynaProperty(String name) { 882 883 if (name == null) { 884 throw new IllegalArgumentException("No property name specified"); 885 } 886 887 // Handle LazyDynaClasses 888 if (dynaClass instanceof LazyDynaClass) { 889 return ((LazyDynaClass)dynaClass).isDynaProperty(name); 890 } 891 892 // Handle other MutableDynaClass 893 return dynaClass.getDynaProperty(name) == null ? false : true; 894 895 } 896 897 /** 898 * Is an object of the source class assignable to the destination class? 899 * 900 * @param dest Destination class 901 * @param source Source class 902 * @return <code>true</code> if the source class is assignable to the 903 * destination class, otherwise <code>false</code> 904 */ 905 protected boolean isAssignable(Class<?> dest, Class<?> source) { 906 907 if (dest.isAssignableFrom(source) || 908 ((dest == Boolean.TYPE) && (source == Boolean.class)) || 909 ((dest == Byte.TYPE) && (source == Byte.class)) || 910 ((dest == Character.TYPE) && (source == Character.class)) || 911 ((dest == Double.TYPE) && (source == Double.class)) || 912 ((dest == Float.TYPE) && (source == Float.class)) || 913 ((dest == Integer.TYPE) && (source == Integer.class)) || 914 ((dest == Long.TYPE) && (source == Long.class)) || 915 ((dest == Short.TYPE) && (source == Short.class))) { 916 return (true); 917 } else { 918 return (false); 919 } 920 921 } 922 923 /** 924 * <p>Creates a new instance of the <code>Map</code>.</p> 925 * @return a new Map instance 926 */ 927 protected Map<String, Object> newMap() { 928 return new HashMap<String, Object>(); 929 } 930 931 /** 932 * <p>Returns the <code>Log</code>. 933 */ 934 private Log logger() { 935 if (logger == null) { 936 logger = LogFactory.getLog(LazyDynaBean.class); 937 } 938 return logger; 939 } 940 941}