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 018 package org.apache.commons.beanutils; 019 020 021 import java.lang.reflect.InvocationTargetException; 022 import java.util.ArrayList; 023 import java.util.HashMap; 024 import java.util.Iterator; 025 import java.util.List; 026 import java.util.Map; 027 028 import junit.framework.TestCase; 029 import junit.framework.Test; 030 import junit.framework.TestSuite; 031 032 033 /** 034 * Test case for BeanUtils when the underlying bean is actually a DynaBean. 035 * 036 * @author Craig R. McClanahan 037 * @version $Revision: 552279 $ $Date: 2007-07-01 07:04:26 -0400 (Sun, 01 Jul 2007) $ 038 */ 039 040 public class DynaBeanUtilsTestCase extends TestCase { 041 042 043 // ----------------------------------------------------- Instance Variables 044 045 046 /** 047 * The basic test bean for each test. 048 */ 049 protected DynaBean bean = null; 050 051 052 /** 053 * The nested bean pointed at by the "nested" property. 054 */ 055 protected TestBean nested = null; 056 057 058 /** 059 * The set of properties that should be described. 060 */ 061 protected String describes[] = 062 { "booleanProperty", 063 "booleanSecond", 064 "byteProperty", 065 "doubleProperty", 066 "dupProperty", 067 "floatProperty", 068 "intArray", 069 "intIndexed", 070 "intProperty", 071 "listIndexed", 072 "longProperty", 073 "mapProperty", 074 "mappedProperty", 075 "mappedIntProperty", 076 "nested", 077 "nullProperty", 078 // "readOnlyProperty", 079 "shortProperty", 080 "stringArray", 081 "stringIndexed", 082 "stringProperty" 083 }; 084 085 086 // ----------------------------------------------------------- Constructors 087 088 089 /** 090 * Construct a new instance of this test case. 091 * 092 * @param name Name of the test case 093 */ 094 public DynaBeanUtilsTestCase(String name) { 095 096 super(name); 097 098 } 099 100 101 // --------------------------------------------------- Overall Test Methods 102 103 104 /** 105 * Set up instance variables required by this test case. 106 */ 107 public void setUp() throws Exception { 108 109 ConvertUtils.deregister(); 110 111 // Instantiate a new DynaBean instance 112 DynaClass dynaClass = createDynaClass(); 113 bean = dynaClass.newInstance(); 114 115 // Initialize the DynaBean's property values (like TestBean) 116 bean.set("booleanProperty", new Boolean(true)); 117 bean.set("booleanSecond", new Boolean(true)); 118 bean.set("byteProperty", new Byte((byte) 121)); 119 bean.set("doubleProperty", new Double(321.0)); 120 bean.set("floatProperty", new Float((float) 123.0)); 121 String dupProperty[] = { "Dup 0", "Dup 1", "Dup 2", "Dup 3", "Dup 4"}; 122 bean.set("dupProperty", dupProperty); 123 int intArray[] = { 0, 10, 20, 30, 40 }; 124 bean.set("intArray", intArray); 125 int intIndexed[] = { 0, 10, 20, 30, 40 }; 126 bean.set("intIndexed", intIndexed); 127 bean.set("intProperty", new Integer(123)); 128 List listIndexed = new ArrayList(); 129 listIndexed.add("String 0"); 130 listIndexed.add("String 1"); 131 listIndexed.add("String 2"); 132 listIndexed.add("String 3"); 133 listIndexed.add("String 4"); 134 bean.set("listIndexed", listIndexed); 135 bean.set("longProperty", new Long(321)); 136 HashMap mapProperty = new HashMap(); 137 mapProperty.put("First Key", "First Value"); 138 mapProperty.put("Second Key", "Second Value"); 139 bean.set("mapProperty", mapProperty); 140 HashMap mappedProperty = new HashMap(); 141 mappedProperty.put("First Key", "First Value"); 142 mappedProperty.put("Second Key", "Second Value"); 143 bean.set("mappedProperty", mappedProperty); 144 HashMap mappedIntProperty = new HashMap(); 145 mappedIntProperty.put("One", new Integer(1)); 146 mappedIntProperty.put("Two", new Integer(2)); 147 bean.set("mappedIntProperty", mappedIntProperty); 148 nested = new TestBean(); 149 bean.set("nested", nested); 150 // Property "nullProperty" is not initialized, so it should return null 151 bean.set("shortProperty", new Short((short) 987)); 152 String stringArray[] = 153 { "String 0", "String 1", "String 2", "String 3", "String 4" }; 154 bean.set("stringArray", stringArray); 155 String stringIndexed[] = 156 { "String 0", "String 1", "String 2", "String 3", "String 4" }; 157 bean.set("stringIndexed", stringIndexed); 158 bean.set("stringProperty", "This is a string"); 159 160 } 161 162 163 /** 164 * Return the tests included in this test suite. 165 */ 166 public static Test suite() { 167 168 return (new TestSuite(DynaBeanUtilsTestCase.class)); 169 170 } 171 172 173 /** 174 * Tear down instance variables required by this test case. 175 */ 176 public void tearDown() { 177 178 bean = null; 179 nested = null; 180 181 } 182 183 184 185 // ------------------------------------------------ Individual Test Methods 186 187 /** 188 * Test the cloneBean() method from a DynaBean. 189 */ 190 public void testCloneDynaBean() { 191 192 // Set up an origin bean with customized properties 193 DynaClass dynaClass = DynaBeanUtilsTestCase.createDynaClass(); 194 DynaBean orig = null; 195 try { 196 orig = dynaClass.newInstance(); 197 } catch (Exception e) { 198 fail("newInstance(): " + e); 199 } 200 orig.set("booleanProperty", Boolean.FALSE); 201 orig.set("byteProperty", new Byte((byte)111)); 202 orig.set("doubleProperty", new Double(333.33)); 203 orig.set("dupProperty", new String[] { "New 0", "New 1", "New 2" }); 204 orig.set("intArray", new int[] { 100, 200, 300 }); 205 orig.set("intProperty", new Integer(333)); 206 orig.set("longProperty", new Long(3333)); 207 orig.set("shortProperty", new Short((short) 33)); 208 orig.set("stringArray", new String[] { "New 0", "New 1" }); 209 orig.set("stringProperty", "Custom string"); 210 211 // Copy the origin bean to our destination test bean 212 DynaBean clonedBean = null; 213 try { 214 clonedBean = (DynaBean) BeanUtils.cloneBean(orig); 215 } catch (Exception e) { 216 fail("Threw exception: " + e); 217 } 218 219 // Validate the results for scalar properties 220 assertEquals("Cloned boolean property", 221 false, 222 ((Boolean) clonedBean.get("booleanProperty")).booleanValue()); 223 assertEquals("Cloned byte property", 224 (byte) 111, 225 ((Byte) clonedBean.get("byteProperty")).byteValue()); 226 assertEquals("Cloned double property", 227 333.33, 228 ((Double) clonedBean.get("doubleProperty")).doubleValue(), 229 0.005); 230 assertEquals("Cloned int property", 231 333, 232 ((Integer) clonedBean.get("intProperty")).intValue()); 233 assertEquals("Cloned long property", 234 3333, 235 ((Long) clonedBean.get("longProperty")).longValue()); 236 assertEquals("Cloned short property", 237 (short) 33, 238 ((Short) clonedBean.get("shortProperty")).shortValue()); 239 assertEquals("Cloned string property", 240 "Custom string", 241 (String) clonedBean.get("stringProperty")); 242 243 // Validate the results for array properties 244 String dupProperty[] = (String[]) clonedBean.get("dupProperty"); 245 assertNotNull("dupProperty present", dupProperty); 246 assertEquals("dupProperty length", 3, dupProperty.length); 247 assertEquals("dupProperty[0]", "New 0", dupProperty[0]); 248 assertEquals("dupProperty[1]", "New 1", dupProperty[1]); 249 assertEquals("dupProperty[2]", "New 2", dupProperty[2]); 250 int intArray[] = (int[]) clonedBean.get("intArray"); 251 assertNotNull("intArray present", intArray); 252 assertEquals("intArray length", 3, intArray.length); 253 assertEquals("intArray[0]", 100, intArray[0]); 254 assertEquals("intArray[1]", 200, intArray[1]); 255 assertEquals("intArray[2]", 300, intArray[2]); 256 String stringArray[] = (String[]) clonedBean.get("stringArray"); 257 assertNotNull("stringArray present", stringArray); 258 assertEquals("stringArray length", 2, stringArray.length); 259 assertEquals("stringArray[0]", "New 0", stringArray[0]); 260 assertEquals("stringArray[1]", "New 1", stringArray[1]); 261 262 } 263 264 /** 265 * Test the copyProperties() method from a DynaBean. 266 */ 267 public void testCopyPropertiesDynaBean() { 268 269 // Set up an origin bean with customized properties 270 DynaClass dynaClass = DynaBeanUtilsTestCase.createDynaClass(); 271 DynaBean orig = null; 272 try { 273 orig = dynaClass.newInstance(); 274 } catch (Exception e) { 275 fail("newInstance(): " + e); 276 } 277 orig.set("booleanProperty", Boolean.FALSE); 278 orig.set("byteProperty", new Byte((byte)111)); 279 orig.set("doubleProperty", new Double(333.33)); 280 orig.set("dupProperty", new String[] { "New 0", "New 1", "New 2" }); 281 orig.set("intArray", new int[] { 100, 200, 300 }); 282 orig.set("intProperty", new Integer(333)); 283 orig.set("longProperty", new Long(3333)); 284 orig.set("shortProperty", new Short((short) 33)); 285 orig.set("stringArray", new String[] { "New 0", "New 1" }); 286 orig.set("stringProperty", "Custom string"); 287 288 // Copy the origin bean to our destination test bean 289 try { 290 BeanUtils.copyProperties(bean, orig); 291 } catch (Exception e) { 292 fail("Threw exception: " + e); 293 } 294 295 // Validate the results for scalar properties 296 assertEquals("Copied boolean property", 297 false, 298 ((Boolean) bean.get("booleanProperty")).booleanValue()); 299 assertEquals("Copied byte property", 300 (byte) 111, 301 ((Byte) bean.get("byteProperty")).byteValue()); 302 assertEquals("Copied double property", 303 333.33, 304 ((Double) bean.get("doubleProperty")).doubleValue(), 305 0.005); 306 assertEquals("Copied int property", 307 333, 308 ((Integer) bean.get("intProperty")).intValue()); 309 assertEquals("Copied long property", 310 3333, 311 ((Long) bean.get("longProperty")).longValue()); 312 assertEquals("Copied short property", 313 (short) 33, 314 ((Short) bean.get("shortProperty")).shortValue()); 315 assertEquals("Copied string property", 316 "Custom string", 317 (String) bean.get("stringProperty")); 318 319 // Validate the results for array properties 320 String dupProperty[] = (String[]) bean.get("dupProperty"); 321 assertNotNull("dupProperty present", dupProperty); 322 assertEquals("dupProperty length", 3, dupProperty.length); 323 assertEquals("dupProperty[0]", "New 0", dupProperty[0]); 324 assertEquals("dupProperty[1]", "New 1", dupProperty[1]); 325 assertEquals("dupProperty[2]", "New 2", dupProperty[2]); 326 int intArray[] = (int[]) bean.get("intArray"); 327 assertNotNull("intArray present", intArray); 328 assertEquals("intArray length", 3, intArray.length); 329 assertEquals("intArray[0]", 100, intArray[0]); 330 assertEquals("intArray[1]", 200, intArray[1]); 331 assertEquals("intArray[2]", 300, intArray[2]); 332 String stringArray[] = (String[]) bean.get("stringArray"); 333 assertNotNull("stringArray present", stringArray); 334 assertEquals("stringArray length", 2, stringArray.length); 335 assertEquals("stringArray[0]", "New 0", stringArray[0]); 336 assertEquals("stringArray[1]", "New 1", stringArray[1]); 337 338 } 339 340 341 /** 342 * Test copyProperties() when the origin is a a <code>Map</code>. 343 */ 344 public void testCopyPropertiesMap() { 345 346 Map map = new HashMap(); 347 map.put("booleanProperty", "false"); 348 map.put("byteProperty", "111"); 349 map.put("doubleProperty", "333.0"); 350 map.put("dupProperty", new String[] { "New 0", "New 1", "New 2" }); 351 map.put("floatProperty", "222.0"); 352 map.put("intArray", new String[] { "0", "100", "200" }); 353 map.put("intProperty", "111"); 354 map.put("longProperty", "444"); 355 map.put("shortProperty", "555"); 356 map.put("stringProperty", "New String Property"); 357 358 try { 359 BeanUtils.copyProperties(bean, map); 360 } catch (Throwable t) { 361 fail("Threw " + t.toString()); 362 } 363 364 // Scalar properties 365 assertEquals("booleanProperty", false, 366 ((Boolean) bean.get("booleanProperty")).booleanValue()); 367 assertEquals("byteProperty", (byte) 111, 368 ((Byte) bean.get("byteProperty")).byteValue()); 369 assertEquals("doubleProperty", 333.0, 370 ((Double) bean.get("doubleProperty")).doubleValue(), 371 0.005); 372 assertEquals("floatProperty", (float) 222.0, 373 ((Float) bean.get("floatProperty")).floatValue(), 374 (float) 0.005); 375 assertEquals("intProperty", 111, 376 ((Integer) bean.get("intProperty")).intValue()); 377 assertEquals("longProperty", 444, 378 ((Long) bean.get("longProperty")).longValue()); 379 assertEquals("shortProperty", (short) 555, 380 ((Short) bean.get("shortProperty")).shortValue()); 381 assertEquals("stringProperty", "New String Property", 382 (String) bean.get("stringProperty")); 383 384 // Indexed Properties 385 String dupProperty[] = (String[]) bean.get("dupProperty"); 386 assertNotNull("dupProperty present", dupProperty); 387 assertEquals("dupProperty length", 3, dupProperty.length); 388 assertEquals("dupProperty[0]", "New 0", dupProperty[0]); 389 assertEquals("dupProperty[1]", "New 1", dupProperty[1]); 390 assertEquals("dupProperty[2]", "New 2", dupProperty[2]); 391 int intArray[] = (int[]) bean.get("intArray"); 392 assertNotNull("intArray present", intArray); 393 assertEquals("intArray length", 3, intArray.length); 394 assertEquals("intArray[0]", 0, intArray[0]); 395 assertEquals("intArray[1]", 100, intArray[1]); 396 assertEquals("intArray[2]", 200, intArray[2]); 397 398 } 399 400 401 /** 402 * Test the copyProperties() method from a standard JavaBean. 403 */ 404 public void testCopyPropertiesStandard() { 405 406 // Set up an origin bean with customized properties 407 TestBean orig = new TestBean(); 408 orig.setBooleanProperty(false); 409 orig.setByteProperty((byte) 111); 410 orig.setDoubleProperty(333.33); 411 orig.setDupProperty(new String[] { "New 0", "New 1", "New 2" }); 412 orig.setIntArray(new int[] { 100, 200, 300 }); 413 orig.setIntProperty(333); 414 orig.setLongProperty(3333); 415 orig.setShortProperty((short) 33); 416 orig.setStringArray(new String[] { "New 0", "New 1" }); 417 orig.setStringProperty("Custom string"); 418 419 // Copy the origin bean to our destination test bean 420 try { 421 BeanUtils.copyProperties(bean, orig); 422 } catch (Exception e) { 423 fail("Threw exception: " + e); 424 } 425 426 // Validate the results for scalar properties 427 assertEquals("Copied boolean property", 428 false, 429 ((Boolean) bean.get("booleanProperty")).booleanValue()); 430 assertEquals("Copied byte property", 431 (byte) 111, 432 ((Byte) bean.get("byteProperty")).byteValue()); 433 assertEquals("Copied double property", 434 333.33, 435 ((Double) bean.get("doubleProperty")).doubleValue(), 436 0.005); 437 assertEquals("Copied int property", 438 333, 439 ((Integer) bean.get("intProperty")).intValue()); 440 assertEquals("Copied long property", 441 3333, 442 ((Long) bean.get("longProperty")).longValue()); 443 assertEquals("Copied short property", 444 (short) 33, 445 ((Short) bean.get("shortProperty")).shortValue()); 446 assertEquals("Copied string property", 447 "Custom string", 448 (String) bean.get("stringProperty")); 449 450 // Validate the results for array properties 451 String dupProperty[] = (String[]) bean.get("dupProperty"); 452 assertNotNull("dupProperty present", dupProperty); 453 assertEquals("dupProperty length", 3, dupProperty.length); 454 assertEquals("dupProperty[0]", "New 0", dupProperty[0]); 455 assertEquals("dupProperty[1]", "New 1", dupProperty[1]); 456 assertEquals("dupProperty[2]", "New 2", dupProperty[2]); 457 int intArray[] = (int[]) bean.get("intArray"); 458 assertNotNull("intArray present", intArray); 459 assertEquals("intArray length", 3, intArray.length); 460 assertEquals("intArray[0]", 100, intArray[0]); 461 assertEquals("intArray[1]", 200, intArray[1]); 462 assertEquals("intArray[2]", 300, intArray[2]); 463 String stringArray[] = (String[]) bean.get("stringArray"); 464 assertNotNull("stringArray present", stringArray); 465 assertEquals("stringArray length", 2, stringArray.length); 466 assertEquals("stringArray[0]", "New 0", stringArray[0]); 467 assertEquals("stringArray[1]", "New 1", stringArray[1]); 468 469 } 470 471 472 /** 473 * Test the describe() method. 474 */ 475 public void testDescribe() { 476 477 Map map = null; 478 try { 479 map = PropertyUtils.describe(bean); 480 } catch (Exception e) { 481 fail("Threw exception " + e); 482 } 483 484 // Verify existence of all the properties that should be present 485 for (int i = 0; i < describes.length; i++) { 486 assertTrue("Property '" + describes[i] + "' is present", 487 map.containsKey(describes[i])); 488 } 489 assertTrue("Property 'writeOnlyProperty' is not present", 490 !map.containsKey("writeOnlyProperty")); 491 492 // Verify the values of scalar properties 493 assertEquals("Value of 'booleanProperty'", 494 Boolean.TRUE, 495 map.get("booleanProperty")); 496 assertEquals("Value of 'byteProperty'", 497 new Byte((byte) 121), 498 map.get("byteProperty")); 499 assertEquals("Value of 'doubleProperty'", 500 new Double(321.0), 501 map.get("doubleProperty")); 502 assertEquals("Value of 'floatProperty'", 503 new Float((float) 123.0), 504 map.get("floatProperty")); 505 assertEquals("Value of 'intProperty'", 506 new Integer(123), 507 map.get("intProperty")); 508 assertEquals("Value of 'longProperty'", 509 new Long(321), 510 map.get("longProperty")); 511 assertEquals("Value of 'shortProperty'", 512 new Short((short) 987), 513 map.get("shortProperty")); 514 assertEquals("Value of 'stringProperty'", 515 "This is a string", 516 (String) map.get("stringProperty")); 517 518 } 519 520 521 /** 522 * Test populate() method on array properties as a whole. 523 */ 524 public void testPopulateArrayProperties() { 525 526 try { 527 528 HashMap map = new HashMap(); 529 // int intArray[] = new int[] { 123, 456, 789 }; 530 String intArrayIn[] = new String[] { "123", "456", "789" }; 531 map.put("intArray", intArrayIn); 532 String stringArray[] = new String[] 533 { "New String 0", "New String 1" }; 534 map.put("stringArray", stringArray); 535 536 BeanUtils.populate(bean, map); 537 538 int intArray[] = (int[]) bean.get("intArray"); 539 assertNotNull("intArray is present", intArray); 540 assertEquals("intArray length", 541 3, intArray.length); 542 assertEquals("intArray[0]", 123, intArray[0]); 543 assertEquals("intArray[1]", 456, intArray[1]); 544 assertEquals("intArray[2]", 789, intArray[2]); 545 stringArray = (String[]) bean.get("stringArray"); 546 assertNotNull("stringArray is present", stringArray); 547 assertEquals("stringArray length", 2, stringArray.length); 548 assertEquals("stringArray[0]", "New String 0", stringArray[0]); 549 assertEquals("stringArray[1]", "New String 1", stringArray[1]); 550 551 } catch (IllegalAccessException e) { 552 fail("IllegalAccessException"); 553 } catch (InvocationTargetException e) { 554 fail("InvocationTargetException"); 555 } 556 557 } 558 559 560 /** 561 * tests the string and int arrays of TestBean 562 */ 563 public void testGetArrayProperty() { 564 try { 565 String arr[] = BeanUtils.getArrayProperty(bean, "stringArray"); 566 String comp[] = (String[]) bean.get("stringArray"); 567 568 assertTrue("String array length = " + comp.length, 569 (comp.length == arr.length)); 570 571 arr = BeanUtils.getArrayProperty(bean, "intArray"); 572 int iarr[] = (int[]) bean.get("intArray"); 573 574 assertTrue("String array length = " + iarr.length, 575 (iarr.length == arr.length)); 576 } catch (IllegalAccessException e) { 577 fail("IllegalAccessException"); 578 } catch (InvocationTargetException e) { 579 fail("InvocationTargetException"); 580 } catch (NoSuchMethodException e) { 581 fail("NoSuchMethodException"); 582 } 583 584 } 585 586 587 /** 588 * tests getting an indexed property 589 */ 590 public void testGetIndexedProperty1() { 591 try { 592 String val = BeanUtils.getIndexedProperty(bean, "intIndexed[3]"); 593 String comp = String.valueOf(bean.get("intIndexed", 3)); 594 assertTrue("intIndexed[3] == " + comp, val.equals(comp)); 595 596 val = BeanUtils.getIndexedProperty(bean, "stringIndexed[3]"); 597 comp = (String) bean.get("stringIndexed", 3); 598 assertTrue("stringIndexed[3] == " + comp, val.equals(comp)); 599 } catch (IllegalAccessException e) { 600 fail("IllegalAccessException"); 601 } catch (InvocationTargetException e) { 602 fail("InvocationTargetException"); 603 } catch (NoSuchMethodException e) { 604 fail("NoSuchMethodException"); 605 } 606 } 607 608 609 /** 610 * tests getting an indexed property 611 */ 612 public void testGetIndexedProperty2() { 613 try { 614 String val = BeanUtils.getIndexedProperty(bean, "intIndexed", 3); 615 String comp = String.valueOf(bean.get("intIndexed", 3)); 616 617 assertTrue("intIndexed,3 == " + comp, val.equals(comp)); 618 619 val = BeanUtils.getIndexedProperty(bean, "stringIndexed", 3); 620 comp = (String) bean.get("stringIndexed", 3); 621 622 assertTrue("stringIndexed,3 == " + comp, val.equals(comp)); 623 624 } catch (IllegalAccessException e) { 625 fail("IllegalAccessException"); 626 } catch (InvocationTargetException e) { 627 fail("InvocationTargetException"); 628 } catch (NoSuchMethodException e) { 629 fail("NoSuchMethodException"); 630 } 631 } 632 633 634 /** 635 * tests getting a nested property 636 */ 637 public void testGetNestedProperty() { 638 try { 639 String val = BeanUtils.getNestedProperty(bean, "nested.stringProperty"); 640 String comp = nested.getStringProperty(); 641 assertTrue("nested.StringProperty == " + comp, 642 val.equals(comp)); 643 } catch (IllegalAccessException e) { 644 fail("IllegalAccessException"); 645 } catch (InvocationTargetException e) { 646 fail("InvocationTargetException"); 647 } catch (NoSuchMethodException e) { 648 fail("NoSuchMethodException"); 649 } 650 } 651 652 653 /** 654 * tests getting a 'whatever' property 655 */ 656 public void testGetGeneralProperty() { 657 try { 658 String val = BeanUtils.getProperty(bean, "nested.intIndexed[2]"); 659 String comp = String.valueOf(bean.get("intIndexed", 2)); 660 661 assertTrue("nested.intIndexed[2] == " + comp, 662 val.equals(comp)); 663 } catch (IllegalAccessException e) { 664 fail("IllegalAccessException"); 665 } catch (InvocationTargetException e) { 666 fail("InvocationTargetException"); 667 } catch (NoSuchMethodException e) { 668 fail("NoSuchMethodException"); 669 } 670 } 671 672 673 /** 674 * tests getting a 'whatever' property 675 */ 676 public void testGetSimpleProperty() { 677 try { 678 String val = BeanUtils.getSimpleProperty(bean, "shortProperty"); 679 String comp = String.valueOf(bean.get("shortProperty")); 680 681 assertTrue("shortProperty == " + comp, 682 val.equals(comp)); 683 } catch (IllegalAccessException e) { 684 fail("IllegalAccessException"); 685 } catch (InvocationTargetException e) { 686 fail("InvocationTargetException"); 687 } catch (NoSuchMethodException e) { 688 fail("NoSuchMethodException"); 689 } 690 } 691 692 693 /** 694 * Test populate() method on individual array elements. 695 */ 696 public void testPopulateArrayElements() { 697 698 try { 699 700 HashMap map = new HashMap(); 701 map.put("intIndexed[0]", "100"); 702 map.put("intIndexed[2]", "120"); 703 map.put("intIndexed[4]", "140"); 704 705 BeanUtils.populate(bean, map); 706 Integer intIndexed0 = (Integer) bean.get("intIndexed", 0); 707 assertEquals("intIndexed[0] is 100", 708 100, intIndexed0.intValue()); 709 Integer intIndexed1 = (Integer) bean.get("intIndexed", 1); 710 assertEquals("intIndexed[1] is 10", 711 10, intIndexed1.intValue()); 712 Integer intIndexed2 = (Integer) bean.get("intIndexed", 2); 713 assertEquals("intIndexed[2] is 120", 714 120, intIndexed2.intValue()); 715 Integer intIndexed3 = (Integer) bean.get("intIndexed", 3); 716 assertEquals("intIndexed[3] is 30", 717 30, intIndexed3.intValue()); 718 Integer intIndexed4 = (Integer) bean.get("intIndexed", 4); 719 assertEquals("intIndexed[4] is 140", 720 140, intIndexed4.intValue()); 721 722 map.clear(); 723 map.put("stringIndexed[1]", "New String 1"); 724 map.put("stringIndexed[3]", "New String 3"); 725 726 BeanUtils.populate(bean, map); 727 728 assertEquals("stringIndexed[0] is \"String 0\"", 729 "String 0", 730 (String) bean.get("stringIndexed", 0)); 731 assertEquals("stringIndexed[1] is \"New String 1\"", 732 "New String 1", 733 (String) bean.get("stringIndexed", 1)); 734 assertEquals("stringIndexed[2] is \"String 2\"", 735 "String 2", 736 (String) bean.get("stringIndexed", 2)); 737 assertEquals("stringIndexed[3] is \"New String 3\"", 738 "New String 3", 739 (String) bean.get("stringIndexed", 3)); 740 assertEquals("stringIndexed[4] is \"String 4\"", 741 "String 4", 742 (String) bean.get("stringIndexed", 4)); 743 744 } catch (IllegalAccessException e) { 745 fail("IllegalAccessException"); 746 } catch (InvocationTargetException e) { 747 fail("InvocationTargetException"); 748 } 749 750 } 751 752 753 /** 754 * Test populate() on mapped properties. 755 */ 756 public void testPopulateMapped() { 757 758 try { 759 760 HashMap map = new HashMap(); 761 map.put("mappedProperty(First Key)", "New First Value"); 762 map.put("mappedProperty(Third Key)", "New Third Value"); 763 764 BeanUtils.populate(bean, map); 765 766 assertEquals("mappedProperty(First Key)", 767 "New First Value", 768 (String) bean.get("mappedProperty", "First Key")); 769 assertEquals("mappedProperty(Second Key)", 770 "Second Value", 771 (String) bean.get("mappedProperty", "Second Key")); 772 assertEquals("mappedProperty(Third Key)", 773 "New Third Value", 774 (String) bean.get("mappedProperty", "Third Key")); 775 assertNull("mappedProperty(Fourth Key", 776 bean.get("mappedProperty", "Fourth Key")); 777 778 } catch (IllegalAccessException e) { 779 fail("IllegalAccessException"); 780 } catch (InvocationTargetException e) { 781 fail("InvocationTargetException"); 782 } 783 784 } 785 786 787 /** 788 * Test populate() method on nested properties. 789 */ 790 public void testPopulateNested() { 791 792 try { 793 794 HashMap map = new HashMap(); 795 map.put("nested.booleanProperty", "false"); 796 // booleanSecond is left at true 797 map.put("nested.doubleProperty", "432.0"); 798 // floatProperty is left at 123.0 799 map.put("nested.intProperty", "543"); 800 // longProperty is left at 321 801 map.put("nested.shortProperty", "654"); 802 // stringProperty is left at "This is a string" 803 804 BeanUtils.populate(bean, map); 805 806 TestBean nested = (TestBean) bean.get("nested"); 807 assertTrue("booleanProperty is false", 808 !nested.getBooleanProperty()); 809 assertTrue("booleanSecond is true", 810 nested.isBooleanSecond()); 811 assertEquals("doubleProperty is 432.0", 812 432.0, 813 nested.getDoubleProperty(), 814 0.005); 815 assertEquals("floatProperty is 123.0", 816 (float) 123.0, 817 nested.getFloatProperty(), 818 (float) 0.005); 819 assertEquals("intProperty is 543", 820 543, nested.getIntProperty()); 821 assertEquals("longProperty is 321", 822 321, nested.getLongProperty()); 823 assertEquals("shortProperty is 654", 824 (short) 654, nested.getShortProperty()); 825 assertEquals("stringProperty is \"This is a string\"", 826 "This is a string", 827 nested.getStringProperty()); 828 829 } catch (IllegalAccessException e) { 830 fail("IllegalAccessException"); 831 } catch (InvocationTargetException e) { 832 fail("InvocationTargetException"); 833 } 834 835 } 836 837 838 /** 839 * Test populate() method on scalar properties. 840 */ 841 public void testPopulateScalar() { 842 843 try { 844 845 bean.set("nullProperty", "non-null value"); 846 847 HashMap map = new HashMap(); 848 map.put("booleanProperty", "false"); 849 // booleanSecond is left at true 850 map.put("doubleProperty", "432.0"); 851 // floatProperty is left at 123.0 852 map.put("intProperty", "543"); 853 // longProperty is left at 321 854 map.put("nullProperty", null); 855 map.put("shortProperty", "654"); 856 // stringProperty is left at "This is a string" 857 858 BeanUtils.populate(bean, map); 859 860 Boolean booleanProperty = (Boolean) bean.get("booleanProperty"); 861 assertTrue("booleanProperty is false", !booleanProperty.booleanValue()); 862 Boolean booleanSecond = (Boolean) bean.get("booleanSecond"); 863 assertTrue("booleanSecond is true", booleanSecond.booleanValue()); 864 Double doubleProperty = (Double) bean.get("doubleProperty"); 865 assertEquals("doubleProperty is 432.0", 866 432.0, doubleProperty.doubleValue(), 0.005); 867 Float floatProperty = (Float) bean.get("floatProperty"); 868 assertEquals("floatProperty is 123.0", 869 (float) 123.0, floatProperty.floatValue(), 870 (float) 0.005); 871 Integer intProperty = (Integer) bean.get("intProperty"); 872 assertEquals("intProperty is 543", 873 543, intProperty.intValue()); 874 Long longProperty = (Long) bean.get("longProperty"); 875 assertEquals("longProperty is 321", 876 321, longProperty.longValue()); 877 assertNull("nullProperty is null", bean.get("nullProperty")); 878 Short shortProperty = (Short) bean.get("shortProperty"); 879 assertEquals("shortProperty is 654", 880 (short) 654, shortProperty.shortValue()); 881 assertEquals("stringProperty is \"This is a string\"", 882 "This is a string", 883 (String) bean.get("stringProperty")); 884 885 } catch (IllegalAccessException e) { 886 fail("IllegalAccessException"); 887 } catch (InvocationTargetException e) { 888 fail("InvocationTargetException"); 889 } 890 891 } 892 893 894 /** 895 * Test calling setProperty() with null property values. 896 */ 897 public void testSetPropertyNullValues() throws Exception { 898 899 Object oldValue = null; 900 Object newValue = null; 901 902 // Scalar value into array 903 oldValue = PropertyUtils.getSimpleProperty(bean, "stringArray"); 904 BeanUtils.setProperty(bean, "stringArray", (String) null); 905 newValue = PropertyUtils.getSimpleProperty(bean, "stringArray"); 906 assertNotNull("stringArray is not null", newValue); 907 assertTrue("stringArray of correct type", 908 newValue instanceof String[]); 909 assertEquals("stringArray length", 910 1, ((String[]) newValue).length); 911 PropertyUtils.setProperty(bean, "stringArray", oldValue); 912 913 // Indexed value into array 914 oldValue = PropertyUtils.getSimpleProperty(bean, "stringArray"); 915 BeanUtils.setProperty(bean, "stringArray[2]", (String) null); 916 newValue = PropertyUtils.getSimpleProperty(bean, "stringArray"); 917 assertNotNull("stringArray is not null", newValue); 918 assertTrue("stringArray of correct type", 919 newValue instanceof String[]); 920 assertEquals("stringArray length", 921 5, ((String[]) newValue).length); 922 assertTrue("stringArray[2] is null", 923 ((String[]) newValue)[2] == null); 924 PropertyUtils.setProperty(bean, "stringArray", oldValue); 925 926 // Value into scalar 927 BeanUtils.setProperty(bean, "stringProperty", null); 928 assertTrue("stringProperty is now null", 929 BeanUtils.getProperty(bean, "stringProperty") == null); 930 931 } 932 933 934 /** 935 * Test converting to and from primitive wrapper types. 936 */ 937 public void testSetPropertyOnPrimitiveWrappers() throws Exception { 938 939 BeanUtils.setProperty(bean,"intProperty", new Integer(1)); 940 assertEquals(1,((Integer) bean.get("intProperty")).intValue()); 941 BeanUtils.setProperty(bean,"stringProperty", new Integer(1)); 942 assertEquals(1, Integer.parseInt((String) bean.get("stringProperty"))); 943 944 } 945 946 947 /** 948 * Test setting a null property value. 949 */ 950 public void testSetPropertyNull() throws Exception { 951 952 bean.set("nullProperty", "non-null value"); 953 BeanUtils.setProperty(bean, "nullProperty", null); 954 assertNull("nullProperty is null", bean.get("nullProperty")); 955 956 } 957 958 959 /** 960 * Test narrowing and widening conversions on byte. 961 */ 962 public void testCopyPropertyByte() throws Exception { 963 964 BeanUtils.setProperty(bean, "byteProperty", new Byte((byte) 123)); 965 assertEquals((byte) 123, ((Byte) bean.get("byteProperty")).byteValue()); 966 /* 967 BeanUtils.setProperty(bean, "byteProperty", new Double((double) 123)); 968 assertEquals((byte) 123, ((Byte) bean.get("byteProperty")).byteValue()); 969 BeanUtils.setProperty(bean, "byteProperty", new Float((float) 123)); 970 assertEquals((byte) 123, ((Byte) bean.get("byteProperty")).byteValue()); 971 */ 972 BeanUtils.setProperty(bean, "byteProperty", new Integer(123)); 973 assertEquals((byte) 123, ((Byte) bean.get("byteProperty")).byteValue()); 974 BeanUtils.setProperty(bean, "byteProperty", new Long(123)); 975 assertEquals((byte) 123, ((Byte) bean.get("byteProperty")).byteValue()); 976 BeanUtils.setProperty(bean, "byteProperty", new Short((short) 123)); 977 assertEquals((byte) 123, ((Byte) bean.get("byteProperty")).byteValue()); 978 979 } 980 981 982 /** 983 * Test narrowing and widening conversions on double. 984 */ 985 public void testCopyPropertyDouble() throws Exception { 986 987 BeanUtils.setProperty(bean, "doubleProperty", new Byte((byte) 123)); 988 assertEquals(123, ((Double) bean.get("doubleProperty")).doubleValue(), 0.005); 989 BeanUtils.setProperty(bean, "doubleProperty", new Double(123)); 990 assertEquals(123, ((Double) bean.get("doubleProperty")).doubleValue(), 0.005); 991 BeanUtils.setProperty(bean, "doubleProperty", new Float(123)); 992 assertEquals(123, ((Double) bean.get("doubleProperty")).doubleValue(), 0.005); 993 BeanUtils.setProperty(bean, "doubleProperty", new Integer(123)); 994 assertEquals(123, ((Double) bean.get("doubleProperty")).doubleValue(), 0.005); 995 BeanUtils.setProperty(bean, "doubleProperty", new Long(123)); 996 assertEquals(123, ((Double) bean.get("doubleProperty")).doubleValue(), 0.005); 997 BeanUtils.setProperty(bean, "doubleProperty", new Short((short) 123)); 998 assertEquals(123, ((Double) bean.get("doubleProperty")).doubleValue(), 0.005); 999 1000 } 1001 1002 1003 /** 1004 * Test narrowing and widening conversions on float. 1005 */ 1006 public void testCopyPropertyFloat() throws Exception { 1007 1008 BeanUtils.setProperty(bean, "floatProperty", new Byte((byte) 123)); 1009 assertEquals(123, ((Float) bean.get("floatProperty")).floatValue(), 0.005); 1010 BeanUtils.setProperty(bean, "floatProperty", new Double(123)); 1011 assertEquals(123, ((Float) bean.get("floatProperty")).floatValue(), 0.005); 1012 BeanUtils.setProperty(bean, "floatProperty", new Float(123)); 1013 assertEquals(123, ((Float) bean.get("floatProperty")).floatValue(), 0.005); 1014 BeanUtils.setProperty(bean, "floatProperty", new Integer(123)); 1015 assertEquals(123, ((Float) bean.get("floatProperty")).floatValue(), 0.005); 1016 BeanUtils.setProperty(bean, "floatProperty", new Long(123)); 1017 assertEquals(123, ((Float) bean.get("floatProperty")).floatValue(), 0.005); 1018 BeanUtils.setProperty(bean, "floatProperty", new Short((short) 123)); 1019 assertEquals(123, ((Float) bean.get("floatProperty")).floatValue(), 0.005); 1020 1021 } 1022 1023 1024 /** 1025 * Test narrowing and widening conversions on int. 1026 */ 1027 public void testCopyPropertyInteger() throws Exception { 1028 1029 BeanUtils.setProperty(bean, "longProperty", new Byte((byte) 123)); 1030 assertEquals(123, ((Integer) bean.get("intProperty")).intValue()); 1031 /* 1032 BeanUtils.setProperty(bean, "longProperty", new Double((double) 123)); 1033 assertEquals((int) 123, ((Integer) bean.get("intProperty")).intValue()); 1034 BeanUtils.setProperty(bean, "longProperty", new Float((float) 123)); 1035 assertEquals((int) 123, ((Integer) bean.get("intProperty")).intValue()); 1036 */ 1037 BeanUtils.setProperty(bean, "longProperty", new Integer(123)); 1038 assertEquals(123, ((Integer) bean.get("intProperty")).intValue()); 1039 BeanUtils.setProperty(bean, "longProperty", new Long(123)); 1040 assertEquals(123, ((Integer) bean.get("intProperty")).intValue()); 1041 BeanUtils.setProperty(bean, "longProperty", new Short((short) 123)); 1042 assertEquals(123, ((Integer) bean.get("intProperty")).intValue()); 1043 1044 } 1045 1046 1047 /** 1048 * Test narrowing and widening conversions on long. 1049 */ 1050 public void testCopyPropertyLong() throws Exception { 1051 1052 BeanUtils.setProperty(bean, "longProperty", new Byte((byte) 123)); 1053 assertEquals(123, ((Long) bean.get("longProperty")).longValue()); 1054 /* 1055 BeanUtils.setProperty(bean, "longProperty", new Double((double) 123)); 1056 assertEquals((long) 123, ((Long) bean.get("longProperty")).longValue()); 1057 BeanUtils.setProperty(bean, "longProperty", new Float((float) 123)); 1058 assertEquals((long) 123, ((Long) bean.get("longProperty")).longValue()); 1059 */ 1060 BeanUtils.setProperty(bean, "longProperty", new Integer(123)); 1061 assertEquals(123, ((Long) bean.get("longProperty")).longValue()); 1062 BeanUtils.setProperty(bean, "longProperty", new Long(123)); 1063 assertEquals(123, ((Long) bean.get("longProperty")).longValue()); 1064 BeanUtils.setProperty(bean, "longProperty", new Short((short) 123)); 1065 assertEquals(123, ((Long) bean.get("longProperty")).longValue()); 1066 1067 } 1068 1069 1070 /** 1071 * Test copying a null property value. 1072 */ 1073 public void testCopyPropertyNull() throws Exception { 1074 1075 bean.set("nullProperty", "non-null value"); 1076 BeanUtils.copyProperty(bean, "nullProperty", null); 1077 assertNull("nullProperty is null", bean.get("nullProperty")); 1078 1079 } 1080 1081 1082 /** 1083 * Test narrowing and widening conversions on short. 1084 */ 1085 public void testCopyPropertyShort() throws Exception { 1086 1087 BeanUtils.setProperty(bean, "shortProperty", new Byte((byte) 123)); 1088 assertEquals((short) 123, ((Short) bean.get("shortProperty")).shortValue()); 1089 /* 1090 BeanUtils.setProperty(bean, "shortProperty", new Double((double) 123)); 1091 assertEquals((short) 123, ((Short) bean.get("shortProperty")).shortValue()); 1092 BeanUtils.setProperty(bean, "shortProperty", new Float((float) 123)); 1093 assertEquals((short) 123, ((Short) bean.get("shortProperty")).shortValue()); 1094 */ 1095 BeanUtils.setProperty(bean, "shortProperty", new Integer(123)); 1096 assertEquals((short) 123, ((Short) bean.get("shortProperty")).shortValue()); 1097 BeanUtils.setProperty(bean, "shortProperty", new Long(123)); 1098 assertEquals((short) 123, ((Short) bean.get("shortProperty")).shortValue()); 1099 BeanUtils.setProperty(bean, "shortProperty", new Short((short) 123)); 1100 assertEquals((short) 123, ((Short) bean.get("shortProperty")).shortValue()); 1101 1102 } 1103 1104 1105 /** 1106 * Test copying a property using a nested indexed array expression, 1107 * with and without conversions. 1108 */ 1109 public void testCopyPropertyNestedIndexedArray() throws Exception { 1110 1111 int origArray[] = { 0, 10, 20, 30, 40}; 1112 int intArray[] = { 0, 0, 0 }; 1113 ((TestBean) bean.get("nested")).setIntArray(intArray); 1114 int intChanged[] = { 0, 0, 0 }; 1115 1116 // No conversion required 1117 BeanUtils.copyProperty(bean, "nested.intArray[1]", new Integer(1)); 1118 checkIntArray((int[]) bean.get("intArray"), origArray); 1119 intChanged[1] = 1; 1120 checkIntArray(((TestBean) bean.get("nested")).getIntArray(), 1121 intChanged); 1122 1123 // Widening conversion required 1124 BeanUtils.copyProperty(bean, "nested.intArray[1]", new Byte((byte) 2)); 1125 checkIntArray((int[]) bean.get("intArray"), origArray); 1126 intChanged[1] = 2; 1127 checkIntArray(((TestBean) bean.get("nested")).getIntArray(), 1128 intChanged); 1129 1130 // Narrowing conversion required 1131 BeanUtils.copyProperty(bean, "nested.intArray[1]", new Long(3)); 1132 checkIntArray((int[]) bean.get("intArray"), origArray); 1133 intChanged[1] = 3; 1134 checkIntArray(((TestBean) bean.get("nested")).getIntArray(), 1135 intChanged); 1136 1137 // String conversion required 1138 BeanUtils.copyProperty(bean, "nested.intArray[1]", "4"); 1139 checkIntArray((int[]) bean.get("intArray"), origArray); 1140 intChanged[1] = 4; 1141 checkIntArray(((TestBean) bean.get("nested")).getIntArray(), 1142 intChanged); 1143 1144 } 1145 1146 1147 /** 1148 * Test copying a property using a nested mapped map property. 1149 */ 1150 public void testCopyPropertyNestedMappedMap() throws Exception { 1151 1152 Map origMap = new HashMap(); 1153 origMap.put("First Key", "First Value"); 1154 origMap.put("Second Key", "Second Value"); 1155 Map changedMap = new HashMap(); 1156 changedMap.put("First Key", "First Value"); 1157 changedMap.put("Second Key", "Second Value"); 1158 1159 // No conversion required 1160 BeanUtils.copyProperty(bean, "nested.mapProperty(Second Key)", 1161 "New Second Value"); 1162 checkMap((Map) bean.get("mapProperty"), origMap); 1163 changedMap.put("Second Key", "New Second Value"); 1164 checkMap(((TestBean) bean.get("nested")).getMapProperty(), changedMap); 1165 1166 } 1167 1168 1169 /** 1170 * Test copying a property using a nested simple expression, with and 1171 * without conversions. 1172 */ 1173 public void testCopyPropertyNestedSimple() throws Exception { 1174 1175 bean.set("intProperty", new Integer(0)); 1176 nested.setIntProperty(0); 1177 1178 // No conversion required 1179 BeanUtils.copyProperty(bean, "nested.intProperty", new Integer(1)); 1180 assertEquals(0, ((Integer) bean.get("intProperty")).intValue()); 1181 assertEquals(1, nested.getIntProperty()); 1182 1183 // Widening conversion required 1184 BeanUtils.copyProperty(bean, "nested.intProperty", new Byte((byte) 2)); 1185 assertEquals(0, ((Integer) bean.get("intProperty")).intValue()); 1186 assertEquals(2, nested.getIntProperty()); 1187 1188 // Narrowing conversion required 1189 BeanUtils.copyProperty(bean, "nested.intProperty", new Long(3)); 1190 assertEquals(0, ((Integer) bean.get("intProperty")).intValue()); 1191 assertEquals(3, nested.getIntProperty()); 1192 1193 // String conversion required 1194 BeanUtils.copyProperty(bean, "nested.intProperty", "4"); 1195 assertEquals(0, ((Integer) bean.get("intProperty")).intValue()); 1196 assertEquals(4, nested.getIntProperty()); 1197 1198 } 1199 1200 1201 // ------------------------------------------------------ Protected Methods 1202 1203 1204 // Ensure that the nested intArray matches the specified values 1205 protected void checkIntArray(int actual[], int expected[]) { 1206 assertNotNull("actual array not null", actual); 1207 assertEquals("actual array length", expected.length, actual.length); 1208 for (int i = 0; i < actual.length; i++) { 1209 assertEquals("actual array value[" + i + "]", 1210 expected[i], actual[i]); 1211 } 1212 } 1213 1214 1215 // Ensure that the actual Map matches the expected Map 1216 protected void checkMap(Map actual, Map expected) { 1217 assertNotNull("actual map not null", actual); 1218 assertEquals("actual map size", expected.size(), actual.size()); 1219 Iterator keys = expected.keySet().iterator(); 1220 while (keys.hasNext()) { 1221 Object key = keys.next(); 1222 assertEquals("actual map value(" + key + ")", 1223 expected.get(key), actual.get(key)); 1224 } 1225 } 1226 1227 1228 /** 1229 * Create and return a <code>DynaClass</code> instance for our test 1230 * <code>DynaBean</code>. 1231 */ 1232 protected static DynaClass createDynaClass() { 1233 1234 int intArray[] = new int[0]; 1235 String stringArray[] = new String[0]; 1236 1237 DynaClass dynaClass = new BasicDynaClass 1238 ("TestDynaClass", null, 1239 new DynaProperty[]{ 1240 new DynaProperty("booleanProperty", Boolean.TYPE), 1241 new DynaProperty("booleanSecond", Boolean.TYPE), 1242 new DynaProperty("byteProperty", Byte.TYPE), 1243 new DynaProperty("doubleProperty", Double.TYPE), 1244 new DynaProperty("dupProperty", stringArray.getClass()), 1245 new DynaProperty("floatProperty", Float.TYPE), 1246 new DynaProperty("intArray", intArray.getClass()), 1247 new DynaProperty("intIndexed", intArray.getClass()), 1248 new DynaProperty("intProperty", Integer.TYPE), 1249 new DynaProperty("listIndexed", List.class), 1250 new DynaProperty("longProperty", Long.TYPE), 1251 new DynaProperty("mapProperty", Map.class), 1252 new DynaProperty("mappedProperty", Map.class), 1253 new DynaProperty("mappedIntProperty", Map.class), 1254 new DynaProperty("nested", TestBean.class), 1255 new DynaProperty("nullProperty", String.class), 1256 new DynaProperty("shortProperty", Short.TYPE), 1257 new DynaProperty("stringArray", stringArray.getClass()), 1258 new DynaProperty("stringIndexed", stringArray.getClass()), 1259 new DynaProperty("stringProperty", String.class), 1260 }); 1261 return (dynaClass); 1262 1263 } 1264 1265 1266 }