001 /** 002 * Copyright 2005-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.rice.krad.uif.util; 017 018 import org.junit.Test; 019 import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; 020 import org.kuali.rice.core.api.util.type.KualiDecimal; 021 import org.kuali.rice.core.api.util.type.KualiPercent; 022 import org.kuali.rice.krad.uif.component.BindingInfo; 023 import org.kuali.rice.krad.uif.container.CollectionGroup; 024 import org.kuali.rice.krad.uif.container.CollectionGroupBase; 025 import org.kuali.rice.krad.uif.container.CollectionGroupBuilder; 026 import org.kuali.rice.krad.uif.container.Group; 027 import org.kuali.rice.krad.uif.container.GroupBase; 028 import org.kuali.rice.krad.uif.element.Action; 029 import org.kuali.rice.krad.uif.element.Message; 030 import org.kuali.rice.krad.uif.element.ViewHeader; 031 import org.kuali.rice.krad.uif.layout.StackedLayoutManager; 032 import org.kuali.rice.krad.uif.layout.StackedLayoutManagerBase; 033 import org.kuali.rice.krad.uif.lifecycle.ViewLifecycle; 034 import org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl; 035 import org.kuali.rice.krad.uif.view.FormView; 036 import org.kuali.rice.krad.uif.view.ViewPresentationControllerBase; 037 import org.kuali.rice.krad.util.GlobalVariables; 038 import org.kuali.rice.krad.web.form.UifFormBase; 039 import org.slf4j.Logger; 040 import org.slf4j.LoggerFactory; 041 042 import java.beans.PropertyDescriptor; 043 import java.io.Serializable; 044 import java.lang.annotation.Retention; 045 import java.lang.annotation.RetentionPolicy; 046 import java.lang.reflect.Method; 047 import java.math.BigDecimal; 048 import java.sql.Timestamp; 049 import java.util.Arrays; 050 import java.util.Date; 051 import java.util.List; 052 import java.util.Map; 053 054 import static org.junit.Assert.*; 055 056 public class ObjectPropertyUtilsTest extends ProcessLoggingUnitTest { 057 058 final Logger LOG = LoggerFactory.getLogger(ObjectPropertyUtilsTest.class); 059 060 @Retention(RetentionPolicy.RUNTIME) 061 public @interface TestAnnotation { 062 String afoo(); 063 } 064 065 public static class TestBean implements Serializable { 066 067 private static final long serialVersionUID = 1L; 068 069 public TestBean() {} 070 071 private String rwProp; 072 private TestBeanTwo complexProp; 073 074 public String getRwProp() { 075 return this.rwProp; 076 } 077 078 public void setRwProp(String r) { 079 this.rwProp = r; 080 } 081 082 private String woProp; 083 084 public void setWoProp(String w) { 085 this.woProp = w; 086 } 087 088 private String roProp; 089 090 @TestAnnotation(afoo = "abar") 091 public String getRoProp() { 092 return this.roProp; 093 } 094 095 private Boolean bitProp; 096 097 public boolean isBitProp() { 098 return bitProp != null && bitProp; 099 } 100 101 public Boolean getBitProp() { 102 return bitProp; 103 } 104 105 public void setBitProp(Boolean bitProp) { 106 this.bitProp = bitProp; 107 } 108 109 private boolean booleanProp; 110 111 public boolean isBooleanProp() { 112 return booleanProp; 113 } 114 115 public void setBooleanProp(boolean booleanProp) { 116 this.booleanProp = booleanProp; 117 } 118 119 private Timestamp timestampProp; 120 121 public Timestamp getTimestampProp() { 122 return timestampProp; 123 } 124 125 public void setTimestampProp(Timestamp timestampProp) { 126 this.timestampProp = timestampProp; 127 } 128 129 private Date dateProp; 130 131 public Date getDateProp() { 132 return dateProp; 133 } 134 135 public void setDateProp(Date dateProp) { 136 this.dateProp = dateProp; 137 } 138 139 private int intProp; 140 141 public int getIntProp() { 142 return intProp; 143 } 144 145 private BigDecimal bigDecimalProp; 146 147 public BigDecimal getBigDecimalProp() { 148 return bigDecimalProp; 149 } 150 151 public void setBigDecimalProp(BigDecimal bigDecimalProp) { 152 this.bigDecimalProp = bigDecimalProp; 153 } 154 155 public void setIntProp(int intProp) { 156 this.intProp = intProp; 157 } 158 159 private Integer integerProp; 160 161 public Integer getIntegerProp() { 162 return integerProp; 163 } 164 165 public void setIntegerProp(Integer integerProp) { 166 this.integerProp = integerProp; 167 } 168 169 private TestBean next; 170 171 public TestBean getNext() { 172 return next; 173 } 174 175 public void setNext(TestBean next) { 176 this.next = next; 177 } 178 179 private List<String> stuffs; 180 181 public List<String> getStuffs() { 182 return stuffs; 183 } 184 185 public void setStuffs(List<String> stuffs) { 186 this.stuffs = stuffs; 187 } 188 189 private Object[] arrayProp; 190 191 public Object[] getArrayProp() { 192 return arrayProp; 193 } 194 195 public void setArrayProp(Object[] arrayProp) { 196 this.arrayProp = arrayProp; 197 } 198 199 private Map<String, Object> mapProp; 200 201 public Map<String, Object> getMapProp() { 202 return this.mapProp; 203 } 204 205 public void setMapProp(Map<String, Object> mapProp) { 206 this.mapProp = mapProp; 207 } 208 209 /** 210 * @return the complexProp 211 */ 212 public TestBeanTwo getComplexProp() { 213 return this.complexProp; 214 } 215 216 /** 217 * @param complexProp the complexProp to set 218 */ 219 public void setComplexProp(TestBeanTwo complexProp) { 220 this.complexProp = complexProp; 221 } 222 } 223 224 public static class TestBeanTwo { 225 226 private String fooProp; 227 228 /** 229 * @return the fooProp 230 */ 231 public String getFooProp() { 232 return this.fooProp; 233 } 234 235 /** 236 * @param fooProp the fooProp to set 237 */ 238 public void setFooProp(String fooProp) { 239 this.fooProp = fooProp; 240 } 241 } 242 243 @Test 244 public void testSetBoolean() { 245 TestBean tb = new TestBean(); 246 ObjectPropertyUtils.setPropertyValue(tb, "booleanProp", "true"); 247 assertTrue(tb.isBooleanProp()); 248 } 249 250 @Test 251 public void testGetPropertyDescriptor() { 252 Map<String, PropertyDescriptor> pds = ObjectPropertyUtils.getPropertyDescriptors(TestBean.class); 253 assertNotNull(pds.get("rwProp")); 254 assertNotNull(pds.get("roProp")); 255 assertNotNull(pds.get("woProp")); 256 assertNull(pds.get("foobar")); 257 } 258 259 @Test 260 public void testGet() { 261 TestBean tb = new TestBean(); 262 tb.setRwProp("foobar"); 263 assertEquals("foobar", ObjectPropertyUtils.getPropertyValue(tb, "rwProp")); 264 265 tb.roProp = "barbaz"; 266 assertEquals("barbaz", ObjectPropertyUtils.getPropertyValue(tb, "roProp")); 267 268 try { 269 ObjectPropertyUtils.getPropertyValue(tb, "woProp"); 270 // KULRICE-10677 - should return null - fail("expected exception"); 271 } catch (RuntimeException e) { 272 // KULRICE-10677 - should return null 273 throw e; 274 } 275 } 276 277 @Test 278 public void testLookup() { 279 TestBean tb = new TestBean(); 280 tb.roProp = "barbaz"; 281 assertEquals("barbaz", ObjectPropertyUtils.getPropertyValue(tb, "roProp")); 282 283 Map<String, Object> tm = new java.util.HashMap<String, Object>(); 284 tb.setMapProp(tm); 285 tm.put("barbaz", "hooray!"); 286 tm.put("bar.baz", "hoorah!"); 287 tm.put("bar.[baz]", "foobah!"); 288 tm.put("b'('r.[\"ain)\"s]", "zombie!"); 289 assertEquals("hooray!", ObjectPropertyUtils.getPropertyValue(tb, "mapProp[barbaz]")); 290 assertEquals("hooray!", ObjectPropertyUtils.getPropertyValue(tb, "mapProp['barbaz']")); 291 assertEquals("hooray!", ObjectPropertyUtils.getPropertyValue(tb, "mapProp[\"barbaz\"]")); 292 assertEquals("hoorah!", ObjectPropertyUtils.getPropertyValue(tb, "mapProp[bar.baz]")); 293 assertEquals("foobah!", ObjectPropertyUtils.getPropertyValue(tb, "mapProp[bar.[baz]]")); 294 assertEquals("zombie!", ObjectPropertyUtils.getPropertyValue(tb, "mapProp['b'('r.[\"ain)\"s]']")); 295 assertEquals("zombie!", ObjectPropertyUtils.getPropertyValue(tb, "mapProp[b'('r.[\"ain)\"s]]")); 296 297 TestBean tb2 = new TestBean(); 298 tb2.setRwProp("foodbar"); 299 tb.setNext(tb2); 300 tm.put("blah", new Object[]{"next", "rwProp"}); 301 tm.put("baz", tb2); 302 assertTrue(ObjectPropertyUtils.isReadableProperty(tb, "mapProp[\"baz\"].rwProp")); 303 assertEquals("barbaz", ObjectPropertyUtils.getPropertyValue(tb, "roProp")); 304 assertEquals("foodbar", ObjectPropertyUtils.getPropertyValue(tb, "next.rwProp")); 305 306 tb.setStuffs(Arrays.asList(new String[]{"foo", "bar", "baz",})); 307 assertEquals("bar", ObjectPropertyUtils.getPropertyValue(tb, "stuffs[1]")); 308 309 TestBean rb = new TestBean(); 310 TestBean nb = new TestBean(); 311 TestBean lb = new TestBean(); 312 rb.setNext(nb); 313 nb.setNext(lb); 314 assertEquals(String.class, ObjectPropertyUtils.getPropertyType(rb, "next.next.rwProp")); 315 rb.setRwProp("baz"); 316 nb.setRwProp("bar"); 317 lb.setRwProp("foo"); 318 assertEquals("foo", ObjectPropertyUtils.getPropertyValue(rb, "next.next.rwProp")); 319 } 320 321 @Test 322 public void testSet() { 323 TestBean tb = new TestBean(); 324 ObjectPropertyUtils.setPropertyValue(tb, "rwProp", "foobar"); 325 assertEquals("foobar", tb.getRwProp()); 326 327 ObjectPropertyUtils.setPropertyValue(tb, "woProp", "barbaz"); 328 assertEquals("barbaz", tb.woProp); 329 330 try { 331 ObjectPropertyUtils.setPropertyValue(tb, "roProp", "bazfoo"); 332 fail("expected exception"); 333 } catch (Exception E) { 334 // OK! 335 } 336 337 long now = System.currentTimeMillis(); 338 ObjectPropertyUtils.setPropertyValue(tb, "dateProp", new java.sql.Date(now)); 339 assertEquals(now, tb.getDateProp().getTime()); 340 } 341 342 @Test 343 public void testPathSet() { 344 TestBean tb = new TestBean(); 345 ObjectPropertyUtils.setPropertyValue(tb, "rwProp", "bar"); 346 assertEquals("bar", tb.getRwProp()); 347 ObjectPropertyUtils.setPropertyValue(tb, "next", new TestBean()); 348 ObjectPropertyUtils.setPropertyValue(tb, "next.next", new TestBean()); 349 ObjectPropertyUtils.setPropertyValue(tb, "next.next.woProp", "baz"); 350 assertEquals("baz", tb.getNext().getNext().woProp); 351 } 352 353 @Test 354 public void testBulk() { 355 Map<String, String> pd = new java.util.HashMap<String, String>(); 356 pd.put("rwProp", "foobar"); 357 pd.put("intProp", "3"); 358 pd.put("booleanProp", "true"); 359 pd.put("stuffs", "foo,bar,baz"); 360 for (int i = 0; i < 10000; i++) { 361 TestBean tb = new TestBean(); 362 ObjectPropertyUtils.copyPropertiesToObject(pd, tb); 363 assertEquals("foobar", tb.getRwProp()); 364 assertEquals(3, tb.getIntProp()); 365 assertEquals(true, tb.isBooleanProp()); 366 assertEquals(3, tb.getStuffs().size()); 367 assertEquals("foo", tb.getStuffs().get(0)); 368 assertEquals("bar", tb.getStuffs().get(1)); 369 assertEquals("baz", tb.getStuffs().get(2)); 370 } 371 } 372 373 @Test 374 public void testReadWriteCheck() { 375 TestBean tb = new TestBean(); 376 assertTrue(ObjectPropertyUtils.isReadableProperty(tb, "rwProp")); 377 assertTrue(ObjectPropertyUtils.isWritableProperty(tb, "rwProp")); 378 assertTrue(ObjectPropertyUtils.isReadableProperty(tb, "roProp")); 379 assertFalse(ObjectPropertyUtils.isWritableProperty(tb, "roProp")); 380 assertFalse(ObjectPropertyUtils.isReadableProperty(tb, "woProp")); 381 assertTrue(ObjectPropertyUtils.isWritableProperty(tb, "woProp")); 382 } 383 384 @Test 385 public void testKradUifTemplateHeaderMetadata() { 386 FormView formView = new FormView(); 387 ViewHeader viewHeader = new ViewHeader(); 388 formView.setHeader(viewHeader); 389 Message headerMetadataMessage = new Message(); 390 viewHeader.setMetadataMessage(headerMetadataMessage); 391 assertSame(headerMetadataMessage, ObjectPropertyUtils.getPropertyValue(formView, "header.metadataMessage")); 392 } 393 394 /** 395 * Collection list item type, for testing UIF interaction with ObjectPropertyUtils. 396 */ 397 public static class CollectionTestItem { 398 399 /** 400 * A string property, called foobar. 401 */ 402 String foobar; 403 404 /** 405 * @return the foobar 406 */ 407 public String getFoobar() { 408 return this.foobar; 409 } 410 411 /** 412 * @param foobar the foobar to set 413 */ 414 public void setFoobar(String foobar) { 415 this.foobar = foobar; 416 } 417 418 } 419 420 /** 421 * Reference to a collection, for testing UIF interaction with ObjectPropertyUtils. 422 */ 423 public static class CollectionTestListRef { 424 425 /** 426 * The collection. 427 */ 428 List<CollectionTestItem> bar; 429 430 /** 431 * Mapping of new line items. 432 */ 433 Map<String, CollectionTestItem> baz; 434 435 /** 436 * @return the bar 437 */ 438 public List<CollectionTestItem> getBar() { 439 return this.bar; 440 } 441 442 /** 443 * @param bar the bar to set 444 */ 445 public void setBar(List<CollectionTestItem> bar) { 446 this.bar = bar; 447 } 448 449 /** 450 * @return the baz 451 */ 452 public Map<String, CollectionTestItem> getBaz() { 453 return this.baz; 454 } 455 456 /** 457 * @param baz the baz to set 458 */ 459 public void setBaz(Map<String, CollectionTestItem> baz) { 460 this.baz = baz; 461 } 462 463 } 464 465 /** 466 * Mock collection form for UIF interaction with ObjectPropertyUtils. 467 */ 468 public static class CollectionTestForm extends UifFormBase { 469 470 private static final long serialVersionUID = 1798800132492441253L; 471 472 /** 473 * Reference to a data object that has a collection. 474 */ 475 CollectionTestListRef foo; 476 477 /** 478 * @return the foo 479 */ 480 public CollectionTestListRef getFoo() { 481 return this.foo; 482 } 483 484 /** 485 * @param foo the foo to set 486 */ 487 public void setFoo(CollectionTestListRef foo) { 488 this.foo = foo; 489 } 490 491 } 492 493 @Test 494 public void testKradUifCollectionGroupBuilder() throws Throwable { 495 UifUnitTestUtils.establishMockConfig(ObjectPropertyUtilsTest.class.getSimpleName()); 496 UifUnitTestUtils.establishMockUserSession("testuser"); 497 try { 498 // Performance medium generates this property path: 499 // newCollectionLines['newCollectionLines_'mediumCollection1'_.subList'] 500 501 // Below recreates the stack trace that ensued due to poorly escaped quotes, 502 // and proves that the parser works around bad quoting in a manner similar to BeanWrapper 503 504 final CollectionGroupBuilder collectionGroupBuilder = new CollectionGroupBuilder(); 505 final CollectionTestForm form = new CollectionTestForm(); 506 CollectionTestItem item = new CollectionTestItem(); 507 item.setFoobar("barfoo"); 508 ObjectPropertyUtils.setPropertyValue(form, "foo.baz['foo_bar_'badquotes'_.foobar']", item); 509 assertEquals("barfoo", form.foo.baz.get("foo_bar_'badquotes'_.foobar").foobar); 510 511 final FormView view = new FormView(); 512 view.setFormClass(CollectionTestForm.class); 513 view.setViewHelperService(new ViewHelperServiceImpl()); 514 view.setPresentationController(new ViewPresentationControllerBase()); 515 view.setAuthorizer(UifUnitTestUtils.getAllowMostViewAuthorizer()); 516 517 final CollectionGroup collectionGroup = new CollectionGroupBase(); 518 collectionGroup.setCollectionObjectClass(CollectionTestItem.class); 519 collectionGroup.setAddLinePropertyName("addLineFoo"); 520 521 StackedLayoutManager layoutManager = new StackedLayoutManagerBase(); 522 Group lineGroupPrototype = new GroupBase(); 523 layoutManager.setLineGroupPrototype(lineGroupPrototype); 524 collectionGroup.setLayoutManager(layoutManager); 525 526 BindingInfo addLineBindingInfo = new BindingInfo(); 527 addLineBindingInfo.setBindingPath("foo.baz['foo_bar_'badquotes'_.foobar']"); 528 collectionGroup.setAddLineBindingInfo(addLineBindingInfo); 529 530 BindingInfo collectionBindingInfo = new BindingInfo(); 531 collectionBindingInfo.setBindingPath("foo.bar"); 532 collectionGroup.setBindingInfo(collectionBindingInfo); 533 534 ViewLifecycle.encapsulateLifecycle(view, form, null, null, new Runnable() { 535 @Override 536 public void run() { 537 collectionGroupBuilder.build(view, form, collectionGroup.<CollectionGroup> copy()); 538 }}); 539 } finally { 540 GlobalVariables.setUserSession(null); 541 GlobalVariables.clear(); 542 GlobalResourceLoader.stop(); 543 } 544 } 545 546 @Test 547 public void testSetStringMapFromInt() { 548 Action action = new Action(); 549 ObjectPropertyUtils.setPropertyValue(action, "actionParameters['lineIndex']", 34); 550 assertEquals("34", action.getActionParameter("lineIndex")); 551 } 552 553 @Test 554 public void testClassNavigation() { 555 assertEquals(String.class, ObjectPropertyUtils.getPropertyType(TestBean.class, "complexProp.fooProp")); 556 557 try { 558 // valid first reference, invalid second reference 559 assertEquals(null, ObjectPropertyUtils.getPropertyType(TestBean.class, "complexProp.foobar")); 560 // NULL is ok - fail("KULRICE-10677 - is this ok?"); 561 } catch (IllegalArgumentException e) { 562 // IAE is not ok - KULRICE-10677 is this ok? 563 throw e; 564 } 565 566 try { 567 // invalid single reference 568 assertEquals(null, ObjectPropertyUtils.getPropertyType(TestBean.class, "foo")); 569 // NULL is ok - fail("KULRICE-10677 - is this ok?"); 570 } catch (IllegalArgumentException e) { 571 // IAE is not ok - KULRICE-10677 is this ok? 572 throw e; 573 } 574 575 try { 576 // invalid first reference 577 assertEquals(null, ObjectPropertyUtils.getPropertyType(TestBean.class, "foo.bar")); 578 // NULL is ok - fail("KULRICE-10677 - is this ok?"); 579 } catch (IllegalArgumentException e) { 580 // IAE is not ok - KULRICE-10677 is this ok? 581 throw e; 582 } 583 } 584 585 @Test 586 public void testPropertySplitPath() { 587 String path = "foo.foo1.foo2"; 588 String[] splitPaths = ObjectPropertyUtils.splitPropertyPath(path); 589 590 assertEquals(3, splitPaths.length); 591 assertEquals("foo", splitPaths[0]); 592 assertEquals("foo1", splitPaths[1]); 593 assertEquals("foo2", splitPaths[2]); 594 595 path = "foo[1]"; 596 splitPaths = ObjectPropertyUtils.splitPropertyPath(path); 597 598 assertEquals(1, splitPaths.length); 599 assertEquals("foo[1]", splitPaths[0]); 600 601 path = "foo.foo1['key.nested'].foo2"; 602 splitPaths = ObjectPropertyUtils.splitPropertyPath(path); 603 604 assertEquals(3, splitPaths.length); 605 assertEquals("foo", splitPaths[0]); 606 assertEquals("foo1['key.nested']", splitPaths[1]); 607 assertEquals("foo2", splitPaths[2]); 608 609 path = "foo.foo1['key.nested'].foo2.foo3['key.nest.nest'].foo4"; 610 splitPaths = ObjectPropertyUtils.splitPropertyPath(path); 611 612 assertEquals(5, splitPaths.length); 613 assertEquals("foo", splitPaths[0]); 614 assertEquals("foo1['key.nested']", splitPaths[1]); 615 assertEquals("foo2", splitPaths[2]); 616 assertEquals("foo3['key.nest.nest']", splitPaths[3]); 617 assertEquals("foo4", splitPaths[4]); 618 } 619 620 // Classes used by testGetterInInterfaceOrSuperHasWiderType to check covariant return types on JDK6 621 622 // Holds a concrete superclass of KualiPercent 623 public interface KualiDecimalHolder { 624 KualiDecimal getDecimal(); 625 } 626 627 // Holds an interface that is implemented by Integer 628 public interface ComparableHolder { 629 Comparable getComparable(); 630 } 631 632 // Holds an abstract class that is extended by Integer 633 public interface NumberHolder { 634 Number getNumber(); 635 } 636 637 public class NumberedImplOne implements NumberHolder { 638 @Override 639 public Integer getNumber() { 640 return 1; 641 } 642 } 643 644 public abstract class AbstractNumberHolder implements NumberHolder { 645 @Override 646 public abstract Number getNumber(); 647 } 648 649 public class ConcreteNumberHolder extends AbstractNumberHolder { 650 @Override 651 public Number getNumber() { 652 return 1; 653 } 654 } 655 656 public class ConcreteNarrowedNumberHolder extends ConcreteNumberHolder { 657 @Override 658 public Integer getNumber() { 659 return 1; 660 } 661 } 662 663 public class ConcreteNarrowedNumberHolderSub extends ConcreteNarrowedNumberHolder { 664 665 } 666 667 public class ComparableHolderImpl implements ComparableHolder { 668 @Override 669 public Integer getComparable() { 670 return 1; 671 } 672 } 673 674 public class KualiPercentHolder implements KualiDecimalHolder { 675 @Override 676 public KualiPercent getDecimal() { 677 return new KualiPercent(1d); 678 } 679 } 680 681 /** 682 * Verifies (at least when run on Linux in JDK6) our fix for the JDK6 Introspector bug/shortcoming WRT covariant 683 * return types that results in a wider getter method being preferred over a more specific implementation getter 684 * method. 685 * 686 * <p>This makes the type reported by Introspector for read methods depending on the order of Methods depend on the 687 * order that they are returned by reflection on a class, which has been demonstrated to vary between platforms.</p> 688 */ 689 @Test 690 public void testGetterInInterfaceOrSuperHasWiderType() { 691 Method readMethod = null; 692 693 readMethod = ObjectPropertyUtils.getReadMethod(ComparableHolderImpl.class, "comparable"); 694 assertEquals(Integer.class, readMethod.getReturnType()); 695 696 readMethod = ObjectPropertyUtils.getReadMethod(NumberedImplOne.class, "number"); 697 assertEquals(Integer.class, readMethod.getReturnType()); 698 699 readMethod = ObjectPropertyUtils.getReadMethod(ConcreteNarrowedNumberHolder.class, "number"); 700 assertEquals(Integer.class, readMethod.getReturnType()); 701 702 readMethod = ObjectPropertyUtils.getReadMethod(ConcreteNarrowedNumberHolderSub.class, "number"); 703 assertEquals(Integer.class, readMethod.getReturnType()); 704 705 // This case is *not* covered by our workaround, and would fail w/ JDK 6 on Linux if enabled. 706 // The interface has a concrete superclass, which will be returned in JDK6 on Linux where the 707 // Method order returned by reflection on a class is different, and the Introspector isn't smart 708 // enough to ask which Method return type is more specific. 709 readMethod = ObjectPropertyUtils.getReadMethod(KualiPercentHolder.class, "decimal"); 710 711 if (readMethod.getReturnType() == KualiDecimal.class) { 712 LOG.info("I bet you're using JDK6 on Linux"); 713 } 714 715 // Other cases to test if we have to refine this functionality: 716 // * similar to the ConcreteNarrowedNumberHolder, 717 // but creating an abstract impl of AbstractKualiDecimalHolder as the intermediate class 718 // * similar to ConcreteNarrowedNumberHolderSub, but ConcreteNarrowedKualiDecimalHolderSub 719 } 720 721 }