com.jgoodies.binding.beans.PropertyAccessor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jgoodies-binding Show documentation
Show all versions of jgoodies-binding Show documentation
The JGoodies Binding library connects object properties to Swing user interface components.
And it helps you represent the state and behavior of a presentation independently
of the GUI components used in the interface.
/*
* Copyright (c) 2002-2013 JGoodies Software GmbH. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of JGoodies Software GmbH nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jgoodies.binding.beans;
import static com.jgoodies.common.base.Preconditions.checkArgument;
import static com.jgoodies.common.base.Preconditions.checkNotNull;
import java.beans.PropertyVetoException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* An unmodifiable Object that describes and provides access to a bean property.
*
* @author Karsten Lentzsch
* @version $Revision: 1.2 $
*
* @since 2.2
*/
public final class PropertyAccessor {
private final String propertyName;
private final Method readMethod;
private final Method writeMethod;
// Instance Creation ------------------------------------------------------
/**
* Constructs a PropertyAcessor for the given property name,
* reader and writer.
*
* @param propertyName
* the name of the property, e.g. "name", "enabled"
* @param readMethod
* the method that returns the property value,
* e.g. {@code getName()}, {@code isEnabled()}
* @param writeMethod
* the method that sets the property value,
* e.g. {@code setName(String)}, {@code setEnabled(boolean)}
* @throws NullPointerException if {@code propertyName} is null
*/
public PropertyAccessor(String propertyName,
Method readMethod,
Method writeMethod) {
checkNotNull(propertyName, "The property name must not be null.");
checkArgument(readMethod != null || writeMethod != null,
"Either the reader or writer must not be null.");
this.propertyName = propertyName;
this.readMethod = readMethod;
this.writeMethod = writeMethod;
}
// Accessors -------------------------------------------------------------
/**
* @return the bean property name.
*/
public String getPropertyName() {
return propertyName;
}
/**
* @return the type of the accessed bean property
*/
public Class> getPropertyType() {
return readMethod != null
? readMethod.getReturnType()
: writeMethod.getParameterTypes()[0];
}
/**
* @return the Method used to read the property value
* or {@code null}, if not available
*/
public Method getReadMethod() {
return readMethod;
}
/**
* @return the Method used to write the property value
* or {@code null}, if not available
*/
public Method getWriteMethod() {
return writeMethod;
}
/**
* @return {@code true} if the property cannot be written,
* {@code false} if it can be written
*/
public boolean isReadOnly() {
return writeMethod == null;
}
/**
* @return {@code true} if the property cannot be read,
* {@code false} if it can be read
*/
public boolean isWriteOnly() {
return readMethod == null;
}
// Operations -------------------------------------------------------------
/**
* Invokes this accessor's reader on the given bean.
*
* @return the value of the property that is described by this accessor
* when read from the given bean
*
* @param bean the target bean where the reader is to be invoked
*
* @throws NullPointerException if {@code bean} is {@code null}
* @throws UnsupportedOperationException if the property is write-only
*/
public Object getValue(Object bean) {
checkNotNull(bean, "The bean must not be null.");
if (isWriteOnly()) {
throw PropertyAccessException.createReadAccessException(
bean, this, createMissingGetterCause(bean));
}
try {
return readMethod.invoke(bean, (Object[]) null);
} catch (InvocationTargetException e) {
throw PropertyAccessException.createReadAccessException(
bean, this, e.getCause());
} catch (IllegalAccessException e) {
throw PropertyAccessException.createReadAccessException(
bean, this, e);
}
}
/**
* Invokes this accessor's writer on the given bean with the given value.
*
* @param bean the target bean where the property value shall be set
* @param newValue the value to set
*
* @throws NullPointerException if {@code bean} is {@code null}
* @throws UnsupportedOperationException if the property is write-only
* @throws PropertyVetoException if the invoked writer throws such an exception
*/
public void setValue(Object bean, Object newValue)
throws PropertyVetoException {
checkNotNull(bean, "The bean must not be null.");
if (isReadOnly()) {
throw PropertyAccessException.createWriteAccessException(
bean, newValue, this, createMissingSetterCause(bean));
}
try {
writeMethod.invoke(bean, newValue);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof PropertyVetoException) {
throw (PropertyVetoException) cause;
}
throw PropertyAccessException.createWriteAccessException(
bean, newValue, this, cause);
} catch (IllegalAccessException e) {
throw PropertyAccessException.createWriteAccessException(
bean, newValue, this, e);
} catch (IllegalArgumentException e) {
throw PropertyAccessException.createWriteAccessException(
bean, newValue, this, e);
}
}
private String createMissingGetterCause(Object bean) {
boolean isBooleanProperty = getPropertyType() == Boolean.class
|| getPropertyType() == Boolean.TYPE;
String methodName1 = formatAccessorMethodSignature(bean, "is", null);
String methodName2 = formatAccessorMethodSignature(bean, "get", null);
String typicalMethodOrMethods = isBooleanProperty
? methodName1 + " or " + methodName2
: methodName2;
return String.format("The bean property is write-only; " +
"the bean class has no public getter method, typically " +
"%s.",
typicalMethodOrMethods);
}
private String createMissingSetterCause(Object bean) {
String methodName = formatAccessorMethodSignature(bean, "set", getPropertyType());
return String.format("The bean property is read-only; " +
"the bean class has no public setter method, typically " +
"%s.",
methodName);
}
private String formatAccessorMethodSignature(
Object bean,
String methodPrefix, Class methodParamType) {
return String.format("%1$s#%2$s%3$s(%4$s)",
bean.getClass().getSimpleName(),
methodPrefix,
capitalizedPropertyName(),
methodParamType == null ? "" : methodParamType.getSimpleName());
}
private String capitalizedPropertyName() {
return getPropertyName().substring(0, 1).toUpperCase()
+ getPropertyName().substring(1);
}
// Overriding Superclass Behavior -----------------------------------------
@Override
public boolean equals(Object obj) {
if (this == obj) { return true; }
if (obj == null) { return false; }
if (getClass() != obj.getClass()) { return false; }
PropertyAccessor other = (PropertyAccessor) obj;
if (propertyName == null) {
if (other.propertyName != null) { return false; }
} else if (!propertyName.equals(other.propertyName)) { return false; }
if (readMethod == null) {
if (other.readMethod != null) { return false; }
} else if (!readMethod.equals(other.readMethod)) { return false; }
if (writeMethod == null) {
if (other.writeMethod != null) { return false; }
} else if (!writeMethod.equals(other.writeMethod)) { return false; }
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result +
(propertyName == null ? 0 : propertyName.hashCode());
result = prime * result +
(readMethod == null ? 0 : readMethod.hashCode());
result = prime * result +
(writeMethod == null ? 0 : writeMethod.hashCode());
return result;
}
}