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