Source Code Cross Referenced for BufferedValueModel.java in  » Swing-Library » jgoodies-data-binding » com » jgoodies » binding » value » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Swing Library » jgoodies data binding » com.jgoodies.binding.value 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Copyright (c) 2002-2007 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.binding.value;
032:
033:        import java.beans.PropertyChangeEvent;
034:        import java.beans.PropertyChangeListener;
035:
036:        /**
037:         * A ValueModel that wraps another ValueModel, the subject,
038:         * and delays changes of the subject's value. Returns the subject's value
039:         * until a value has been set. The buffered value is not written to the
040:         * subject until the trigger channel changes to <code>Boolean.TRUE</code>.
041:         * The buffered value can be flushed by changing the trigger channel value
042:         * to <code>Boolean.FALSE</code>. Note that the commit and flush events
043:         * are performed only if the trigger channel fires a change event. Since a
044:         * plain ValueHolder fires no property change event if a value is set that has
045:         * been set before, it is recommended to use a {@link Trigger} instead
046:         * and invoke its <code>#triggerCommit</code> and <code>triggerFlush</code>
047:         * methods.<p>
048:         *
049:         * The BufferedValueModel has been designed to behave much like its subject
050:         * when accessing the value. Therefore it throws all exceptions that would
051:         * arise when accessing the subject directly. Hence, attempts to read or
052:         * write a value while the subject is <code>null</code> are always rejected
053:         * with a <code>NullPointerException</code>.<p>
054:         *
055:         * This class provides the bound read-write properties <em>subject</em> and
056:         * <em>triggerChannel</em> for the subject and trigger channel and a bound
057:         * read-only property <em>buffering</em> for the buffering state.<p>
058:         *
059:         * The BufferedValueModel registers listeners with the subject and
060:         * trigger channel. It is recommended to remove these listeners by invoking
061:         * <code>#release</code> if the subject and trigger channel live much longer
062:         * than this buffer. After <code>#release</code> has been called
063:         * you must not use the BufferedValueModel instance any longer.
064:         * As an alternative you may use event listener lists in subjects and
065:         * trigger channels that are based on <code>WeakReference</code>s.<p>
066:         *
067:         * If the subject value changes while this model is in buffering state
068:         * this change won't show through as this model's new value. If you want
069:         * to update the value whenever the subject value changes, register a
070:         * listener with the subject value and flush this model's trigger.<p>
071:         *
072:         * <strong>Constraints:</strong> The subject is of type <code>Object</code>,
073:         * the trigger channel value of type <code>Boolean</code>.
074:         *
075:         * @author Karsten Lentzsch
076:         * @version $Revision: 1.9 $
077:         *
078:         * @see     ValueModel
079:         * @see     ValueModel#getValue()
080:         * @see     ValueModel#setValue(Object)
081:         */
082:        public final class BufferedValueModel extends AbstractValueModel {
083:
084:            // Names of the bound bean properties *************************************
085:
086:            /**
087:             * The name of the bound read-only bean property that indicates
088:             * whether this models is buffering or in write-through state.
089:             *
090:             * @see #isBuffering()
091:             */
092:            public static final String PROPERTYNAME_BUFFERING = "buffering";
093:
094:            /**
095:             * The name of the bound read-write bean property for the subject.
096:             *
097:             * @see #getSubject()
098:             * @see #setSubject(ValueModel)
099:             */
100:            public static final String PROPERTYNAME_SUBJECT = "subject";
101:
102:            /**
103:             * The name of the bound read-write bean property for the trigger channel.
104:             *
105:             * @see #getTriggerChannel()
106:             * @see #setTriggerChannel(ValueModel)
107:             */
108:            public static final String PROPERTYNAME_TRIGGER_CHANNEL = "triggerChannel";
109:
110:            // ************************************************************************
111:
112:            /**
113:             * Holds the subject that provides the underlying value
114:             * of type <code>Object</code>.
115:             */
116:            private ValueModel subject;
117:
118:            /**
119:             * Holds the three-state trigger of type <code>Boolean</code>.
120:             */
121:            private ValueModel triggerChannel;
122:
123:            /**
124:             * Holds the buffered value. This value is ignored if we are not buffering.
125:             */
126:            private Object bufferedValue;
127:
128:            /**
129:             * Indicates whether a value has been assigned since the last trigger change.
130:             */
131:            private boolean valueAssigned;
132:
133:            /**
134:             * Holds a PropertyChangeListener that observes subject value changes.
135:             */
136:            private final ValueChangeHandler valueChangeHandler;
137:
138:            /**
139:             * Holds a PropertyChangeListener that observes trigger changes.
140:             */
141:            private final TriggerChangeHandler triggerChangeHandler;
142:
143:            // Instance Creation ****************************************************
144:
145:            /**
146:             * Constructs a BufferedValueModel on the given subject
147:             * using the given trigger channel.
148:             *
149:             * @param subject          the value model to be buffered
150:             * @param triggerChannel   the value model that triggers the commit or flush event
151:             * @throws NullPointerException  if the triggerChannel is <code>null</code>
152:             */
153:            public BufferedValueModel(ValueModel subject,
154:                    ValueModel triggerChannel) {
155:                valueChangeHandler = new ValueChangeHandler();
156:                triggerChangeHandler = new TriggerChangeHandler();
157:                setSubject(subject);
158:                setTriggerChannel(triggerChannel);
159:                setBuffering(false);
160:            }
161:
162:            // Accessing the Subject and Trigger Channel ******************************
163:
164:            /**
165:             * Returns the subject, i.e. the underlying ValueModel that provides
166:             * the unbuffered value.
167:             *
168:             * @return the ValueModel that provides the unbuffered value
169:             */
170:            public ValueModel getSubject() {
171:                return subject;
172:            }
173:
174:            /**
175:             * Sets a new subject ValueModel, i.e. the model that provides
176:             * the unbuffered value. Notifies all listeners that the <i>subject</i>
177:             * property has changed.
178:             *
179:             * @param newSubject  the subject ValueModel to be set
180:             */
181:            public void setSubject(ValueModel newSubject) {
182:                ValueModel oldSubject = getSubject();
183:                ReadAccessResult oldReadValue = readBufferedOrSubjectValue();
184:                Object oldValue = oldReadValue.value;
185:                if (oldSubject != null) {
186:                    oldSubject.removeValueChangeListener(valueChangeHandler);
187:                }
188:                subject = newSubject;
189:                if (newSubject != null) {
190:                    newSubject.addValueChangeListener(valueChangeHandler);
191:                }
192:                firePropertyChange(PROPERTYNAME_SUBJECT, oldSubject, newSubject);
193:                if (isBuffering())
194:                    return;
195:
196:                ReadAccessResult newReadValue = readBufferedOrSubjectValue();
197:                Object newValue = newReadValue.value;
198:                // TODO: Check if the following conditional is valid.
199:                // Note that the old and/or new value may be null
200:                // just because the property is read-only.
201:                if (oldValue != null || newValue != null) {
202:                    fireValueChange(oldValue, newValue, true);
203:                }
204:            }
205:
206:            /**
207:             * Returns the ValueModel that is used to trigger commit and flush events.
208:             *
209:             * @return the ValueModel that is used to trigger commit and flush events
210:             */
211:            public ValueModel getTriggerChannel() {
212:                return triggerChannel;
213:            }
214:
215:            /**
216:             * Sets the ValueModel that triggers the commit and flush events.
217:             *
218:             * @param newTriggerChannel  the ValueModel to be set as trigger channel
219:             * @throws NullPointerException  if the newTriggerChannel is <code>null</code>
220:             */
221:            public void setTriggerChannel(ValueModel newTriggerChannel) {
222:                if (newTriggerChannel == null)
223:                    throw new NullPointerException(
224:                            "The trigger channel must not be null.");
225:
226:                ValueModel oldTriggerChannel = getTriggerChannel();
227:                if (oldTriggerChannel != null) {
228:                    oldTriggerChannel
229:                            .removeValueChangeListener(triggerChangeHandler);
230:                }
231:                triggerChannel = newTriggerChannel;
232:                //if (newTriggerChannel != null) {
233:                newTriggerChannel.addValueChangeListener(triggerChangeHandler);
234:                //}
235:                firePropertyChange(PROPERTYNAME_TRIGGER_CHANNEL,
236:                        oldTriggerChannel, newTriggerChannel);
237:            }
238:
239:            // Implementing the ValueModel Interface ********************************
240:
241:            /**
242:             * Returns the subject's value if no value has been set since the last
243:             * commit or flush, and returns the buffered value otherwise.
244:             * Attempts to read a value when no subject is set are rejected
245:             * with a NullPointerException.
246:             *
247:             * @return the buffered value
248:             * @throws NullPointerException  if no subject is set
249:             */
250:            public Object getValue() {
251:                if (subject == null)
252:                    throw new NullPointerException(
253:                            "The subject must not be null "
254:                                    + "when reading a value from a BufferedValueModel.");
255:
256:                return isBuffering() ? bufferedValue : subject.getValue();
257:            }
258:
259:            /**
260:             * Sets a new buffered value and turns this BufferedValueModel into
261:             * the buffering state. The buffered value is not provided to the
262:             * underlying model until the trigger channel indicates a commit.
263:             * Attempts to set a value when no subject is set are rejected
264:             * with a NullPointerException.<p>
265:             *
266:             * The above semantics is easy to understand, however it is tempting
267:             * to check the new value against the current subject value to avoid
268:             * that the buffer unnecessary turns into the buffering state. But here's
269:             * a problem. Let's say the subject value is "first" at buffer
270:             * creation time, and let's say the subject value has changed in the
271:             * meantime to "second". Now someone sets the value "second" to this buffer.
272:             * The subject value and the value to be set are equal. Shall we buffer?
273:             * Also, this decision would depend on the ability to read the subject.
274:             * The semantics would depend on the subject' state and capabilities.<p>
275:             *
276:             * It is often sufficient to observe the buffering state when enabling
277:             * or disabling a commit command button like "OK" or "Apply".
278:             * And later check the <em>changed</em> state in a PresentationModel.
279:             * You may want to do better and may want to observe a property like
280:             * "defersTrueChange" that indicates whether flushing a buffer will
281:             * actually change the subject. But note that such a state may change
282:             * with subject value changes, which may be hard to understand for a user.<p>
283:             *
284:             * TODO: Consider adding an optimized execution path for the case
285:             * that this model is already in buffering state. In this case
286:             * the old buffered value can be used instead of invoking
287:             * <code>#readBufferedOrSubjectValue()</code>.
288:             *
289:             * @param newBufferedValue   the value to be buffered
290:             * @throws NullPointerException  if no subject is set
291:             */
292:            public void setValue(Object newBufferedValue) {
293:                if (subject == null)
294:                    throw new NullPointerException(
295:                            "The subject must not be null "
296:                                    + "when setting a value to a BufferedValueModel.");
297:
298:                ReadAccessResult oldReadValue = readBufferedOrSubjectValue();
299:                Object oldValue = oldReadValue.value;
300:                bufferedValue = newBufferedValue;
301:                setBuffering(true);
302:                if (oldReadValue.readable && oldValue == newBufferedValue)
303:                    return;
304:                fireValueChange(oldValue, newBufferedValue, true);
305:            }
306:
307:            /**
308:             * Tries to lookup the current buffered or subject value
309:             * and returns this value plus a marker that indicates
310:             * whether the read-access succeeded or failed.
311:             * The latter situation arises in an attempt to read a value from
312:             * a write-only subject if this BufferedValueModel is not buffering
313:             * and if this model changes its subject.
314:             *
315:             * @return the current value plus a boolean that indicates the success or failure
316:             */
317:            private ReadAccessResult readBufferedOrSubjectValue() {
318:                try {
319:                    Object value = getValue(); // May fail with write-only models
320:                    return new ReadAccessResult(value, true);
321:                } catch (Exception e) {
322:                    return new ReadAccessResult(null, false);
323:                }
324:            }
325:
326:            // Releasing PropertyChangeListeners **************************************
327:
328:            /**
329:             * Removes the PropertyChangeListeners from the subject and
330:             * trigger channel.<p>
331:             *
332:             * To avoid memory leaks it is recommended to invoke this method
333:             * if the subject and trigger channel live much longer than this buffer.
334:             * Once #release has been invoked the BufferedValueModel instance
335:             * must not be used any longer.<p>
336:             *
337:             * As an alternative you may use event listener lists in subjects and
338:             * trigger channels that are based on <code>WeakReference</code>s.
339:             *
340:             * @see java.lang.ref.WeakReference
341:             */
342:            public void release() {
343:                ValueModel aSubject = getSubject();
344:                if (aSubject != null) {
345:                    aSubject.removeValueChangeListener(valueChangeHandler);
346:                }
347:                ValueModel aTriggerChannel = getTriggerChannel();
348:                if (aTriggerChannel != null) {
349:                    aTriggerChannel
350:                            .removeValueChangeListener(triggerChangeHandler);
351:                }
352:            }
353:
354:            // Misc *****************************************************************
355:
356:            /**
357:             * Returns whether this model buffers a value or not, that is, whether
358:             * a value has been assigned since the last commit or flush.
359:             *
360:             * @return true if a value has been assigned since the last commit or flush
361:             */
362:            public boolean isBuffering() {
363:                return valueAssigned;
364:            }
365:
366:            private void setBuffering(boolean newValue) {
367:                boolean oldValue = isBuffering();
368:                valueAssigned = newValue;
369:                firePropertyChange(PROPERTYNAME_BUFFERING, oldValue, newValue);
370:            }
371:
372:            /**
373:             * Sets the buffered value as new subject value - if any value has been set.
374:             * After this commit this BufferedValueModel behaves as if no value
375:             * has been set before. This method is invoked if the trigger has changed
376:             * to {@code Boolean.TRUE}.<p>
377:             *
378:             * Since the subject's value is assigned <em>after</em> the buffer marker
379:             * is reset, subject change notifications will be handled. In this case
380:             * the subject's old value is not this BufferedValueModel's old value;
381:             * instead the old value reported to listeners of this model
382:             * is the formerly buffered value.
383:             *
384:             * @throws NullPointerException   if no subject is set
385:             */
386:            private void commit() {
387:                if (isBuffering()) {
388:                    setBuffering(false);
389:                    valueChangeHandler.oldValue = bufferedValue;
390:                    subject.setValue(bufferedValue);
391:                    valueChangeHandler.oldValue = null;
392:                } else if (subject == null)
393:                    throw new NullPointerException(
394:                            "The subject must not be null "
395:                                    + "while committing a value in a BufferedValueModel.");
396:            }
397:
398:            /**
399:             * Flushes the buffered value. This method is invoked if the trigger
400:             * has changed to {@code Boolean.FALSE}. After this flush
401:             * this BufferedValueModel behaves as if no value has been set before.<p>
402:             *
403:             * TODO: Check whether we need to use #getValueSafe instead of #getValue.
404:             *
405:             * @throws NullPointerException   if no subject is set
406:             */
407:            private void flush() {
408:                Object oldValue = getValue();
409:                setBuffering(false);
410:                Object newValue = getValue();
411:                fireValueChange(oldValue, newValue, true);
412:            }
413:
414:            // Helper Class ***********************************************************
415:
416:            /**
417:             * Describes the result of a subject value read-access plus a marker
418:             * that indicates if the value could be read or not. The latter is
419:             * used in <code>#setValue</code> to suppress some unnecessary
420:             * change notifications in case the value could be read successfully.
421:             *
422:             * @see BufferedValueModel#setValue(Object)
423:             */
424:            private static final class ReadAccessResult {
425:
426:                final Object value;
427:                final boolean readable;
428:
429:                private ReadAccessResult(Object value, boolean readable) {
430:                    this .value = value;
431:                    this .readable = readable;
432:                }
433:
434:            }
435:
436:            // Event Handling *********************************************************
437:
438:            /**
439:             * Listens to changes of the subject.
440:             */
441:            private final class ValueChangeHandler implements 
442:                    PropertyChangeListener {
443:
444:                Object oldValue;
445:
446:                /**
447:                 * The subject's value has changed. Notifies this BufferedValueModel's
448:                 * listeners iff we are not buffering, does nothing otherwise.<p>
449:                 *
450:                 * @param evt   the property change event to be handled
451:                 */
452:                public void propertyChange(PropertyChangeEvent evt) {
453:                    if (!isBuffering()) {
454:                        fireValueChange(oldValue != null ? oldValue : evt
455:                                .getOldValue(), evt.getNewValue(), true);
456:                    }
457:                }
458:            }
459:
460:            /**
461:             * Listens to changes of the trigger channel.
462:             */
463:            private final class TriggerChangeHandler implements 
464:                    PropertyChangeListener {
465:
466:                /**
467:                 * The trigger has been changed. Commits or flushes the buffered value.
468:                 *
469:                 * @param evt   the property change event to be handled
470:                 */
471:                public void propertyChange(PropertyChangeEvent evt) {
472:                    if (Boolean.TRUE.equals(evt.getNewValue()))
473:                        commit();
474:                    else if (Boolean.FALSE.equals(evt.getNewValue()))
475:                        flush();
476:                }
477:            }
478:
479:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.