/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans;
import java.beans.BeanInfo;
import java.beans.IndexedPropertyDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.math.BigDecimal;
import org.junit.Test;
import org.springframework.tests.sample.beans.TestBean;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
/**
* @author Chris Beams
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.1
*/
public class ExtendedBeanInfoTests {
@Test
public void standardReadMethodOnly() throws IntrospectionException {
@SuppressWarnings("unused") class C {
public String getFoo() { return null; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(false));
}
@Test
public void standardWriteMethodOnly() throws IntrospectionException {
@SuppressWarnings("unused") class C {
public void setFoo(String f) { }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(true));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
}
@Test
public void standardReadAndWriteMethods() throws IntrospectionException {
@SuppressWarnings("unused") class C {
public void setFoo(String f) { }
public String getFoo() { return null; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(true));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
}
@Test
public void nonStandardWriteMethodOnly() throws IntrospectionException {
@SuppressWarnings("unused") class C {
public C setFoo(String foo) { return this; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
}
@Test
public void standardReadAndNonStandardWriteMethods() throws IntrospectionException {
@SuppressWarnings("unused") class C {
public String getFoo() { return null; }
public C setFoo(String foo) { return this; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
}
@Test
public void standardReadAndNonStandardIndexedWriteMethod() throws IntrospectionException {
@SuppressWarnings("unused") class C {
public String[] getFoo() { return null; }
public C setFoo(int i, String foo) { return this; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
assertThat(hasIndexedWriteMethodForProperty(bi, "foo"), is(false));
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(false));
assertThat(hasIndexedWriteMethodForProperty(ebi, "foo"), is(true));
}
@Test
public void standardReadMethodsAndOverloadedNonStandardWriteMethods() throws Exception {
@SuppressWarnings("unused") class C {
public String getFoo() { return null; }
public C setFoo(String foo) { return this; }
public C setFoo(Number foo) { return this; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) {
if (pd.getName().equals("foo")) {
assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", String.class)));
return;
}
}
fail("never matched write method");
}
@Test
public void cornerSpr9414() throws IntrospectionException {
@SuppressWarnings("unused") class Parent {
public Number getProperty1() {
return 1;
}
}
class Child extends Parent {
@Override
public Integer getProperty1() {
return 2;
}
}
{ // always passes
ExtendedBeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(Parent.class));
assertThat(hasReadMethodForProperty(bi, "property1"), is(true));
}
{ // failed prior to fix for SPR-9414
ExtendedBeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(Child.class));
assertThat(hasReadMethodForProperty(bi, "property1"), is(true));
}
}
@Test
public void cornerSpr9453() throws IntrospectionException {
final class Bean implements Spr9453<Class<?>> {
@Override
public Class<?> getProp() {
return null;
}
}
{ // always passes
BeanInfo info = Introspector.getBeanInfo(Bean.class);
assertThat(info.getPropertyDescriptors().length, equalTo(2));
}
{ // failed prior to fix for SPR-9453
BeanInfo info = new ExtendedBeanInfo(Introspector.getBeanInfo(Bean.class));
assertThat(info.getPropertyDescriptors().length, equalTo(2));
}
}
@Test
public void standardReadMethodInSuperclassAndNonStandardWriteMethodInSubclass() throws Exception {
@SuppressWarnings("unused") class B {
public String getFoo() { return null; }
}
@SuppressWarnings("unused") class C extends B {
public C setFoo(String foo) { return this; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
}
@Test
public void standardReadMethodInSuperAndSubclassesAndGenericBuilderStyleNonStandardWriteMethodInSuperAndSubclasses() throws Exception {
abstract class B<This extends B<This>> {
@SuppressWarnings("unchecked")
protected final This instance = (This) this;
private String foo;
public String getFoo() { return foo; }
public This setFoo(String foo) {
this.foo = foo;
return this.instance;
}
}
class C extends B<C> {
private int bar = -1;
public int getBar() { return bar; }
public C setBar(int bar) {
this.bar = bar;
return this.instance;
}
}
C c = new C()
.setFoo("blue")
.setBar(42);
assertThat(c.getFoo(), is("blue"));
assertThat(c.getBar(), is(42));
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
assertThat(hasReadMethodForProperty(bi, "bar"), is(true));
assertThat(hasWriteMethodForProperty(bi, "bar"), is(false));
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
assertThat(hasReadMethodForProperty(bi, "bar"), is(true));
assertThat(hasWriteMethodForProperty(bi, "bar"), is(false));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
assertThat(hasReadMethodForProperty(ebi, "bar"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "bar"), is(true));
}
@Test
public void nonPublicStandardReadAndWriteMethods() throws Exception {
@SuppressWarnings("unused") class C {
String getFoo() { return null; }
C setFoo(String foo) { return this; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(false));
}
/**
* {@link ExtendedBeanInfo} should behave exactly like {@link BeanInfo}
* in strange edge cases.
*/
@Test
public void readMethodReturnsSupertypeOfWriteMethodParameter() throws IntrospectionException {
@SuppressWarnings("unused") class C {
public Number getFoo() { return null; }
public void setFoo(Integer foo) { }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
assertEquals(hasWriteMethodForProperty(bi, "foo"), hasWriteMethodForProperty(ebi, "foo"));
}
@Test
public void indexedReadMethodReturnsSupertypeOfIndexedWriteMethodParameter() throws IntrospectionException {
@SuppressWarnings("unused") class C {
public Number getFoos(int index) { return null; }
public void setFoos(int index, Integer foo) { }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
assertEquals(hasIndexedWriteMethodForProperty(bi, "foos"), hasIndexedWriteMethodForProperty(ebi, "foos"));
}
/**
* {@link ExtendedBeanInfo} should behave exactly like {@link BeanInfo}
* in strange edge cases.
*/
@Test
public void readMethodReturnsSubtypeOfWriteMethodParameter() throws IntrospectionException {
@SuppressWarnings("unused") class C {
public Integer getFoo() { return null; }
public void setFoo(Number foo) { }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(false));
}
@Test
public void indexedReadMethodReturnsSubtypeOfIndexedWriteMethodParameter() throws IntrospectionException {
@SuppressWarnings("unused") class C {
public Integer getFoos(int index) { return null; }
public void setFoo(int index, Number foo) { }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(false));
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(false));
}
@Test
public void indexedReadMethodOnly() throws IntrospectionException {
@SuppressWarnings("unused")
class C {
// indexed read method
public String getFoos(int i) { return null; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
BeanInfo ebi = new ExtendedBeanInfo(Introspector.getBeanInfo(C.class));
assertThat(hasReadMethodForProperty(bi, "foos"), is(false));
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
assertThat(hasReadMethodForProperty(ebi, "foos"), is(false));
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
}
@Test
public void indexedWriteMethodOnly() throws IntrospectionException {
@SuppressWarnings("unused")
class C {
// indexed write method
public void setFoos(int i, String foo) { }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
BeanInfo ebi = new ExtendedBeanInfo(Introspector.getBeanInfo(C.class));
assertThat(hasWriteMethodForProperty(bi, "foos"), is(false));
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foos"), is(false));
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(true));
}
@Test
public void indexedReadAndIndexedWriteMethods() throws IntrospectionException {
@SuppressWarnings("unused")
class C {
// indexed read method
public String getFoos(int i) { return null; }
// indexed write method
public void setFoos(int i, String foo) { }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
BeanInfo ebi = new ExtendedBeanInfo(Introspector.getBeanInfo(C.class));
assertThat(hasReadMethodForProperty(bi, "foos"), is(false));
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foos"), is(false));
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(true));
assertThat(hasReadMethodForProperty(ebi, "foos"), is(false));
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foos"), is(false));
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(true));
}
@Test
public void readAndWriteAndIndexedReadAndIndexedWriteMethods() throws IntrospectionException {
@SuppressWarnings("unused")
class C {
// read method
public String[] getFoos() { return null; }
// indexed read method
public String getFoos(int i) { return null; }
// write method
public void setFoos(String[] foos) { }
// indexed write method
public void setFoos(int i, String foo) { }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
BeanInfo ebi = new ExtendedBeanInfo(Introspector.getBeanInfo(C.class));
assertThat(hasReadMethodForProperty(bi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foos"), is(true));
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(true));
assertThat(hasReadMethodForProperty(ebi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foos"), is(true));
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(true));
}
@Test
public void indexedReadAndNonStandardIndexedWrite() throws IntrospectionException {
@SuppressWarnings("unused")
class C {
// indexed read method
public String getFoos(int i) { return null; }
// non-standard indexed write method
public C setFoos(int i, String foo) { return this; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
// interesting! standard Inspector picks up non-void return types on indexed write methods by default
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(false));
BeanInfo ebi = new ExtendedBeanInfo(Introspector.getBeanInfo(C.class));
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(true));
}
@Test
public void indexedReadAndNonStandardWriteAndNonStandardIndexedWrite() throws IntrospectionException {
@SuppressWarnings("unused")
class C {
// non-standard write method
public C setFoos(String[] foos) { return this; }
// indexed read method
public String getFoos(int i) { return null; }
// non-standard indexed write method
public C setFoos(int i, String foo) { return this; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foos"), is(false));
// again as above, standard Inspector picks up non-void return types on indexed write methods by default
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(false));
BeanInfo ebi = new ExtendedBeanInfo(Introspector.getBeanInfo(C.class));
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foos"), is(false));
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(false));
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foos"), is(true));
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(true));
}
@Test
public void cornerSpr9702() throws IntrospectionException {
{ // baseline with standard write method
@SuppressWarnings("unused")
class C {
// VOID-RETURNING, NON-INDEXED write method
public void setFoos(String[] foos) { }
// indexed read method
public String getFoos(int i) { return null; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(bi, "foos"), is(false));
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foos"), is(true));
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(false));
BeanInfo ebi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(ebi, "foos"), is(false));
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foos"), is(true));
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(false));
}
{ // variant with non-standard write method
@SuppressWarnings("unused")
class C {
// NON-VOID-RETURNING, NON-INDEXED write method
public C setFoos(String[] foos) { return this; }
// indexed read method
public String getFoos(int i) { return null; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(bi, "foos"), is(false));
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foos"), is(false));
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(false));
BeanInfo ebi = new ExtendedBeanInfo(Introspector.getBeanInfo(C.class));
assertThat(hasReadMethodForProperty(ebi, "foos"), is(false));
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foos"), is(true));
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(false));
}
}
/**
* Prior to SPR-10111 (a follow-up fix for SPR-9702), this method would throw an
* IntrospectionException regarding a "type mismatch between indexed and non-indexed
* methods" intermittently (approximately one out of every four times) under JDK 7
* due to non-deterministic results from {@link Class#getDeclaredMethods()}.
* See http://bugs.sun.com/view_bug.do?bug_id=7023180
* @see #cornerSpr9702()
*/
@Test
public void cornerSpr10111() throws Exception {
new ExtendedBeanInfo(Introspector.getBeanInfo(BigDecimal.class));
}
@Test
public void subclassWriteMethodWithCovariantReturnType() throws IntrospectionException {
@SuppressWarnings("unused") class B {
public String getFoo() { return null; }
public Number setFoo(String foo) { return null; }
}
class C extends B {
@Override
public String getFoo() { return null; }
@Override
public Integer setFoo(String foo) { return null; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
assertThat(ebi.getPropertyDescriptors().length, equalTo(bi.getPropertyDescriptors().length));
}
@Test
public void nonStandardReadMethodAndStandardWriteMethod() throws IntrospectionException {
@SuppressWarnings("unused") class C {
public void getFoo() { }
public void setFoo(String foo) { }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(true));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
}
/**
* Ensures that an empty string is not passed into a PropertyDescriptor constructor. This
* could occur when handling ArrayList.set(int,Object)
*/
@Test
public void emptyPropertiesIgnored() throws IntrospectionException {
@SuppressWarnings("unused") class C {
public Object set(Object o) { return null; }
public Object set(int i, Object o) { return null; }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(ebi.getPropertyDescriptors(), equalTo(bi.getPropertyDescriptors()));
}
@Test
public void overloadedNonStandardWriteMethodsOnly_orderA() throws IntrospectionException, SecurityException, NoSuchMethodException {
@SuppressWarnings("unused") class C {
public Object setFoo(String p) { return new Object(); }
public Object setFoo(int p) { return new Object(); }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) {
if (pd.getName().equals("foo")) {
assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", String.class)));
return;
}
}
fail("never matched write method");
}
@Test
public void overloadedNonStandardWriteMethodsOnly_orderB() throws IntrospectionException, SecurityException, NoSuchMethodException {
@SuppressWarnings("unused") class C {
public Object setFoo(int p) { return new Object(); }
public Object setFoo(String p) { return new Object(); }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
assertThat(hasReadMethodForProperty(ebi, "foo"), is(false));
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) {
if (pd.getName().equals("foo")) {
assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", String.class)));
return;
}
}
fail("never matched write method");
}
/**
* Corners the bug revealed by SPR-8522, in which an (apparently) indexed write method
* without a corresponding indexed read method would fail to be processed correctly by
* ExtendedBeanInfo. The local class C below represents the relevant methods from
* Google's GsonBuilder class. Interestingly, the setDateFormat(int, int) method was
* not actually intended to serve as an indexed write method; it just appears that way.
*/
@Test
public void reproSpr8522() throws IntrospectionException {
@SuppressWarnings("unused") class C {
public Object setDateFormat(String pattern) { return new Object(); }
public Object setDateFormat(int style) { return new Object(); }
public Object setDateFormat(int dateStyle, int timeStyle) { return new Object(); }
}
BeanInfo bi = Introspector.getBeanInfo(C.class);
assertThat(hasReadMethodForProperty(bi, "dateFormat"), is(false));
assertThat(hasWriteMethodForProperty(bi, "dateFormat"), is(false));
assertThat(hasIndexedReadMethodForProperty(bi, "dateFormat"), is(false));
assertThat(hasIndexedWriteMethodForProperty(bi, "dateFormat"), is(false));
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "dateFormat"), is(false));
assertThat(hasWriteMethodForProperty(bi, "dateFormat"), is(false));
assertThat(hasIndexedReadMethodForProperty(bi, "dateFormat"), is(false));
assertThat(hasIndexedWriteMethodForProperty(bi, "dateFormat"), is(false));
assertThat(hasReadMethodForProperty(ebi, "dateFormat"), is(false));
assertThat(hasWriteMethodForProperty(ebi, "dateFormat"), is(true));
assertThat(hasIndexedReadMethodForProperty(ebi, "dateFormat"), is(false));
assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(false));
}
@Test
public void propertyCountsMatch() throws IntrospectionException {
BeanInfo bi = Introspector.getBeanInfo(TestBean.class);
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(ebi.getPropertyDescriptors().length, equalTo(bi.getPropertyDescriptors().length));
}
@Test
public void propertyCountsWithNonStandardWriteMethod() throws IntrospectionException {
class ExtendedTestBean extends TestBean {
@SuppressWarnings("unused")
public ExtendedTestBean setFoo(String s) { return this; }
}
BeanInfo bi = Introspector.getBeanInfo(ExtendedTestBean.class);
BeanInfo ebi = new ExtendedBeanInfo(bi);
boolean found = false;
for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) {
if (pd.getName().equals("foo")) {
found = true;
}
}
assertThat(found, is(true));
assertThat(ebi.getPropertyDescriptors().length, equalTo(bi.getPropertyDescriptors().length+1));
}
/**
* {@link BeanInfo#getPropertyDescriptors()} returns alphanumerically sorted.
* Test that {@link ExtendedBeanInfo#getPropertyDescriptors()} does the same.
*/
@Test
public void propertyDescriptorOrderIsEqual() throws IntrospectionException {
BeanInfo bi = Introspector.getBeanInfo(TestBean.class);
BeanInfo ebi = new ExtendedBeanInfo(bi);
for (int i = 0; i < bi.getPropertyDescriptors().length; i++) {
assertThat("element " + i + " in BeanInfo and ExtendedBeanInfo propertyDescriptor arrays do not match",
ebi.getPropertyDescriptors()[i].getName(), equalTo(bi.getPropertyDescriptors()[i].getName()));
}
}
@Test
public void propertyDescriptorComparator() throws IntrospectionException {
ExtendedBeanInfo.PropertyDescriptorComparator c = new ExtendedBeanInfo.PropertyDescriptorComparator();
assertThat(c.compare(new PropertyDescriptor("a", null, null), new PropertyDescriptor("a", null, null)), equalTo(0));
assertThat(c.compare(new PropertyDescriptor("abc", null, null), new PropertyDescriptor("abc", null, null)), equalTo(0));
assertThat(c.compare(new PropertyDescriptor("a", null, null), new PropertyDescriptor("b", null, null)), lessThan(0));
assertThat(c.compare(new PropertyDescriptor("b", null, null), new PropertyDescriptor("a", null, null)), greaterThan(0));
assertThat(c.compare(new PropertyDescriptor("abc", null, null), new PropertyDescriptor("abd", null, null)), lessThan(0));
assertThat(c.compare(new PropertyDescriptor("xyz", null, null), new PropertyDescriptor("123", null, null)), greaterThan(0));
assertThat(c.compare(new PropertyDescriptor("a", null, null), new PropertyDescriptor("abc", null, null)), lessThan(0));
assertThat(c.compare(new PropertyDescriptor("abc", null, null), new PropertyDescriptor("a", null, null)), greaterThan(0));
assertThat(c.compare(new PropertyDescriptor("abc", null, null), new PropertyDescriptor("b", null, null)), lessThan(0));
assertThat(c.compare(new PropertyDescriptor(" ", null, null), new PropertyDescriptor("a", null, null)), lessThan(0));
assertThat(c.compare(new PropertyDescriptor("1", null, null), new PropertyDescriptor("a", null, null)), lessThan(0));
assertThat(c.compare(new PropertyDescriptor("a", null, null), new PropertyDescriptor("A", null, null)), greaterThan(0));
}
@Test
public void reproSpr8806() throws IntrospectionException {
// does not throw
Introspector.getBeanInfo(LawLibrary.class);
// does not throw after the changes introduced in SPR-8806
new ExtendedBeanInfo(Introspector.getBeanInfo(LawLibrary.class));
}
@Test
public void cornerSpr8949() throws IntrospectionException {
class A {
@SuppressWarnings("unused")
public boolean isTargetMethod() {
return false;
}
}
class B extends A {
@Override
public boolean isTargetMethod() {
return false;
}
}
BeanInfo bi = Introspector.getBeanInfo(B.class);
// java.beans.Introspector returns the "wrong" declaring class for overridden read
// methods, which in turn violates expectations in {@link ExtendedBeanInfo} regarding
// method equality. Spring's {@link ClassUtils#getMostSpecificMethod(Method, Class)}
// helps out here, and is now put into use in ExtendedBeanInfo as well.
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertThat(hasReadMethodForProperty(bi, "targetMethod"), is(true));
assertThat(hasWriteMethodForProperty(bi, "targetMethod"), is(false));
assertThat(hasReadMethodForProperty(ebi, "targetMethod"), is(true));
assertThat(hasWriteMethodForProperty(ebi, "targetMethod"), is(false));
}
@Test
public void cornerSpr8937AndSpr12582() throws IntrospectionException {
@SuppressWarnings("unused") class A {
public void setAddress(String addr){ }
public void setAddress(int index, String addr) { }
public String getAddress(int index){ return null; }
}
// Baseline:
BeanInfo bi = Introspector.getBeanInfo(A.class);
boolean hasReadMethod = hasReadMethodForProperty(bi, "address");
boolean hasWriteMethod = hasWriteMethodForProperty(bi, "address");
boolean hasIndexedReadMethod = hasIndexedReadMethodForProperty(bi, "address");
boolean hasIndexedWriteMethod = hasIndexedWriteMethodForProperty(bi, "address");
// ExtendedBeanInfo needs to behave exactly like BeanInfo...
BeanInfo ebi = new ExtendedBeanInfo(bi);
assertEquals(hasReadMethod, hasReadMethodForProperty(ebi, "address"));
assertEquals(hasWriteMethod, hasWriteMethodForProperty(ebi, "address"));
assertEquals(hasIndexedReadMethod, hasIndexedReadMethodForProperty(ebi, "address"));
assertEquals(hasIndexedWriteMethod, hasIndexedWriteMethodForProperty(ebi, "address"));
}
@Test
public void shouldSupportStaticWriteMethod() throws IntrospectionException {
{
BeanInfo bi = Introspector.getBeanInfo(WithStaticWriteMethod.class);
assertThat(hasReadMethodForProperty(bi, "prop1"), is(false));
assertThat(hasWriteMethodForProperty(bi, "prop1"), is(false));
assertThat(hasIndexedReadMethodForProperty(bi, "prop1"), is(false));
assertThat(hasIndexedWriteMethodForProperty(bi, "prop1"), is(false));
}
{
BeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(WithStaticWriteMethod.class));
assertThat(hasReadMethodForProperty(bi, "prop1"), is(false));
assertThat(hasWriteMethodForProperty(bi, "prop1"), is(true));
assertThat(hasIndexedReadMethodForProperty(bi, "prop1"), is(false));
assertThat(hasIndexedWriteMethodForProperty(bi, "prop1"), is(false));
}
}
@Test // SPR-12434
public void shouldDetectValidPropertiesAndIgnoreInvalidProperties() throws IntrospectionException {
BeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(java.awt.Window.class));
assertThat(hasReadMethodForProperty(bi, "locationByPlatform"), is(true));
assertThat(hasWriteMethodForProperty(bi, "locationByPlatform"), is(true));
assertThat(hasIndexedReadMethodForProperty(bi, "locationByPlatform"), is(false));
assertThat(hasIndexedWriteMethodForProperty(bi, "locationByPlatform"), is(false));
}
private boolean hasWriteMethodForProperty(BeanInfo beanInfo, String propertyName) {
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
if (pd.getName().equals(propertyName)) {
return pd.getWriteMethod() != null;
}
}
return false;
}
private boolean hasReadMethodForProperty(BeanInfo beanInfo, String propertyName) {
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
if (pd.getName().equals(propertyName)) {
return pd.getReadMethod() != null;
}
}
return false;
}
private boolean hasIndexedWriteMethodForProperty(BeanInfo beanInfo, String propertyName) {
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
if (pd.getName().equals(propertyName)) {
if (!(pd instanceof IndexedPropertyDescriptor)) {
return false;
}
return ((IndexedPropertyDescriptor)pd).getIndexedWriteMethod() != null;
}
}
return false;
}
private boolean hasIndexedReadMethodForProperty(BeanInfo beanInfo, String propertyName) {
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
if (pd.getName().equals(propertyName)) {
if (!(pd instanceof IndexedPropertyDescriptor)) {
return false;
}
return ((IndexedPropertyDescriptor)pd).getIndexedReadMethod() != null;
}
}
return false;
}
interface Spr9453<T> {
T getProp();
}
interface Book {
}
interface TextBook extends Book {
}
interface LawBook extends TextBook {
}
interface BookOperations {
Book getBook();
void setBook(Book book);
}
interface TextBookOperations extends BookOperations {
@Override
TextBook getBook();
}
abstract class Library {
public Book getBook() {
return null;
}
public void setBook(Book book) {
}
}
class LawLibrary extends Library implements TextBookOperations {
@Override
public LawBook getBook() {
return null;
}
}
static class WithStaticWriteMethod {
public static void setProp1(String prop1) {
}
}
}