001: /*
002: * Copyright (c) 2002-2006 JGoodies Karsten Lentzsch. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package com.jgoodies.validation.extras;
032:
033: import java.awt.event.ActionEvent;
034: import java.awt.event.ActionListener;
035: import java.beans.PropertyChangeEvent;
036: import java.beans.PropertyChangeListener;
037:
038: import javax.swing.Timer;
039:
040: import com.jgoodies.validation.ValidationResult;
041: import com.jgoodies.validation.ValidationResultModel;
042: import com.jgoodies.validation.util.AbstractValidationResultModel;
043:
044: /**
045: * A ValidationResultModel that defers changes for a specified delay.
046: * Useful to coalesce frequent validation result changes. For example
047: * if a validation requires some heavy computation, you may desire
048: * to perform the validation only for a "stable" input state after
049: * a series of quick input changes.<p>
050: *
051: * Wraps a given subject ValidationResultModel and returns the subject
052: * ValidationResult or the ValidationResult to be set as this model's result.
053: * Observes subject validation result property changes and forwards them
054: * to listeners of this model. If a ValidationResult is set to this model,
055: * a Swing Timer is used to delay the ValidationResult commit to the subject.
056: * A previously started timer - if any - will be stopped before.
057: *
058: * @author Karsten Lentzsch
059: * @version $Revision: 1.7 $
060: */
061: public final class DelayedValidationResultModel extends
062: AbstractValidationResultModel {
063:
064: /**
065: * Refers to the underlying subject ValidationResultModel.
066: */
067: private final ValidationResultModel subject;
068:
069: /**
070: * The Timer used to perform the delayed commit.
071: */
072: private final Timer timer;
073:
074: /**
075: * Holds the most recent pending value. It is updated
076: * every time <code>#setResult</code> is invoked.
077: */
078: private ValidationResult pendingValue = null;
079:
080: // Instance Creation ******************************************************
081:
082: /**
083: * Constructs a DelayedValidationResultModel for the given subject
084: * ValidationResultModel and the specified Timer delay in milliseconds.
085: *
086: * @param subject the underlying (or wrapped) ValidationResultModel
087: * @param delay the milliseconds to wait before a change
088: * shall be committed
089: */
090: public DelayedValidationResultModel(ValidationResultModel subject,
091: int delay) {
092: this .subject = subject;
093: this .timer = new Timer(delay,
094: new ValidationResultCommitListener());
095: timer.setRepeats(false);
096: subject
097: .addPropertyChangeListener(new SubjectPropertyChangeHandler());
098: }
099:
100: // ValueModel Implementation ******************************************
101:
102: /**
103: * Returns the subject's value or in case of a pending commit,
104: * the pending new value.
105: *
106: * @return the subject's current or future value.
107: */
108: public ValidationResult getResult() {
109: return hasPendingChange() ? pendingValue : subject.getResult();
110: }
111:
112: /**
113: * Sets the given new value after this model's delay.
114: * Does nothing if the new value and the latest pending value are the same.
115: *
116: * @param newResult the value to set
117: *
118: * @throws NullPointerException if the new result is <code>null</code>
119: */
120: public void setResult(ValidationResult newResult) {
121: if (newResult == null)
122: throw new NullPointerException(
123: "The new result must not be null.");
124:
125: if (newResult == pendingValue)
126: return;
127: if (hasPendingChange()) {
128: timer.stop();
129: }
130: pendingValue = newResult;
131: timer.start();
132: }
133:
134: // Misc *******************************************************************
135:
136: private boolean hasPendingChange() {
137: return timer.isRunning();
138: }
139:
140: // Event Handling *****************************************************
141:
142: /**
143: * Describes the delayed action to be performed by the timer.
144: */
145: private final class ValidationResultCommitListener implements
146: ActionListener {
147:
148: /**
149: * An ActionEvent has been fired by the Timer after its delay.
150: * Commits the pending ValidationResult and stops the timer.
151: */
152: public void actionPerformed(ActionEvent e) {
153: subject.setResult(pendingValue);
154: timer.stop();
155: }
156: }
157:
158: /**
159: * Forwards validation property changes in the subject.
160: */
161: private final class SubjectPropertyChangeHandler implements
162: PropertyChangeListener {
163:
164: public void propertyChange(PropertyChangeEvent evt) {
165: firePropertyChange(evt.getPropertyName(),
166: evt.getOldValue(), evt.getNewValue());
167: }
168: }
169:
170: }
|