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 }