001: /*
002: * $RCSfile: DeferredProperty.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:57:07 $
010: * $State: Exp $
011: */
012: package javax.media.jai;
013:
014: import java.beans.PropertyChangeEvent;
015: import java.beans.PropertyChangeListener;
016: import java.util.Arrays;
017:
018: /**
019: * A subclass of <code>DeferredData</code> to be used to wrap JAI property
020: * values which will be computed at a later time. For example, an instance
021: * of this class could be used to wrap a property emitted by an operation
022: * node so that the actual computation of the property value was deferred
023: * until it was actually needed.
024: *
025: * @see DeferredData
026: * @see RenderedOp
027: *
028: * @since JAI 1.1
029: */
030: public class DeferredProperty extends DeferredData implements
031: PropertyChangeListener {
032:
033: /**
034: * The <code>PropertySource</code> from which the value of the named
035: * property is to be drawn.
036: */
037: protected transient PropertySource propertySource;
038:
039: /**
040: * The name of the property the value of which is to be obtained.
041: */
042: protected String propertyName;
043:
044: /**
045: * Creates a <code>DeferredProperty</code>. If the specified
046: * <code>PropertySource</code> is a <code>PropertyChangeEmitter</code>,
047: * then this <code>DeferredProperty</code> object is registered as a
048: * <code>PropertyChangeListener</code> of the <code>PropertySource</code>.
049: *
050: * @exception IllegalArgumentException if a parameter is <code>null</code>
051: * or if the <code>propertyName</code> is not among those
052: * emitted by the <code>PropertySource</code>.
053: */
054: public DeferredProperty(PropertySource propertySource,
055: String propertyName, Class propertyClass) {
056: super (propertyClass);
057:
058: if (propertySource == null || propertyName == null) {
059: throw new IllegalArgumentException(JaiI18N
060: .getString("DeferredData0"));
061: }
062:
063: String[] propertyNames = propertySource.getPropertyNames();
064: boolean isPropertyEmitted = false;
065: if (propertyNames != null) {
066: int length = propertyNames.length;
067: for (int i = 0; i < length; i++) {
068: if (propertyName.equalsIgnoreCase(propertyNames[i])) {
069: isPropertyEmitted = true;
070: break;
071: }
072: }
073: }
074:
075: if (!isPropertyEmitted) {
076: throw new IllegalArgumentException(JaiI18N
077: .getString("DeferredProperty0"));
078: }
079:
080: if (propertySource instanceof PropertyChangeEmitter) {
081: PropertyChangeEmitter pce = (PropertyChangeEmitter) propertySource;
082: pce.addPropertyChangeListener(propertyName, this );
083: }
084:
085: this .propertySource = propertySource;
086: this .propertyName = propertyName;
087: }
088:
089: /**
090: * Returns the <code>PropertySource</code> of the property value.
091: */
092: public PropertySource getPropertySource() {
093: return propertySource;
094: }
095:
096: /**
097: * Returns the name of the property the value of which is to be obtained.
098: */
099: public String getPropertyName() {
100: return propertyName;
101: }
102:
103: /**
104: * Returns the value of the image property associated with the
105: * image and property name specified at construction.
106: */
107: protected Object computeData() {
108: return propertySource.getProperty(propertyName);
109: }
110:
111: /**
112: * Tests whether the parameter equals this object. Equality obtains
113: * if the parameter object is a <code>DeferredProperty</code> and
114: * the respective <code>propertySource</code> and
115: * <code>dataClass</code> variables are equal according to their
116: * respective <code>equals()</code> methods and the
117: * <code>propertyName</code>s are equal ignoring case. The wrapped
118: * data object is not tested unless the <code>isValid()</code> of
119: * both objects returns <code>true</code> because requesting it via
120: * <code>getData()</code> may provoke computation of a deferred quantity.
121: */
122: public boolean equals(Object obj) {
123: if (obj == null || !(obj instanceof DeferredProperty)) {
124: return false;
125: }
126:
127: DeferredProperty dp = (DeferredProperty) obj;
128:
129: return propertyName.equalsIgnoreCase(dp.getPropertyName())
130: && propertySource.equals(dp.getPropertySource())
131: && (!isValid() || !dp.isValid() || data.equals(dp
132: .getData()));
133: }
134:
135: /**
136: * Returns a hash code value for the object.
137: */
138: public int hashCode() {
139: return propertySource.hashCode()
140: ^ propertyName.toLowerCase().hashCode();
141: }
142:
143: /**
144: * The implementation of <code>PropertyChangeListener</code>. This
145: * method responds to certain <code>PropertyChangeEvent</code>s generated
146: * by the <code>PropertySource</code> used to construct this object.
147: *
148: * <p> If the <code>PropertyChangeEvent</code> is named "Rendering" and is
149: * an instance of <code>javax.media.jai.RenderingChangeEvent</code>, then
150: * the source of the event is checked to determine whether it equals the
151: * <code>PropertySource</code> used to construct this object. If this test
152: * is passed then the <code>PropertySource</code> is a
153: * <code>RenderedOp</code> the rendering of which has changed. Therefore
154: * setData() will be invoked with a null argument. This will indicate to
155: * any registered observers that the property data should be re-requested
156: * by invoking getData() on this object.
157: *
158: * <p> If the <code>PropertyChangeEvent</code> was generated by the
159: * <code>PropertySource</code> used to construct this object, has name
160: * equal to the name of the deferred property, and is an instance of
161: * <code>PropertySourceChangeEvent</code>, then the value returned by
162: * the <code>getNewValue()</code> method of the event object will be
163: * passed to <code>setData()</code> unless <code>getNewValue()</code>
164: * returns <code>java.awt.Image.UndefinedProperty</code> in which case
165: * <code>null</code> will be passed to <code>setData()</code>. Registered
166: * observers will be notified according to the specification of
167: * <code>setData()</code>.
168: */
169: public void propertyChange(PropertyChangeEvent evt) {
170: if (evt.getSource() == propertySource) {
171: if (evt instanceof RenderingChangeEvent) {
172: setData(null);
173: } else if (evt instanceof PropertySourceChangeEvent
174: && propertyName.equalsIgnoreCase(evt
175: .getPropertyName())) {
176: Object newValue = evt.getNewValue();
177: setData(newValue == java.awt.Image.UndefinedProperty ? null
178: : newValue);
179: }
180: }
181: }
182: }
|