📄 binding.java
字号:
/*
* Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.beansbinding;
import java.util.List;
import java.util.ArrayList;
import java.beans.*;
/**
* {@code Binding} is an abstract class that represents the concept of a
* binding between two properties, typically of two objects, and contains
* methods for explicitly syncing the values of the two properties. {@code Binding}
* itself does no automatic syncing between property values. Subclasses
* will typically keep the values in sync according to some strategy.
* <p>
* Some {@code Bindings} are managed, often by another {@code Binding}.
* A managed {@code Binding} does not allow certain methods to be called by
* the user. These methods are identified in their documentation.
* Subclasses should call {@code setManaged(true)} to make themselves managed.
* {@code Binding} provides protected versions of the managed methods with the
* suffix {@code "Unmanaged"} for subclasses to use internally without
* checking whether or not they are managed.
* <p>
* Any {@code PropertyResolutionExceptions} thrown by {@code Property}
* objects used by this binding are allowed to flow through to the caller
* of the {@code Binding} methods.
*
* @param <SS> the type of source object
* @param <SV> the type of value that the source property represents
* @param <TS> the type of target object
* @param <TV> the type of value that the target property represents
*
* @author Shannon Hickey
*/
public abstract class Binding<SS, SV, TS, TV> {
private String name;
private SS sourceObject;
private TS targetObject;
private Property<SS, SV> sourceProperty;
private Property<TS, TV> targetProperty;
private Validator<? super SV> validator;
private Converter<SV, TV> converter;
private TV sourceNullValue;
private SV targetNullValue;
private TV sourceUnreadableValue;
private boolean sourceUnreadableValueSet;
private List<BindingListener> listeners;
private PropertyStateListener psl;
private boolean ignoreChange;
private boolean isManaged;
private boolean isBound;
private PropertyChangeSupport changeSupport;
/**
* An enumeration representing the reasons a sync ({@code save} or {@code refresh})
* can fail on a {@code Binding}.
*
* @see Binding#refresh
* @see Binding#save
*/
public enum SyncFailureType {
/**
* A {@code refresh} failed because the {@code Binding's} target property is unwriteable
* for the {@code Binding's} target object.
*/
TARGET_UNWRITEABLE,
/**
* A {@code save} failed because the {@code Binding's} source property is unwriteable
* for the {@code Binding's} source object.
*/
SOURCE_UNWRITEABLE,
/**
* A {@code save} failed because the {@code Binding's} target property is unreadable
* for the {@code Binding's} target object.
*/
TARGET_UNREADABLE,
/**
* A {@code refresh} failed because the {@code Binding's} source property is unreadable
* for the {@code Binding's} source object.
*/
SOURCE_UNREADABLE,
/**
* A {@code save} failed due to a conversion failure on the value
* returned by the {@code Binding's} target property for the {@code Binding's}
* target object.
*/
CONVERSION_FAILED,
/**
* A {@code save} failed due to a validation failure on the value
* returned by the {@code Binding's} target property for the {@code Binding's}
* target object.
*/
VALIDATION_FAILED
}
/**
* {@code SyncFailure} represents a failure to sync ({@code save} or {@code refresh}) a
* {@code Binding}.
*/
public static final class SyncFailure {
private SyncFailureType type;
private Object reason;
private static SyncFailure TARGET_UNWRITEABLE = new SyncFailure(SyncFailureType.TARGET_UNWRITEABLE);
private static SyncFailure SOURCE_UNWRITEABLE = new SyncFailure(SyncFailureType.SOURCE_UNWRITEABLE);
private static SyncFailure TARGET_UNREADABLE = new SyncFailure(SyncFailureType.TARGET_UNREADABLE);
private static SyncFailure SOURCE_UNREADABLE = new SyncFailure(SyncFailureType.SOURCE_UNREADABLE);
private static SyncFailure conversionFailure(RuntimeException rte) {
return new SyncFailure(rte);
}
private static SyncFailure validationFailure(Validator.Result result) {
return new SyncFailure(result);
}
private SyncFailure(SyncFailureType type) {
if (type == SyncFailureType.CONVERSION_FAILED || type == SyncFailureType.VALIDATION_FAILED) {
throw new IllegalArgumentException();
}
this.type = type;
}
private SyncFailure(RuntimeException exception) {
this.type = SyncFailureType.CONVERSION_FAILED;
this.reason = exception;
}
private SyncFailure(Validator.Result result) {
this.type = SyncFailureType.VALIDATION_FAILED;
this.reason = result;
}
/**
* Returns the type of failure.
*
* @return the type of failure
*/
public SyncFailureType getType() {
return type;
}
/**
* Returns the exception that occurred during conversion if
* this failure represents a conversion failure. Throws
* {@code UnsupportedOperationException} otherwise.
*
* @return the exception that occurred during conversion
* @throws UnsupportedOperationException if the type of failure
* is not {@code SyncFailureType.CONVERSION_FAILED}
*/
public RuntimeException getConversionException() {
if (type != SyncFailureType.CONVERSION_FAILED) {
throw new UnsupportedOperationException();
}
return (RuntimeException)reason;
}
/**
* Returns the result that was returned from the
* {@code Binding's} validator if this failure represents a
* validation failure. Throws {@code UnsupportedOperationException} otherwise.
*
* @return the result that was returned from the {@code Binding's} validator
* @throws UnsupportedOperationException if the type of failure
* is not {@code SyncFailureType.VALIDATION_FAILED}
*/
public Validator.Result getValidationResult() {
if (type != SyncFailureType.VALIDATION_FAILED) {
throw new UnsupportedOperationException();
}
return (Validator.Result)reason;
}
/**
* Returns a string representation of the {@code SyncFailure}. This
* method is intended to be used for debugging purposes only, and
* the content and format of the returned string may vary between
* implementations. The returned string may be empty but may not
* be {@code null}.
*
* @return a string representation of this {@code SyncFailure}
*/
public String toString() {
return type + (reason == null ? "" : ": " + reason.toString());
}
}
/**
* Encapsulates the result from calling
* {@link org.jdesktop.beansbinding.Binding#getSourceValueForTarget} or
* {@link org.jdesktop.beansbinding.Binding#getTargetValueForSource}, which
* can either be a successful value or a failure.
*/
public static final class ValueResult<V> {
private V value;
private SyncFailure failure;
private ValueResult(V value) {
this.value = value;
}
private ValueResult(SyncFailure failure) {
if (failure == null) {
throw new AssertionError();
}
this.failure = failure;
}
/**
* Returns {@code true} if this {@code ValueResult} represents
* a failure and {@code false} otherwise.
*
* @return {@code true} if this {@code ValueResult} represents
* a failure and {@code false} otherwise
* @see #getFailure
*/
public boolean failed() {
return failure != null;
}
/**
* Returns the resulting value if this {@code ValueResult} does
* not represent a failure and throws {@code UnsupportedOperationException}
* otherwise.
*
* @return the resulting value
* @throws UnsupportedOperationException if this {@code ValueResult} represents a failure
* @see #failed
*/
public V getValue() {
if (failed()) {
throw new UnsupportedOperationException();
}
return value;
}
/**
* Returns the failure if this {@code ValueResult} represents
* a failure and throws {@code UnsupportedOperationException}
* otherwise.
*
* @return the failure
* @throws UnsupportedOperationException if this {@code ValueResult} does not represent a failure
* @see #failed
*/
public SyncFailure getFailure() {
if (!failed()) {
throw new UnsupportedOperationException();
}
return failure;
}
/**
* Returns a string representation of the {@code ValueResult}. This
* method is intended to be used for debugging purposes only, and
* the content and format of the returned string may vary between
* implementations. The returned string may be empty but may not
* be {@code null}.
*
* @return a string representation of this {@code ValueResult}
*/
public String toString() {
return value == null ? "failure: " + failure : "value: " + value;
}
}
/**
* Create an instance of {@code Binding} between two properties of two objects.
*
* @param sourceObject the source object
* @param sourceProperty a property on the source object
* @param targetObject the target object
* @param targetProperty a property on the target object
* @param name a name for the {@code Binding}
* @throws IllegalArgumentException if the source property or target property is {@code null}
*/
protected Binding(SS sourceObject, Property<SS, SV> sourceProperty, TS targetObject, Property<TS, TV> targetProperty, String name) {
setSourceProperty(sourceProperty);
setTargetProperty(targetProperty);
this.sourceObject = sourceObject;
this.sourceProperty = sourceProperty;
this.targetObject = targetObject;
this.targetProperty = targetProperty;
this.name = name;
}
/**
* Sets the {@code Binding's} source property.
* <p>
* {@code Binding} fires a property change notification with
* property name {@code "sourceProperty"} when the value of
* this property changes.
* <p>
* This method may not be called on a bound binding.
*
* @param sourceProperty the source property
* @throws IllegalArgumentException if the source property is {@code null}
* @throws IllegalStateException if the {@code Binding} is bound
* @see #isBound()
*/
protected final void setSourceProperty(Property<SS, SV> sourceProperty) {
throwIfBound();
if (sourceProperty == null) {
throw new IllegalArgumentException("source property can't be null");
}
Property<SS, SV> old = this.sourceProperty;
this.sourceProperty = sourceProperty;
firePropertyChange("sourceProperty", old, sourceProperty);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -