Introduce ExtendedBeanInfo
Decorator for instances returned from Introspector#getBeanInfo(Class<?>) that supports detection and inclusion of non-void returning setter methods. Fully supports indexed properties and otherwise faithfully mimics the default BeanInfo#getPropertyDescriptors() behavior, e.g., PropertyDescriptor ordering, etc. This decorator has been integrated with CachedIntrospectionResults meaning that, in simple terms, the Spring container now supports injection of setter methods having any return type. Issue: SPR-8079
This commit is contained in:
parent
ec1b230ae5
commit
2f5085aef1
|
|
@ -221,7 +221,7 @@ public class CachedIntrospectionResults {
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Getting BeanInfo for class [" + beanClass.getName() + "]");
|
logger.trace("Getting BeanInfo for class [" + beanClass.getName() + "]");
|
||||||
}
|
}
|
||||||
this.beanInfo = Introspector.getBeanInfo(beanClass);
|
this.beanInfo = new ExtendedBeanInfo(Introspector.getBeanInfo(beanClass));
|
||||||
|
|
||||||
// Immediately remove class from Introspector cache, to allow for proper
|
// Immediately remove class from Introspector cache, to allow for proper
|
||||||
// garbage collection on class loader shutdown - we cache it here anyway,
|
// garbage collection on class loader shutdown - we cache it here anyway,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,326 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2011 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.awt.Image;
|
||||||
|
import java.beans.BeanDescriptor;
|
||||||
|
import java.beans.BeanInfo;
|
||||||
|
import java.beans.EventSetDescriptor;
|
||||||
|
import java.beans.IndexedPropertyDescriptor;
|
||||||
|
import java.beans.IntrospectionException;
|
||||||
|
import java.beans.Introspector;
|
||||||
|
import java.beans.MethodDescriptor;
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorates a standard {@link BeanInfo} object (likely created created by
|
||||||
|
* {@link Introspector#getBeanInfo(Class)}) by including non-void returning setter
|
||||||
|
* methods in the collection of {@link #getPropertyDescriptors() property descriptors}.
|
||||||
|
* Both regular and
|
||||||
|
* <a href="http://download.oracle.com/javase/tutorial/javabeans/properties/indexed.html">
|
||||||
|
* indexed properties</a> are fully supported.
|
||||||
|
*
|
||||||
|
* <p>The wrapped {@code BeanInfo} object is not modified in any way.
|
||||||
|
*
|
||||||
|
* @author Chris Beams
|
||||||
|
* @since 3.1
|
||||||
|
* @see CachedIntrospectionResults
|
||||||
|
*/
|
||||||
|
public class ExtendedBeanInfo implements BeanInfo {
|
||||||
|
private final BeanInfo delegate;
|
||||||
|
private final SortedSet<PropertyDescriptor> propertyDescriptors =
|
||||||
|
new TreeSet<PropertyDescriptor>(new PropertyDescriptorComparator());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap the given delegate {@link BeanInfo} instance and find any non-void returning
|
||||||
|
* setter methods, creating and adding a {@link PropertyDescriptor} for each.
|
||||||
|
*
|
||||||
|
* <p>The wrapped {@code BeanInfo} is not modified in any way by this process.
|
||||||
|
*
|
||||||
|
* @see #getPropertyDescriptors()
|
||||||
|
* @throws IntrospectionException if any problems occur creating and adding new {@code PropertyDescriptors}
|
||||||
|
*/
|
||||||
|
public ExtendedBeanInfo(BeanInfo delegate) throws IntrospectionException {
|
||||||
|
this.delegate = delegate;
|
||||||
|
|
||||||
|
// PropertyDescriptor instances from the delegate object are never added directly, but always
|
||||||
|
// copied to the local collection of #propertyDescriptors and returned by calls to
|
||||||
|
// #getPropertyDescriptors(). this algorithm iterates through all methods (method descriptors)
|
||||||
|
// in the wrapped BeanInfo object, copying any existing PropertyDescriptor or creating a new
|
||||||
|
// one for any non-standard setter methods found.
|
||||||
|
|
||||||
|
ALL_METHODS:
|
||||||
|
for (MethodDescriptor md : delegate.getMethodDescriptors()) {
|
||||||
|
Method method = md.getMethod();
|
||||||
|
|
||||||
|
// bypass non-getter java.lang.Class methods for efficiency
|
||||||
|
if (ReflectionUtils.isObjectMethod(method) && !method.getName().startsWith("get")) {
|
||||||
|
continue ALL_METHODS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// is the method a NON-INDEXED setter? ignore return type in order to capture non-void signatures
|
||||||
|
if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) {
|
||||||
|
String propertyName = propertyNameFor(method);
|
||||||
|
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
|
||||||
|
Method readMethod = pd.getReadMethod();
|
||||||
|
Method writeMethod = pd.getWriteMethod();
|
||||||
|
// has the setter already been found by the wrapped BeanInfo?
|
||||||
|
if (writeMethod != null
|
||||||
|
&& writeMethod.getName().equals(method.getName())) {
|
||||||
|
// yes -> copy it, including corresponding getter method (if any -- may be null)
|
||||||
|
this.addOrUpdatePropertyDescriptor(propertyName, readMethod, writeMethod);
|
||||||
|
continue ALL_METHODS;
|
||||||
|
}
|
||||||
|
// has a getter corresponding to this setter already been found by the wrapped BeanInfo?
|
||||||
|
if (readMethod != null
|
||||||
|
&& readMethod.getName().equals(getterMethodNameFor(propertyName))
|
||||||
|
&& readMethod.getReturnType().equals(method.getParameterTypes()[0])) {
|
||||||
|
this.addOrUpdatePropertyDescriptor(propertyName, readMethod, method);
|
||||||
|
continue ALL_METHODS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// the setter method was not found by the wrapped BeanInfo -> add a new PropertyDescriptor for it
|
||||||
|
// no corresponding getter was detected, so the 'read method' parameter is null.
|
||||||
|
this.addOrUpdatePropertyDescriptor(propertyName, null, method);
|
||||||
|
continue ALL_METHODS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// is the method an INDEXED setter? ignore return type in order to capture non-void signatures
|
||||||
|
if (method.getName().startsWith("set") && method.getParameterTypes().length == 2 && method.getParameterTypes()[0].equals(int.class)) {
|
||||||
|
String propertyName = propertyNameFor(method);
|
||||||
|
DELEGATE_PD:
|
||||||
|
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
|
||||||
|
if (!(pd instanceof IndexedPropertyDescriptor)) {
|
||||||
|
continue DELEGATE_PD;
|
||||||
|
}
|
||||||
|
IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
|
||||||
|
Method readMethod = ipd.getReadMethod();
|
||||||
|
Method writeMethod = ipd.getWriteMethod();
|
||||||
|
Method indexedReadMethod = ipd.getIndexedReadMethod();
|
||||||
|
Method indexedWriteMethod = ipd.getIndexedWriteMethod();
|
||||||
|
// has the setter already been found by the wrapped BeanInfo?
|
||||||
|
if (indexedWriteMethod != null
|
||||||
|
&& indexedWriteMethod.getName().equals(method.getName())) {
|
||||||
|
// yes -> copy it, including corresponding getter method (if any -- may be null)
|
||||||
|
this.addOrUpdatePropertyDescriptor(propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod);
|
||||||
|
continue ALL_METHODS;
|
||||||
|
}
|
||||||
|
// has a getter corresponding to this setter already been found by the wrapped BeanInfo?
|
||||||
|
if (indexedReadMethod != null
|
||||||
|
&& indexedReadMethod.getName().equals(getterMethodNameFor(propertyName))
|
||||||
|
&& indexedReadMethod.getReturnType().equals(method.getParameterTypes()[1])) {
|
||||||
|
this.addOrUpdatePropertyDescriptor(propertyName, readMethod, writeMethod, indexedReadMethod, method);
|
||||||
|
continue ALL_METHODS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// the INDEXED setter method was not found by the wrapped BeanInfo -> add a new PropertyDescriptor
|
||||||
|
// for it. no corresponding INDEXED getter was detected, so the 'indexed read method' parameter is null.
|
||||||
|
this.addOrUpdatePropertyDescriptor(propertyName, null, null, null, method);
|
||||||
|
continue ALL_METHODS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the method is not a setter, but is it a getter?
|
||||||
|
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
|
||||||
|
// have we already copied this read method to a property descriptor locally?
|
||||||
|
for (PropertyDescriptor existingPD : this.propertyDescriptors) {
|
||||||
|
if (method.equals(pd.getReadMethod())
|
||||||
|
&& existingPD.getName().equals(pd.getName())) {
|
||||||
|
if (existingPD.getReadMethod() == null) {
|
||||||
|
// no -> add it now
|
||||||
|
this.addOrUpdatePropertyDescriptor(pd.getName(), method, pd.getWriteMethod());
|
||||||
|
}
|
||||||
|
// yes -> do not add a duplicate
|
||||||
|
continue ALL_METHODS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (method == pd.getReadMethod()
|
||||||
|
|| (pd instanceof IndexedPropertyDescriptor && method == ((IndexedPropertyDescriptor) pd).getIndexedReadMethod())) {
|
||||||
|
// yes -> copy it, including corresponding setter method (if any -- may be null)
|
||||||
|
if (pd instanceof IndexedPropertyDescriptor) {
|
||||||
|
this.addOrUpdatePropertyDescriptor(pd.getName(), pd.getReadMethod(), pd.getWriteMethod(), ((IndexedPropertyDescriptor)pd).getIndexedReadMethod(), ((IndexedPropertyDescriptor)pd).getIndexedWriteMethod());
|
||||||
|
} else {
|
||||||
|
this.addOrUpdatePropertyDescriptor(pd.getName(), pd.getReadMethod(), pd.getWriteMethod());
|
||||||
|
}
|
||||||
|
continue ALL_METHODS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addOrUpdatePropertyDescriptor(String propertyName, Method readMethod, Method writeMethod) throws IntrospectionException {
|
||||||
|
addOrUpdatePropertyDescriptor(propertyName, readMethod, writeMethod, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addOrUpdatePropertyDescriptor(String propertyName, Method readMethod, Method writeMethod, Method indexedReadMethod, Method indexedWriteMethod) throws IntrospectionException {
|
||||||
|
for (PropertyDescriptor existingPD : this.propertyDescriptors) {
|
||||||
|
if (existingPD.getName().equals(propertyName)) {
|
||||||
|
// is there already a descriptor that captures this read method or its corresponding write method?
|
||||||
|
if (existingPD.getReadMethod() != null) {
|
||||||
|
if (readMethod != null && existingPD.getReadMethod().getReturnType() != readMethod.getReturnType()
|
||||||
|
|| writeMethod != null && existingPD.getReadMethod().getReturnType() != writeMethod.getParameterTypes()[0]) {
|
||||||
|
// no -> add a new descriptor for it below
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update the existing descriptor's read method
|
||||||
|
if (readMethod != null) {
|
||||||
|
try {
|
||||||
|
existingPD.setReadMethod(readMethod);
|
||||||
|
} catch (IntrospectionException ex) {
|
||||||
|
// there is a conflicting setter method present -> null it out and try again
|
||||||
|
existingPD.setWriteMethod(null);
|
||||||
|
existingPD.setReadMethod(readMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// is there already a descriptor that captures this write method or its corresponding read method?
|
||||||
|
if (existingPD.getWriteMethod() != null) {
|
||||||
|
if (readMethod != null && existingPD.getWriteMethod().getParameterTypes()[0] != readMethod.getReturnType()
|
||||||
|
|| writeMethod != null && existingPD.getWriteMethod().getParameterTypes()[0] != writeMethod.getParameterTypes()[0]) {
|
||||||
|
// no -> add a new descriptor for it below
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update the existing descriptor's write method
|
||||||
|
if (writeMethod != null) {
|
||||||
|
existingPD.setWriteMethod(writeMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// is this descriptor indexed?
|
||||||
|
if (existingPD instanceof IndexedPropertyDescriptor) {
|
||||||
|
IndexedPropertyDescriptor existingIPD = (IndexedPropertyDescriptor) existingPD;
|
||||||
|
|
||||||
|
// is there already a descriptor that captures this indexed read method or its corresponding indexed write method?
|
||||||
|
if (existingIPD.getIndexedReadMethod() != null) {
|
||||||
|
if (indexedReadMethod != null && existingIPD.getIndexedReadMethod().getReturnType() != indexedReadMethod.getReturnType()
|
||||||
|
|| indexedWriteMethod != null && existingIPD.getIndexedReadMethod().getReturnType() != indexedWriteMethod.getParameterTypes()[1]) {
|
||||||
|
// no -> add a new descriptor for it below
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update the existing descriptor's indexed read method
|
||||||
|
try {
|
||||||
|
existingIPD.setIndexedReadMethod(indexedReadMethod);
|
||||||
|
} catch (IntrospectionException ex) {
|
||||||
|
// there is a conflicting indexed setter method present -> null it out and try again
|
||||||
|
existingIPD.setIndexedWriteMethod(null);
|
||||||
|
existingIPD.setIndexedReadMethod(indexedReadMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// is there already a descriptor that captures this indexed write method or its corresponding indexed read method?
|
||||||
|
if (existingIPD.getIndexedWriteMethod() != null) {
|
||||||
|
if (indexedReadMethod != null && existingIPD.getIndexedWriteMethod().getParameterTypes()[1] != indexedReadMethod.getReturnType()
|
||||||
|
|| indexedWriteMethod != null && existingIPD.getIndexedWriteMethod().getParameterTypes()[1] != indexedWriteMethod.getParameterTypes()[1]) {
|
||||||
|
// no -> add a new descriptor for it below
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update the existing descriptor's indexed write method
|
||||||
|
if (indexedWriteMethod != null) {
|
||||||
|
existingIPD.setIndexedWriteMethod(indexedWriteMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the descriptor has been updated -> return immediately
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we haven't yet seen read or write methods for this property -> add a new descriptor
|
||||||
|
if (indexedReadMethod == null && indexedWriteMethod == null) {
|
||||||
|
this.propertyDescriptors.add(new PropertyDescriptor(propertyName, readMethod, writeMethod));
|
||||||
|
} else {
|
||||||
|
this.propertyDescriptors.add(new IndexedPropertyDescriptor(propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String propertyNameFor(Method method) {
|
||||||
|
return Introspector.decapitalize(method.getName().substring(3,method.getName().length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getterMethodNameFor(String name) {
|
||||||
|
return "get" + StringUtils.capitalize(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeanInfo[] getAdditionalBeanInfo() {
|
||||||
|
return delegate.getAdditionalBeanInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeanDescriptor getBeanDescriptor() {
|
||||||
|
return delegate.getBeanDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDefaultEventIndex() {
|
||||||
|
return delegate.getDefaultEventIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDefaultPropertyIndex() {
|
||||||
|
return delegate.getDefaultPropertyIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EventSetDescriptor[] getEventSetDescriptors() {
|
||||||
|
return delegate.getEventSetDescriptors();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Image getIcon(int arg0) {
|
||||||
|
return delegate.getIcon(arg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodDescriptor[] getMethodDescriptors() {
|
||||||
|
return delegate.getMethodDescriptors();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the set of {@link PropertyDescriptor}s from the wrapped {@link BeanInfo}
|
||||||
|
* object as well as {@code PropertyDescriptor}s for each non-void returning setter
|
||||||
|
* method found during construction.
|
||||||
|
* @see #ExtendedBeanInfo(BeanInfo)
|
||||||
|
*/
|
||||||
|
public PropertyDescriptor[] getPropertyDescriptors() {
|
||||||
|
return this.propertyDescriptors.toArray(new PropertyDescriptor[this.propertyDescriptors.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts PropertyDescriptor instances alphanumerically to emulate the behavior of {@link java.beans.BeanInfo#getPropertyDescriptors()}.
|
||||||
|
*
|
||||||
|
* @see ExtendedBeanInfo#propertyDescriptors
|
||||||
|
*/
|
||||||
|
static class PropertyDescriptorComparator implements Comparator<PropertyDescriptor> {
|
||||||
|
public int compare(PropertyDescriptor desc1, PropertyDescriptor desc2) {
|
||||||
|
String left = desc1.getName();
|
||||||
|
String right = desc2.getName();
|
||||||
|
for (int i = 0; i < left.length(); i++) {
|
||||||
|
if (right.length() == i) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int result = left.getBytes()[i] - right.getBytes()[i];
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return left.length() - right.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,587 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2011 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 static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||||
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||||
|
import static org.hamcrest.CoreMatchers.nullValue;
|
||||||
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
|
import static org.hamcrest.Matchers.lessThan;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.beans.BeanInfo;
|
||||||
|
import java.beans.IndexedPropertyDescriptor;
|
||||||
|
import java.beans.IntrospectionException;
|
||||||
|
import java.beans.Introspector;
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
|
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.ExtendedBeanInfo.PropertyDescriptorComparator;
|
||||||
|
|
||||||
|
import test.beans.TestBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link ExtendedBeanInfo}.
|
||||||
|
*
|
||||||
|
* @author Chris Beams
|
||||||
|
* @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);
|
||||||
|
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 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);
|
||||||
|
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 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);
|
||||||
|
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);
|
||||||
|
ExtendedBeanInfo 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);
|
||||||
|
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(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);
|
||||||
|
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 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);
|
||||||
|
ExtendedBeanInfo 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@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);
|
||||||
|
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 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);
|
||||||
|
ExtendedBeanInfo 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);
|
||||||
|
BeanInfo ebi = new ExtendedBeanInfo(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(true));
|
||||||
|
|
||||||
|
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);
|
||||||
|
BeanInfo ebi = new ExtendedBeanInfo(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(true));
|
||||||
|
|
||||||
|
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
|
||||||
|
assertThat(hasWriteMethodForProperty(ebi, "foos"), is(true));
|
||||||
|
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
public String getFoo() { return null; }
|
||||||
|
public Integer setFoo(String foo) { 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(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);
|
||||||
|
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 propertyCountsMatch() throws IntrospectionException {
|
||||||
|
BeanInfo bi = Introspector.getBeanInfo(TestBean.class);
|
||||||
|
ExtendedBeanInfo 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);
|
||||||
|
ExtendedBeanInfo 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);
|
||||||
|
ExtendedBeanInfo 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 {
|
||||||
|
PropertyDescriptorComparator c = new 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
assertThat(propertyName + " property is not indexed", pd, instanceOf(IndexedPropertyDescriptor.class));
|
||||||
|
return ((IndexedPropertyDescriptor)pd).getIndexedWriteMethod() != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasIndexedReadMethodForProperty(BeanInfo beanInfo, String propertyName) {
|
||||||
|
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
|
||||||
|
if (pd.getName().equals(propertyName)) {
|
||||||
|
assertThat(propertyName + " property is not indexed", pd, instanceOf(IndexedPropertyDescriptor.class));
|
||||||
|
return ((IndexedPropertyDescriptor)pd).getIndexedReadMethod() != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue