Source Code Cross Referenced for PresentationModel.java in  » Swing-Library » jgoodies-data-binding » com » jgoodies » binding » 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 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright (c) 2002-2007 JGoodies Karsten Lentzsch. All Rights Reserved.
0003:         *
0004:         * Redistribution and use in source and binary forms, with or without
0005:         * modification, are permitted provided that the following conditions are met:
0006:         *
0007:         *  o Redistributions of source code must retain the above copyright notice,
0008:         *    this list of conditions and the following disclaimer.
0009:         *
0010:         *  o Redistributions in binary form must reproduce the above copyright notice,
0011:         *    this list of conditions and the following disclaimer in the documentation
0012:         *    and/or other materials provided with the distribution.
0013:         *
0014:         *  o Neither the name of JGoodies Karsten Lentzsch nor the names of
0015:         *    its contributors may be used to endorse or promote products derived
0016:         *    from this software without specific prior written permission.
0017:         *
0018:         * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019:         * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
0020:         * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0021:         * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0022:         * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023:         * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024:         * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
0025:         * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
0026:         * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
0027:         * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
0028:         * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029:         */
0030:
0031:        package com.jgoodies.binding;
0032:
0033:        import java.beans.PropertyChangeEvent;
0034:        import java.beans.PropertyChangeListener;
0035:        import java.beans.PropertyVetoException;
0036:        import java.util.HashMap;
0037:        import java.util.Map;
0038:
0039:        import javax.swing.JComponent;
0040:
0041:        import com.jgoodies.binding.adapter.Bindings;
0042:        import com.jgoodies.binding.beans.*;
0043:        import com.jgoodies.binding.value.*;
0044:
0045:        /**
0046:         * The standard base class to implement the <em>Presentation Model</em> pattern,
0047:         * that represents the state and behavior of a presentation independently
0048:         * of the GUI components used in the interface. This
0049:         * <a href="http://martinfowler.com/eaaDev/PresentationModel.html">pattern</a>
0050:         * is described in Martin Fowler's upcoming
0051:         * <a href="http://martinfowler.com/eaaDev/">addition</a>
0052:         * to his "Patterns of Enterprise Application Architecture". More details
0053:         * around this implementation of the Presentation Model pattern and a 3-tier
0054:         * Swing client architecture with a presentation model layer can be found in
0055:         * the <a href="http://www.jgoodies.com/articles/binding.pdf">JGoodies
0056:         * Binding presentation</a>. This architecture is supported
0057:         * by the JGoodies Binding library. The PresentationModel pattern is known
0058:         * to users of VisualWorks Smalltalk as <em>ApplicationModel</em>.<p>
0059:         *
0060:         * This class minimizes the effort required to bind, edit,
0061:         * buffer, and observe the bound properties of an exchangeable bean.
0062:         * Therefore it provides five groups of features that are described below:<ol>
0063:         * <li>adapt bean properties,
0064:         * <li>change the adapted bean,
0065:         * <li>buffer values,
0066:         * <li>observe the buffering state, and
0067:         * <li>track changes in adapted bean properties.
0068:         * </ol><p>
0069:         *
0070:         * Typically this class will be extended to add custom models, Actions,
0071:         * presentation logic, model operations and other higher-level behavior.
0072:         * However, in simple cases you can use this class as-is.
0073:         * Several methods are intended to be used as-is and a typical subclass
0074:         * should not modify them. For example #isChanged, #isBuffering,
0075:         * #getBean, #setBean, #getBeanChannel, #getModel, #getBufferedModel,
0076:         * #getTriggerChannel, #setTriggerChannel, #triggerCommit and #triggerFlush.<p>
0077:         *
0078:         * <strong>Adapting Bean Properties</strong><br>
0079:         * The method {@link #getModel(String)} vends ValueModels that adapt
0080:         * a bound bean property of an exchangeable bean. These ValueModels will be
0081:         * requested from an underlying BeanAdapter.
0082:         * To get such a model you specify the name of the bean property.
0083:         * All properties adapted must be read-write and must comply with
0084:         * the Java Bean coding conventions.
0085:         * In case you need to adapt a read-only or write-only property,
0086:         * or if the bean uses custom names for the reader and writer,
0087:         * use {@link #getModel(String, String, String)}.
0088:         * Also note that you must not mix calls to these methods for the same
0089:         * property name. For details see the JavaDoc class comment in
0090:         * {@link com.jgoodies.binding.beans.BeanAdapter}.<p>
0091:         *
0092:         * <strong>Changing the Adapted Bean</strong><br>
0093:         * The adapted bean is not stored in this PresentationModel.
0094:         * Instead it is held by a ValueModel, the <em>bean channel</em>
0095:         * - just as in the PropertyAdapter and BeanAdapter.
0096:         * This indirection enables you to manage the adapted bean outside
0097:         * of this PresentationModel, and it enables you to share bean channels
0098:         * between multiple PresentationModels, PropertyAdapters, and BeanAdapters.
0099:         * The bean channel is used by all adapting models created
0100:         * by the factory methods <code>#getModel</code>.
0101:         * You can get and set the current bean by means of <code>#getBean</code>
0102:         * and <code>#setBean</code>. Or you can set a new value to the bean channel.<p>
0103:         *
0104:         * PresentationModel fires three PropertyChangeEvents if the bean changes:
0105:         * <i>beforeBean</i>, <i>bean</i> and <i>afterBean</i>. This is useful
0106:         * when sharing a bean channel and you must perform an operation before
0107:         * or after other listeners handle a bean change. Since you cannot rely
0108:         * on the order listeners will be notified, only the <i>beforeBean</i>
0109:         * and <i>afterBean</i> events are guaranteed to be fired before and
0110:         * after the bean change is fired.
0111:         * Note that <code>#getBean()</code> returns the new bean before
0112:         * any of these three PropertyChangeEvents is fired. Therefore listeners
0113:         * that handle these events must use the event's old and new value
0114:         * to determine the old and new bean.
0115:         * The order of events fired during a bean change is:<ol>
0116:         * <li>the bean channel fires a <i>value</i> change,
0117:         * <li>this model fires a <i>beforeBean</i> change,
0118:         * <li>this model fires the <i>bean</i> change,
0119:         * <li>this model fires an <i>afterBean</i> change.
0120:         * </ol>
0121:         *
0122:         * <strong>Buffering Values</strong><br>
0123:         * At the core of this feature are the methods {@link #getBufferedModel(String)}
0124:         * that vend BufferedValueModels that wrap an adapted bean property.
0125:         * The buffer can be committed or flushed using <code>#triggerCommit</code>
0126:         * and <code>#triggerFlush</code> respectively.<p>
0127:         *
0128:         * The trigger channel is provided as a bound Java bean property
0129:         * <em>triggerChannel</em> that must be a non-<code>null</code>
0130:         * <code>ValueModel</code> with values of type <code>Boolean</code>.
0131:         * Attempts to read or write other value types may be rejected
0132:         * with runtime exceptions.
0133:         * By default the trigger channel is initialized as an instance of
0134:         * <code>Trigger</code>. As an alternative it can be set in the constructor.<p>
0135:         *
0136:         * <strong>Observing the Buffering State</strong><br>
0137:         * This class also provides support for observing the buffering state
0138:         * of the BufferedValueModels created with this model. The buffering state
0139:         * is useful for UI actions and operations that are enabled or disabled
0140:         * if there are pending changes, for example on OK or APPLY button.
0141:         * API users can request the buffering state via <code>#isBuffering</code>
0142:         * and can observe the bound property <em>buffering</em>.<p>
0143:         *
0144:         * <strong>Tracking Changes in the Adapted Bean</strong><br>
0145:         * PresentationModel provides support for observing bean property changes
0146:         * and it tracks all changes to report the overall changed state.
0147:         * The latter is useful to detect whether the bean has changed at all,
0148:         * for example to mark the bean as dirty, so it will be updated in a database.
0149:         * API users can request the changed state via <code>#isChanged</code>
0150:         * and can observe the bound property <em>changed</em>.
0151:         * If you want to track changes of other ValueModels, bean properties,
0152:         * or of submodels, register them using <code>#observeChanged</code>.
0153:         * To reset the changed state invoke <code>#resetChanged</code>.
0154:         * In case you track the changed state of submodels you should override
0155:         * <code>#resetChanged</code> to reset the changed state in these submodels.<p>
0156:         *
0157:         * The changed state changes once only (from false to true). If you need
0158:         * instant notifications about changes in the properties of the target bean,
0159:         * you can register PropertyChangeListeners with this model. This is useful
0160:         * if you change the bean and don't want to move your listeners from one bean
0161:         * to the other. And it's useful if you want to observe multiple bean
0162:         * properties at the same time. These listeners are managed by the method set
0163:         * <code>#addBeanPropertyChangeListener</code> and
0164:         * <code>#removeBeanPropertyChangeListener</code>.
0165:         * Listeners registered via these methods will be removed
0166:         * from the old bean before the bean changes and will be re-added after
0167:         * the new bean has been set. Therefore these listeners will be notified
0168:         * about changes only if the current bean changes a property. They won't be
0169:         * notified if the bean changes - and in turn the property value. If you want
0170:         * to observes property changes caused by bean changes too, register with
0171:         * the adapting ValueModel as returned by <code>#getModel(String)</code>.<p>
0172:         *
0173:         * <strong>Instance Creation</strong><br>
0174:         * PresentationModel can be instantiated using four different constructors:
0175:         * you can specify the target bean directly, or you can provide a
0176:         * <em>bean channel</em> to access the bean indirectly.
0177:         * In the latter case you specify a <code>ValueModel</code>
0178:         * that holds the bean that in turn holds the adapted property.
0179:         * In both cases the target bean is accessed indirectly through
0180:         * the bean channel. In both cases you can specify a custom trigger channel,
0181:         * or you can use a default trigger channel.<p>
0182:         *
0183:         * <strong>Note:</strong> This PresentationModel provides bound bean properties
0184:         * and you can register and unregister PropertyChangeListers as usual using
0185:         * <code>#addPropertyChangeListener</code> and
0186:         * <code>#removePropertyChangeListener</code>. Do not mix up
0187:         * the model listeners with the listeners registered with the bean.<p>
0188:         *
0189:         * <strong>Warning:</strong> PresentationModels register a
0190:         * PropertyChangeListener with the target bean. Hence, a bean has a reference
0191:         * to all PresentationModels that hold it as target bean. To avoid memory leaks
0192:         * it is recommended to remove this listener if the bean lives much longer
0193:         * than the PresentationModel, enabling the garbage collector to remove
0194:         * the PresentationModel.
0195:         * Setting a PresentationModel's target bean to null removes this listener,
0196:         * which in turn clears the reference from the bean to the PresentationModel.
0197:         * To do so, you can call <code>setBean(null)</code> or set the
0198:         * bean channel's value to null.
0199:         * As an alternative you can use event listener lists in your beans
0200:         * that implement references with <code>WeakReference</code>.
0201:         * Setting the bean to null has side effects, which is fine in most cases.
0202:         * However, you can release all listeners by calling <code>#release</code>.<p>
0203:         *
0204:         * TODO: Further improve the class comment.<p>
0205:         *
0206:         * TODO: Consider adding a feature to ensure that update notifications
0207:         * are performed in the event dispatch thread. In case the adapted bean
0208:         * is changed in a thread other than the event dispatch thread, such
0209:         * a feature would help complying with Swing's single thread rule.
0210:         * The feature could be implemented by an extended PropertyChangeSupport.<p>
0211:         *
0212:         * TODO: I plan to improve the support for adapting beans that do not fire
0213:         * PropertyChangeEvents. This affects the classes PropertyAdapter, BeanAdapter,
0214:         * and PresentationModel. Basically the PropertyAdapter and the BeanAdapter's
0215:         * internal SimplePropertyAdapter's shall be able to optionally self-fire
0216:         * a PropertyChangeEvent in case the bean does not. There are several
0217:         * downsides with self-firing events compared to bound bean properties.
0218:         * See <a href="https://binding.dev.java.net/issues/show_bug.cgi?id=49">Issue
0219:         * 49</a> for more information about the downsides.<p>
0220:         *
0221:         * The observeChanges constructor parameter shall be replaced by a more
0222:         * fine-grained choice to not observe (former observeChanges=false),
0223:         * to observe bound properties (former observeChanges=true), and a new
0224:         * setting for self-firing PropertyChangeEvents if a value is set.
0225:         * The latter case may be further split up to specify how the
0226:         * self-fired PropertyChangeEvent is created:
0227:         * <ol>
0228:         * <li>oldValue=null, newValue=null
0229:         * <li>oldValue=null, newValue=the value set
0230:         * <li>oldValue=value read before the set, newValue=the value set
0231:         * <li>oldValue=value read before the set, newValue=value read after the set
0232:         * </ol>
0233:         *
0234:         * @author  Karsten Lentzsch
0235:         * @version $Revision: 1.17 $
0236:         *
0237:         * @see     com.jgoodies.binding.beans.BeanAdapter
0238:         * @see     com.jgoodies.binding.value.ValueModel
0239:         * @see     com.jgoodies.binding.beans.PropertyAdapter
0240:         * @see     com.jgoodies.binding.value.Trigger
0241:         *
0242:         * @param <B>  the type of the bean managed by this PresentationModel
0243:         */
0244:        public class PresentationModel<B> extends Model {
0245:
0246:            /**
0247:             * The property name used in the PropertyChangeEvent that is fired
0248:             * before the <em>bean</em> property fires its PropertyChangeEvent.
0249:             * Useful to perform an operation before listeners that handle the
0250:             * bean change are notified. See also the class comment.
0251:             */
0252:            public static final String PROPERTYNAME_BEFORE_BEAN = "beforeBean";
0253:
0254:            /**
0255:             * The name of the read-write bound property that holds the target bean.
0256:             *
0257:             * @see #getBean()
0258:             * @see #setBean(Object)
0259:             */
0260:            public static final String PROPERTYNAME_BEAN = "bean";
0261:
0262:            /**
0263:             * The property name used in the PropertyChangeEvent that is fired
0264:             * after the <em>bean</em> property fires its PropertyChangeEvent.
0265:             * Useful to perform an operation after listeners that handle the
0266:             * bean change are notified. See also the class comment.
0267:             */
0268:            public static final String PROPERTYNAME_AFTER_BEAN = "afterBean";
0269:
0270:            /**
0271:             * The name of the read-write bound bean property for the
0272:             * trigger channel that is shared by all PropertyAdapters
0273:             * that are created via <code>#getBufferedModel</code>.
0274:             *
0275:             * @see #getTriggerChannel()
0276:             * @see #setTriggerChannel(ValueModel)
0277:             * @see #getBufferedModel(String)
0278:             */
0279:            public static final String PROPERTYNAME_TRIGGERCHANNEL = "triggerChannel";
0280:
0281:            /**
0282:             * The name of the read-only bound bean property that indicates
0283:             * whether one of the buffered models is buffering.
0284:             *
0285:             * @see #isBuffering()
0286:             * @see #getBufferedModel(String)
0287:             */
0288:            public static final String PROPERTYNAME_BUFFERING = "buffering";
0289:
0290:            /**
0291:             * The name of the read-only bound bean property that
0292:             * indicates whether one of the observed models has changed.
0293:             *
0294:             * @see #isChanged()
0295:             * @see #resetChanged()
0296:             * @see #observeChanged(ValueModel)
0297:             * @see #observeChanged(Object, String)
0298:             */
0299:            public static final String PROPERTYNAME_CHANGED = "changed";
0300:
0301:            // Fields *****************************************************************
0302:
0303:            /**
0304:             * Refers to the BeanAdapter that provides all underlying behavior
0305:             * to vend adapting ValueModels, track bean changes, and to register
0306:             * with bound bean properties.
0307:             */
0308:            private final BeanAdapter<B> beanAdapter;
0309:
0310:            /**
0311:             * Holds a three-state trigger channel that can be used to trigger
0312:             * commit and reset events in instances of BufferedValueModel.
0313:             * The trigger value is changed to true in <code>#triggerCommit</code>
0314:             * and is changed to false in <code>#triggerFlush</code>.<p>
0315:             *
0316:             * The trigger channel is initialized as a <code>Trigger</code>
0317:             * but may be replaced by any other ValueModel that accepts booleans.
0318:             *
0319:             * @see #getTriggerChannel()
0320:             * @see #setTriggerChannel(ValueModel)
0321:             * @see #getBufferedModel(String)
0322:             */
0323:            private ValueModel triggerChannel;
0324:
0325:            /**
0326:             * Maps property names to instances of the inner class WrappedBuffer.
0327:             * These hold a BufferedValueModel associated with the property name,
0328:             * as well as an optional getter and setter name. These accessor names
0329:             * are used to check that multiple calls to <code>#getBufferedModel</code>
0330:             * use the same getter and setter for a given property name.<p>
0331:             *
0332:             * The indirectly stored BufferedValueModel are checked whenever
0333:             * the buffering state is updated. And these model's trigger channel
0334:             * is updated when the PresentationModel gets a new trigger channel.
0335:             *
0336:             * @see #getBufferedModel(String)
0337:             * @see #getBufferedModel(String, String, String)
0338:             * @see #isBuffering()
0339:             * @see #setTriggerChannel(ValueModel)
0340:             */
0341:            private final Map<String, WrappedBuffer> wrappedBuffers;
0342:
0343:            /**
0344:             * Listens to value changes and validates this model.
0345:             * The validation result is available in the validationResultHolder.<p>
0346:             *
0347:             * Also listens to changes of the <em>buffering</em> property in
0348:             * <code>BufferedValueModel</code>s and updates the buffering state
0349:             * - if necessary.
0350:             */
0351:            private final PropertyChangeListener bufferingUpdateHandler;
0352:
0353:            /**
0354:             * Indicates whether a registered buffered model has a pending change,
0355:             * in other words whether any of the values has been edited or not.
0356:             */
0357:            private boolean buffering = false;
0358:
0359:            /**
0360:             * Listens to property changes and updates the <em>changed</em> property.
0361:             */
0362:            private final PropertyChangeListener changedUpdateHandler;
0363:
0364:            /**
0365:             * Indicates whether a registered model has changed.
0366:             */
0367:            private boolean changed = false;
0368:
0369:            /**
0370:             * Maps property names to instances of ComponentValueModel.
0371:             * Used to ensure that multiple calls to #getComponentModel
0372:             * return the same instance.
0373:             *
0374:             * @see #getComponentModel(String)
0375:             */
0376:            private final Map<String, ComponentValueModel> componentModels;
0377:
0378:            /**
0379:             * Maps property names to instances of ComponentValueModel.
0380:             * Used to ensure that multiple calls to #getBufferedComponentModel
0381:             * return the same instance.
0382:             *
0383:             * @see #getBufferedComponentModel(String)
0384:             */
0385:            private final Map<String, ComponentValueModel> bufferedComponentModels;
0386:
0387:            // Instance Creation ******************************************************
0388:
0389:            /**
0390:             * Constructs a PresentationModel that adapts properties of the given bean.<p>
0391:             *
0392:             * Installs a default bean channel that checks the identity not equity
0393:             * to ensure that listeners are unregistered properly if the old and
0394:             * new bean are equal but not the same.<p>
0395:             *
0396:             * Installs a Trigger as initial trigger channel.
0397:             *
0398:             * @param bean   the bean that holds the properties to adapt
0399:             * @throws PropertyUnboundException  if the <code>bean</code> does not
0400:             *     provide a pair of methods to register a PropertyChangeListener
0401:             */
0402:            public PresentationModel(Object bean) {
0403:                this (new ValueHolder(bean, true));
0404:            }
0405:
0406:            /**
0407:             * Constructs a PresentationModel on the given bean using the given
0408:             * trigger channel. The bean provides the properties to adapt.<p>
0409:             *
0410:             * Installs a default bean channel that checks the identity not equity
0411:             * to ensure that listeners are unregistered properly if the old and
0412:             * new bean are equal but not the same.<p>
0413:             *
0414:             * The trigger channel is shared by all buffered models that are created
0415:             * using <code>#getBufferedModel</code>.
0416:             * It can be replaced by any other Boolean ValueModel later.
0417:             * Note that PresentationModel observes trigger value changes,
0418:             * not value state. Therefore you must ensure that customer triggers
0419:             * report value changes when asked to commit or flush. See the
0420:             * Trigger implementation for an example.
0421:             *
0422:             * @param bean           the bean that holds the properties to adapt
0423:             * @param triggerChannel the ValueModel that triggers commit and flush events
0424:             */
0425:            public PresentationModel(Object bean, ValueModel triggerChannel) {
0426:                this (new ValueHolder(bean, true), triggerChannel);
0427:            }
0428:
0429:            /**
0430:             * Constructs a PresentationModel on the given bean channel. This channel
0431:             * holds a bean that in turn holds the properties to adapt.<p>
0432:             *
0433:             * It is strongly recommended that the bean channel checks the identity
0434:             * not equity. This ensures that listeners are unregistered properly if
0435:             * the old and new bean are equal but not the same.<p>
0436:             *
0437:             * The trigger channel is initialized as a <code>Trigger</code>.
0438:             * It may be replaced by any other Boolean ValueModel later.
0439:             * Note that PresentationModel observes trigger value changes,
0440:             * not value state. Therefore you must ensure that customer triggers
0441:             * report value changes when asked to commit or flush. See the
0442:             * Trigger implementation for an example.
0443:             *
0444:             * @param beanChannel   the ValueModel that holds the bean
0445:             *
0446:             * @throws PropertyUnboundException  if the <code>bean</code> does not
0447:             *     provide a pair of methods to register a PropertyChangeListener
0448:             */
0449:            public PresentationModel(ValueModel beanChannel) {
0450:                this (beanChannel, new Trigger());
0451:            }
0452:
0453:            /**
0454:             * Constructs a PresentationModel on the given bean channel using the given
0455:             * trigger channel. The bean channel holds a bean that in turn holds
0456:             * the properties to adapt.<p>
0457:             *
0458:             * It is strongly recommended that the bean channel checks the identity
0459:             * not equity. This ensures that listeners are unregistered properly if
0460:             * the old and new bean are equal but not the same.<p>
0461:             *
0462:             * The trigger channel is shared by all buffered
0463:             * models that are created using <code>#buffer</code>.
0464:             * It can be replaced by any other Boolean ValueModel later.
0465:             * Note that PresentationModel observes trigger value changes,
0466:             * not value state. Therefore you must ensure that customer triggers
0467:             * report value changes when asked to commit or flush. See the
0468:             * Trigger implementation for an example.
0469:             *
0470:             * @param beanChannel    the ValueModel that holds the bean
0471:             * @param triggerChannel the ValueModel that triggers commit and flush events
0472:             */
0473:            public PresentationModel(ValueModel beanChannel,
0474:                    ValueModel triggerChannel) {
0475:                this .beanAdapter = createBeanAdapter(beanChannel);
0476:                this .triggerChannel = triggerChannel;
0477:                this .wrappedBuffers = new HashMap<String, WrappedBuffer>();
0478:                this .componentModels = new HashMap<String, ComponentValueModel>();
0479:                this .bufferedComponentModels = new HashMap<String, ComponentValueModel>();
0480:                this .bufferingUpdateHandler = new BufferingStateHandler();
0481:                this .changed = false;
0482:                this .changedUpdateHandler = new UpdateHandler();
0483:
0484:                beanAdapter.addPropertyChangeListener(new BeanChangeHandler());
0485:
0486:                // By default we observe changes in the bean.
0487:                observeChanged(beanAdapter, BeanAdapter.PROPERTYNAME_CHANGED);
0488:            }
0489:
0490:            /**
0491:             *  Creates and returns a BeanAdapter for the given bean channel.
0492:             *  For compatibility with the 1.0.x, 1.1.x, and 1.2.x series,
0493:             *  this default implementation creates a BeanAdapter that always observes
0494:             *  the bean. Subclasses may override to observe only observable beans.<p>
0495:             *
0496:             *  Here's an example code for a custom implementation:
0497:             *  <pre>
0498:             *  boolean observe =
0499:             *        (beanChannel == null)
0500:             *     || (beanChannel.getValue() == null)
0501:             *     || BeanUtils.supportsBoundProperties((beanChannel.getValue().getClass());
0502:             *  return new BeanAdapter(beanChannel, observe);
0503:             *  </pre><p>
0504:             *
0505:             *  A future implementation shall return a BeanAdapter-like interface,
0506:             *  not a BeanAdapter.
0507:             *
0508:             *  @param beanChannel the ValueModel that holds the bean
0509:             *  @return the created bean adapter
0510:             *  @since 1.3
0511:             */
0512:            protected BeanAdapter<B> createBeanAdapter(ValueModel beanChannel) {
0513:                return new BeanAdapter<B>(beanChannel, true);
0514:            }
0515:
0516:            // Managing the Target Bean **********************************************
0517:
0518:            /**
0519:             * Returns the ValueModel that holds the bean that in turn holds
0520:             * the adapted properties. This bean channel is shared by the
0521:             * PropertyAdapters created by the factory methods
0522:             * <code>#getModel</code> and <code>#getBufferedModel</code>.
0523:             *
0524:             * @return the ValueModel that holds the bean that in turn
0525:             *     holds the adapted properties
0526:             *
0527:             * @see #getBean()
0528:             * @see #setBean(Object)
0529:             */
0530:            public ValueModel getBeanChannel() {
0531:                return beanAdapter.getBeanChannel();
0532:            }
0533:
0534:            /**
0535:             * Returns the bean that holds the adapted properties. This bean
0536:             * is the bean channel's content.
0537:             *
0538:             * @return the bean that holds the adapted properties
0539:             *
0540:             * @see #setBean(Object)
0541:             * @see #getBeanChannel()
0542:             */
0543:            public B getBean() {
0544:                return (B) getBeanChannel().getValue();
0545:            }
0546:
0547:            /**
0548:             * Sets a new bean as content of the bean channel.
0549:             * All adapted properties will reflect this change.
0550:             *
0551:             * @param newBean   the new bean
0552:             *
0553:             * @see #getBean()
0554:             * @see #getBeanChannel()
0555:             */
0556:            public void setBean(B newBean) {
0557:                getBeanChannel().setValue(newBean);
0558:            }
0559:
0560:            /**
0561:             * The underlying BeanAdapter is about to change the bean.
0562:             * Allows to perform actions before the bean change happens.
0563:             * For example you can remove listeners that shall not be notified
0564:             * if adapted properties change just because of the bean change.
0565:             * Or you can reset values, set fields to <code>null</code> etc.<p>
0566:             *
0567:             * The default behavior fires a PropertyChangeEvent for property
0568:             * <code>#PROPERTYNAME_BEFORE_BEAN</code>.
0569:             * <strong>Note:</strong> Subclasses that override this method
0570:             * must invoke super or perform the same behavior.<p>
0571:             *
0572:             * This method is invoked by the BeanChangeHandler listening to the
0573:             * <em>beforeBean</em> non-readable property of the BeanAdapter.
0574:             *
0575:             * @param oldBean  the bean before the change
0576:             * @param newBean  the bean that will be adapted after the change
0577:             *
0578:             * @see #afterBeanChange(Object, Object)
0579:             * @see #PROPERTYNAME_BEFORE_BEAN
0580:             * @see #PROPERTYNAME_BEAN
0581:             * @see #PROPERTYNAME_AFTER_BEAN
0582:             * @see BeanAdapter
0583:             */
0584:            public void beforeBeanChange(B oldBean, B newBean) {
0585:                firePropertyChange(PROPERTYNAME_BEFORE_BEAN, oldBean, newBean,
0586:                        true);
0587:            }
0588:
0589:            /**
0590:             * The underlying BeanAdapter has changed the target bean.
0591:             * Allows to perform actions after the bean changed.
0592:             * For example you can re-add listeners that were removed in
0593:             * <code>#beforeBeanChange</code>. Or you can reset values,
0594:             * reset custom changed state, set fields to <code>null</code> etc.<p>
0595:             *
0596:             * The default behavior resets the change tracker's <em>changed</em> state
0597:             * and fires a PropertyChangeEvent for the property
0598:             * <code>#PROPERTYNAME_AFTER_BEAN</code>.
0599:             * <strong>Note:</strong> Subclasses that override this method
0600:             * must invoke super or perform the same behavior.<p>
0601:             *
0602:             * This method is invoked by the BeanChangeHandler listening to the
0603:             * <em>afterBean</em> non-readable property of the BeanAdapter.
0604:             *
0605:             * @param oldBean  the bean that was adapted before the change
0606:             * @param newBean  the bean that is already the new target bean
0607:             *
0608:             * @see #beforeBeanChange(Object, Object)
0609:             * @see #PROPERTYNAME_BEFORE_BEAN
0610:             * @see #PROPERTYNAME_BEAN
0611:             * @see #PROPERTYNAME_AFTER_BEAN
0612:             * @see BeanAdapter
0613:             */
0614:            public void afterBeanChange(B oldBean, B newBean) {
0615:                setChanged(false);
0616:                firePropertyChange(PROPERTYNAME_AFTER_BEAN, oldBean, newBean,
0617:                        true);
0618:            }
0619:
0620:            // Accessing Property Values **********************************************
0621:
0622:            /**
0623:             * Returns the value of specified bean property, <code>null</code>
0624:             * if the current bean is <code>null</code>.<p>
0625:             *
0626:             * This operation is supported only for readable bean properties.
0627:             *
0628:             * @param propertyName  the name of the property to be read
0629:             * @return the value of the adapted bean property, null if the bean is null
0630:             *
0631:             * @throws NullPointerException           if the property name is null
0632:             * @throws UnsupportedOperationException  if the property is write-only
0633:             * @throws PropertyNotFoundException      if the property could not be found
0634:             * @throws PropertyAccessException        if the value could not be read
0635:             *
0636:             * @since 1.1
0637:             */
0638:            public Object getValue(String propertyName) {
0639:                return beanAdapter.getValue(propertyName);
0640:            }
0641:
0642:            /**
0643:             * Sets the given new value for the specified bean property. Does nothing
0644:             * if this adapter's bean is <code>null</code>. If the setter associated
0645:             * with the propertyName throws a PropertyVetoException, it is silently
0646:             * ignored.<p>
0647:             *
0648:             * Notifies the associated value change listeners if the bean reports
0649:             * a property change. Note that a bean may suppress PropertyChangeEvents
0650:             * if the old and new value are the same, or if the old and new value
0651:             * are equal.<p>
0652:             *
0653:             * This operation is supported only for writable bean properties.
0654:             *
0655:             * @param propertyName   the name of the property to set
0656:             * @param newValue       the value to set
0657:             *
0658:             * @throws NullPointerException           if the property name is null
0659:             * @throws UnsupportedOperationException  if the property is read-only
0660:             * @throws PropertyNotFoundException      if the property could not be found
0661:             * @throws PropertyAccessException        if the new value could not be set
0662:             *
0663:             * @since 1.1
0664:             */
0665:            public void setValue(String propertyName, Object newValue) {
0666:                beanAdapter.setValue(propertyName, newValue);
0667:            }
0668:
0669:            /**
0670:             * Sets a new value for the specified bean property. Does nothing if the
0671:             * bean is <code>null</code>. If the setter associated with the propertyName
0672:             * throws a PropertyVetoException, this methods throws the same exception.<p>
0673:             *
0674:             * Notifies the associated value change listeners if the bean reports
0675:             * a property change. Note that a bean may suppress PropertyChangeEvents
0676:             * if the old and new value are the same, or if the old and new value
0677:             * are equal.<p>
0678:             *
0679:             * This operation is supported only for writable bean properties.
0680:             *
0681:             * @param propertyName   the name of the property to set
0682:             * @param newValue       the value to set
0683:             *
0684:             * @throws NullPointerException           if the property name is null
0685:             * @throws UnsupportedOperationException  if the property is read-only
0686:             * @throws PropertyNotFoundException      if the property could not be found
0687:             * @throws PropertyAccessException        if the new value could not be set
0688:             * @throws PropertyVetoException          if the bean setter
0689:             *     throws a PropertyVetoException
0690:             *
0691:             * @since 1.1
0692:             */
0693:            public void setVetoableValue(String propertyName, Object newValue)
0694:                    throws PropertyVetoException {
0695:                beanAdapter.setVetoableValue(propertyName, newValue);
0696:            }
0697:
0698:            /**
0699:             * Returns the value of specified buffered bean property.
0700:             * It is a shorthand for writing
0701:             * <pre>getBufferedModel(propertyName).getValue()</pre>
0702:             * As a side-effect, this method may create a buffered model.
0703:             *
0704:             * @param propertyName  the name of the property to be read
0705:             * @return the value of the adapted bean property, null if the bean is null
0706:             *
0707:             * @throws NullPointerException           if the property name is null
0708:             * @throws UnsupportedOperationException  if the property is write-only
0709:             * @throws PropertyNotFoundException      if the property could not be found
0710:             * @throws PropertyAccessException        if the value could not be read
0711:             *
0712:             * @since 1.1
0713:             */
0714:            public Object getBufferedValue(String propertyName) {
0715:                return getBufferedModel(propertyName).getValue();
0716:            }
0717:
0718:            /**
0719:             * Buffers the given value for the specified bean property.
0720:             * It is a shorthand for writing
0721:             * <pre>getBufferedModel(propertyName).setValue(newValue)</pre>
0722:             * As a side-effect, this method may create a buffered model.
0723:             *
0724:             * @param propertyName   the name of the property to set
0725:             * @param newValue       the value to set
0726:             *
0727:             * @throws NullPointerException           if the property name is null
0728:             * @throws PropertyNotFoundException      if the property could not be found
0729:             * @throws PropertyAccessException        if the new value could not be set
0730:             *
0731:             * @since 1.1
0732:             */
0733:            public void setBufferedValue(String propertyName, Object newValue) {
0734:                getBufferedModel(propertyName).setValue(newValue);
0735:            }
0736:
0737:            // Factory Methods for Bound Models ***************************************
0738:
0739:            /**
0740:             * Looks up and lazily creates a ValueModel that adapts
0741:             * the bound property with the specified name. Uses the
0742:             * Bean introspection to look up the getter and setter names.<p>
0743:             *
0744:             * Subsequent calls to this method with the same property name
0745:             * return the same ValueModel.<p>
0746:             *
0747:             * To prevent potential runtime errors it eagerly looks up
0748:             * the associated PropertyDescriptor if the target bean is not null.<p>
0749:             *
0750:             * For each property name all calls to this method
0751:             * and to <code>#getModel(String, String, String)</code> must use
0752:             * the same getter and setter names. Attempts to violate this constraint
0753:             * will be rejected with an IllegalArgumentException. Especially once
0754:             * you've called this method you must not call
0755:             * <code>#getModel(String, String, String)</code> with a non-null
0756:             * getter or setter name. And vice versa, once you've called the latter
0757:             * method with a non-null getter or setter name, you must not call
0758:             * this method.<p>
0759:             *
0760:             * This method uses a return type of AbstractValueModel, not a ValueModel.
0761:             * This makes the AbstractValueModel convenience type converters available,
0762:             * which can significantly shrink the source code necessary to read and
0763:             * write values from/to these models.
0764:             *
0765:             * @param propertyName   the name of the property to adapt
0766:             * @return a ValueModel that adapts the property with the specified name
0767:             *
0768:             * @throws NullPointerException       if the property name is null
0769:             * @throws PropertyNotFoundException  if the property could not be found
0770:             * @throws IllegalArgumentException
0771:             *     if <code>#getModel(String, String, String)</code> has been
0772:             *     called before with the same property name and a non-null getter
0773:             *     or setter name
0774:             *
0775:             * @see AbstractValueModel
0776:             * @see BeanAdapter
0777:             * @see #getModel(String, String, String)
0778:             * @see #getBufferedModel(String)
0779:             */
0780:            public AbstractValueModel getModel(String propertyName) {
0781:                return beanAdapter.getValueModel(propertyName);
0782:            }
0783:
0784:            /**
0785:             * Looks up and lazily creates a ValueModel that adapts the bound property
0786:             * with the given name. Unlike <code>#getModel(String)</code>
0787:             * this method bypasses the Bean Introspection and uses the given getter
0788:             * and setter names to setup the access to the adapted Bean property.<p>
0789:             *
0790:             * Subsequent calls to this method with the same parameters
0791:             * will return the same ValueModel.<p>
0792:             *
0793:             * To prevent potential runtime errors this method eagerly looks up
0794:             * the associated PropertyDescriptor if the target bean is not null.<p>
0795:             *
0796:             * For each property name all calls to this method
0797:             * and to <code>#getModel(String)</code> must use the same
0798:             * getter and setter names. Attempts to violate this constraint
0799:             * will be rejected with an IllegalArgumentException. Especially
0800:             * once you've called this method with a non-null getter or setter name,
0801:             * you must not call <code>#getModel(String)</code>. And vice versa,
0802:             * once you've called the latter method you must not call this method
0803:             * with a non-null getter or setter name.<p>
0804:             *
0805:             * This method uses a return type of AbstractValueModel, not a ValueModel.
0806:             * This makes the AbstractValueModel convenience type converters available,
0807:             * which can significantly shrink the source code necessary to read and
0808:             * write values from/to these models.
0809:             *
0810:             * @param propertyName   the name of the property to adapt
0811:             * @param getterName     the name of the method that reads the value
0812:             * @param setterName     the name of the method that sets the value
0813:             * @return a ValueModel that adapts the property with the specified name
0814:             *
0815:             * @throws NullPointerException       if the property name is null
0816:             * @throws PropertyNotFoundException  if the property could not be found
0817:             * @throws IllegalArgumentException   if this method has been called before
0818:             *     with the same property name and different getter or setter names
0819:             *
0820:             * @see AbstractValueModel
0821:             * @see BeanAdapter
0822:             * @see #getModel(String, String, String)
0823:             * @see #getBufferedModel(String)
0824:             */
0825:            public AbstractValueModel getModel(String propertyName,
0826:                    String getterName, String setterName) {
0827:                return beanAdapter.getValueModel(propertyName, getterName,
0828:                        setterName);
0829:            }
0830:
0831:            /**
0832:             * Looks up and lazily creates a ComponentValueModel that adapts
0833:             * the bound property with the specified name. Uses the standard
0834:             * Bean introspection to look up the getter and setter names.<p>
0835:             *
0836:             * Subsequent calls to this method with the same property name
0837:             * return the same ComponentValueModel.<p>
0838:             *
0839:             * To prevent potential runtime errors it eagerly looks up
0840:             * the associated PropertyDescriptor if the target bean is not null.<p>
0841:             *
0842:             * For each property name all calls to this method
0843:             * and to <code>#getModel(String, String, String)</code> must use
0844:             * the same getter and setter names. Attempts to violate this constraint
0845:             * will be rejected with an IllegalArgumentException. Especially once
0846:             * you've called this method you must not call
0847:             * <code>#getModel(String, String, String)</code> with a non-null
0848:             * getter or setter name. And vice versa, once you've called the latter
0849:             * method with a non-null getter or setter name, you must not call
0850:             * this method.<p>
0851:             *
0852:             * This returned ComponentValueModel provides convenience type converter
0853:             * method from AbstractValueModel and allows to modify GUI state such as
0854:             * enabled, visible, and editable in this presentation model.
0855:             * This can significantly shrink the source code necessary to handle
0856:             * GUI state changes.
0857:             *
0858:             * @param propertyName   the name of the property to adapt
0859:             * @return a ValueModel that adapts the property with the specified name
0860:             *
0861:             * @throws NullPointerException       if the property name is null
0862:             * @throws PropertyNotFoundException  if the property could not be found
0863:             * @throws IllegalArgumentException
0864:             *     if <code>#getModel(String, String, String)</code> has been
0865:             *     called before with the same property name and a non-null getter
0866:             *     or setter name
0867:             *
0868:             * @see ComponentValueModel
0869:             * @see AbstractValueModel
0870:             * @see BeanAdapter
0871:             * @see #getModel(String, String, String)
0872:             * @see #getBufferedModel(String)
0873:             * @see Bindings#addComponentPropertyHandler(JComponent, ValueModel)
0874:             *
0875:             * @since 1.1
0876:             */
0877:            public ComponentValueModel getComponentModel(String propertyName) {
0878:                ComponentValueModel componentModel = componentModels
0879:                        .get(propertyName);
0880:                if (componentModel == null) {
0881:                    AbstractValueModel model = getModel(propertyName);
0882:                    componentModel = new ComponentValueModel(model);
0883:                    componentModels.put(propertyName, componentModel);
0884:                }
0885:                return componentModel;
0886:            }
0887:
0888:            // Factory Methods for Buffered Models ************************************
0889:
0890:            /**
0891:             * Looks up or creates a buffered adapter to the read-write property
0892:             * with the given name on this PresentationModel's bean channel. Creates a
0893:             * BufferedValueModel that wraps a ValueModel that adapts the bean property
0894:             * with the specified name. The buffered model uses this PresentationModel's
0895:             * trigger channel to listen for commit and flush events.<p>
0896:             *
0897:             * The created BufferedValueModel is stored in a Map. Hence
0898:             * subsequent calls to this method with the same property name
0899:             * return the same BufferedValueModel.<p>
0900:             *
0901:             * To prevent potential runtime errors this method eagerly looks up
0902:             * the associated PropertyDescriptor if the target bean is not null.<p>
0903:             *
0904:             * For each property name all calls to this method
0905:             * and to <code>#getBufferedModel(String, String, String)</code> must use
0906:             * the same getter and setter names. Attempts to violate this constraint
0907:             * will be rejected with an IllegalArgumentException. Especially once
0908:             * you've called this method you must not call
0909:             * <code>#getBufferedModel(String, String, String)</code> with a non-null
0910:             * getter or setter name. And vice versa, once you've called the latter
0911:             * method with a non-null getter or setter name, you must not call
0912:             * this method.
0913:             *
0914:             * @param propertyName the name of the read-write property to adapt
0915:             * @return a buffered adapter to the property with the given name
0916:             *    on this model's bean channel using this model's trigger channel
0917:             *
0918:             * @throws NullPointerException       if the property name is null
0919:             * @throws PropertyNotFoundException  if the property could not be found
0920:             * @throws IllegalArgumentException
0921:             *     if <code>#getBufferedModel(String, String, String)</code> has been
0922:             *     called before with the same property name and a non-null getter
0923:             *     or setter name
0924:             *
0925:             * @see BufferedValueModel
0926:             * @see ValueModel
0927:             * @see Trigger
0928:             * @see BeanAdapter
0929:             * @see #getModel(String)
0930:             * @see #getBufferedModel(String, String, String)
0931:             */
0932:            public BufferedValueModel getBufferedModel(String propertyName) {
0933:                return getBufferedModel(propertyName, null, null);
0934:            }
0935:
0936:            /**
0937:             * Looks up or creates a buffered adapter to the read-write property
0938:             * with the given name on this PresentationModel's bean channel using
0939:             * the specified getter and setter name to read and write values. Creates
0940:             * a <code>BufferedValueModel</code> that wraps a <code>ValueModel</code>
0941:             * that adapts the bean property with the specified name.
0942:             * The buffered model uses this PresentationModel's trigger channel
0943:             * to listen for commit and flush events.<p>
0944:             *
0945:             * The created BufferedValueModel is stored in a Map so it can be
0946:             * looked up if it is requested multiple times.<p>
0947:             *
0948:             * To prevent potential runtime errors this method eagerly looks up
0949:             * the associated PropertyDescriptor if the target bean is not null.<p>
0950:             *
0951:             * For each property name all calls to this method
0952:             * and to <code>#getBufferedModel(String)</code> must use the same
0953:             * getter and setter names. Attempts to violate this constraint
0954:             * will be rejected with an IllegalArgumentException. Especially
0955:             * once you've called this method with a non-null getter or setter name,
0956:             * you must not call <code>#getBufferedModel(String)</code>. And vice versa,
0957:             * once you've called the latter method you must not call this method
0958:             * with a non-null getter or setter name.
0959:             *
0960:             * @param propertyName   the name of the property to adapt
0961:             * @param getterName     the name of the method that reads the value
0962:             * @param setterName     the name of the method that sets the value
0963:             * @return a buffered adapter to the property with the given name
0964:             *    on this model's bean channel using this model's trigger channel
0965:             *
0966:             * @throws NullPointerException       if the property name is null
0967:             * @throws PropertyNotFoundException  if the property could not be found
0968:             * @throws IllegalArgumentException   if this method has been called before
0969:             *     with the same property name and different getter or setter names
0970:             *
0971:             * @see BufferedValueModel
0972:             * @see ValueModel
0973:             * @see Trigger
0974:             * @see BeanAdapter
0975:             * @see #getModel(String)
0976:             * @see #getBufferedModel(String)
0977:             */
0978:            public BufferedValueModel getBufferedModel(String propertyName,
0979:                    String getterName, String setterName) {
0980:                WrappedBuffer wrappedBuffer = wrappedBuffers.get(propertyName);
0981:                if (wrappedBuffer == null) {
0982:                    wrappedBuffer = new WrappedBuffer(buffer(getModel(
0983:                            propertyName, getterName, setterName)), getterName,
0984:                            setterName);
0985:                    wrappedBuffers.put(propertyName, wrappedBuffer);
0986:                } else if (!equals(getterName, wrappedBuffer.getterName)
0987:                        || !equals(setterName, wrappedBuffer.setterName)) {
0988:                    throw new IllegalArgumentException(
0989:                            "You must not invoke this method twice "
0990:                                    + "with different getter and/or setter names.");
0991:                }
0992:                return wrappedBuffer.buffer;
0993:            }
0994:
0995:            /**
0996:             * Looks up or creates a buffered component adapter to the read-write
0997:             * property with the given name on this PresentationModel's bean channel.
0998:             * Creates a ComponentValueModel that wraps a BufferedValueModel that
0999:             * in turn wraps a ValueModel that adapts the bean property with the
1000:             * specified name. The buffered model uses this PresentationModel's
1001:             * trigger channel to listen for commit and flush events.
1002:             * The ComponentValueModel allows to set component state in this
1003:             * presentation model.<p>
1004:             *
1005:             * The created ComponentValueModel is stored in a Map. Hence
1006:             * subsequent calls to this method with the same property name
1007:             * return the same ComponentValueModel.<p>
1008:             *
1009:             * To prevent potential runtime errors this method eagerly looks up
1010:             * the associated PropertyDescriptor if the target bean is not null.<p>
1011:             *
1012:             * For each property name all calls to this method
1013:             * and to <code>#getBufferedModel(String, String, String)</code> must use
1014:             * the same getter and setter names. Attempts to violate this constraint
1015:             * will be rejected with an IllegalArgumentException. Especially once
1016:             * you've called this method you must not call
1017:             * <code>#getBufferedModel(String, String, String)</code> with a non-null
1018:             * getter or setter name. And vice versa, once you've called the latter
1019:             * method with a non-null getter or setter name, you must not call
1020:             * this method.
1021:             *
1022:             * @param propertyName the name of the read-write property to adapt
1023:             * @return a ComponentValueModel that wraps a buffered adapter
1024:             *    to the property with the given name
1025:             *    on this model's bean channel using this model's trigger channel
1026:             *
1027:             * @throws NullPointerException       if the property name is null
1028:             * @throws PropertyNotFoundException  if the property could not be found
1029:             * @throws IllegalArgumentException
1030:             *     if <code>#getBufferedModel(String, String, String)</code> has been
1031:             *     called before with the same property name and a non-null getter
1032:             *     or setter name
1033:             *
1034:             * @see ComponentValueModel
1035:             * @see BufferedValueModel
1036:             * @see ValueModel
1037:             * @see Trigger
1038:             * @see BeanAdapter
1039:             * @see #getModel(String)
1040:             * @see #getBufferedModel(String)
1041:             * @see #getComponentModel(String)
1042:             * @see Bindings#addComponentPropertyHandler(JComponent, ValueModel)
1043:             *
1044:             * @since 1.1
1045:             */
1046:            public ComponentValueModel getBufferedComponentModel(
1047:                    String propertyName) {
1048:                ComponentValueModel bufferedComponentModel = bufferedComponentModels
1049:                        .get(propertyName);
1050:                if (bufferedComponentModel == null) {
1051:                    AbstractValueModel model = getBufferedModel(propertyName);
1052:                    bufferedComponentModel = new ComponentValueModel(model);
1053:                    bufferedComponentModels.put(propertyName,
1054:                            bufferedComponentModel);
1055:                }
1056:                return bufferedComponentModel;
1057:            }
1058:
1059:            /**
1060:             * Wraps the given ValueModel with a BufferedValueModel that
1061:             * uses this model's trigger channel to trigger commit and flush events.
1062:             *
1063:             * @param valueModel  the ValueModel to be buffered
1064:             * @return a BufferedValueModel triggered by the model's trigger channel
1065:             *
1066:             * @see BufferedValueModel
1067:             * @see ValueModel
1068:             * @see Trigger
1069:             * @see #getBufferedModel(String)
1070:             */
1071:            private BufferedValueModel buffer(ValueModel valueModel) {
1072:                BufferedValueModel bufferedModel = new BufferedValueModel(
1073:                        valueModel, getTriggerChannel());
1074:                bufferedModel.addPropertyChangeListener(
1075:                        BufferedValueModel.PROPERTYNAME_BUFFERING,
1076:                        bufferingUpdateHandler);
1077:                return bufferedModel;
1078:            }
1079:
1080:            // Accessing the Trigger Channel ******************************************
1081:
1082:            /**
1083:             * Returns a ValueModel that can be shared and used to trigger commit
1084:             * and flush events in BufferedValueModels. The trigger channel's value
1085:             * changes to true in <code>#triggerCommit</code> and it changes to false
1086:             * in <code>#triggerFlush</code>.<p>
1087:             *
1088:             * This trigger channel is used to commit and flush values
1089:             * in the BufferedValueModels returned by <code>#getBufferedModel</code>.
1090:             *
1091:             * @return this model's trigger channel
1092:             *
1093:             * @see BufferedValueModel
1094:             * @see ValueModel
1095:             * @see #setTriggerChannel(ValueModel)
1096:             */
1097:            public ValueModel getTriggerChannel() {
1098:                return triggerChannel;
1099:            }
1100:
1101:            /**
1102:             * Sets the given ValueModel as this model's new trigger channel.
1103:             * Sets the new trigger channel in all existing BufferedValueModels
1104:             * that have been created using <code>#getBufferedModel</code>.
1105:             * Subsequent invocations of <code>#triggerCommit</code> and
1106:             * <code>#triggerFlush</code> will trigger commit and flush events
1107:             * using the new trigger channel.
1108:             *
1109:             * @param newTriggerChannel  the ValueModel to be set as
1110:             *     this model's new trigger channel
1111:             * @throws NullPointerException  if the new trigger channel is <code>null</code>
1112:             *
1113:             * @see BufferedValueModel
1114:             * @see ValueModel
1115:             * @see #getTriggerChannel()
1116:             */
1117:            public void setTriggerChannel(ValueModel newTriggerChannel) {
1118:                if (newTriggerChannel == null)
1119:                    throw new NullPointerException(
1120:                            "The trigger channel must not be null.");
1121:
1122:                ValueModel oldTriggerChannel = getTriggerChannel();
1123:                triggerChannel = newTriggerChannel;
1124:                for (WrappedBuffer wrappedBuffer : wrappedBuffers.values()) {
1125:                    wrappedBuffer.buffer.setTriggerChannel(triggerChannel);
1126:                }
1127:                firePropertyChange(PROPERTYNAME_TRIGGERCHANNEL,
1128:                        oldTriggerChannel, newTriggerChannel);
1129:            }
1130:
1131:            /**
1132:             * Sets the trigger channel to true which in turn triggers commit
1133:             * events in all BufferedValueModels that share this trigger.
1134:             *
1135:             * @see #triggerFlush()
1136:             */
1137:            public void triggerCommit() {
1138:                if (Boolean.TRUE.equals(getTriggerChannel().getValue()))
1139:                    getTriggerChannel().setValue(null);
1140:                getTriggerChannel().setValue(Boolean.TRUE);
1141:            }
1142:
1143:            /**
1144:             * Sets the trigger channel to false which in turn triggers flush
1145:             * events in all BufferedValueModels that share this trigger.
1146:             *
1147:             * @see #triggerCommit()
1148:             */
1149:            public void triggerFlush() {
1150:                if (Boolean.FALSE.equals(getTriggerChannel().getValue()))
1151:                    getTriggerChannel().setValue(null);
1152:                getTriggerChannel().setValue(Boolean.FALSE);
1153:            }
1154:
1155:            // Managing the Buffering State *******************************************
1156:
1157:            /**
1158:             * Answers whether any of the buffered models is buffering.
1159:             * Useful to enable and disable UI actions and operations
1160:             * that depend on the buffering state.
1161:             *
1162:             * @return true if any of the buffered models is buffering,
1163:             *     false, if all buffered models write-through
1164:             */
1165:            public boolean isBuffering() {
1166:                return buffering;
1167:            }
1168:
1169:            /**
1170:             * Sets the buffering state to the specified value.
1171:             *
1172:             * @param newValue  the new buffering state
1173:             */
1174:            private void setBuffering(boolean newValue) {
1175:                boolean oldValue = isBuffering();
1176:                buffering = newValue;
1177:                firePropertyChange(PROPERTYNAME_BUFFERING, oldValue, newValue);
1178:            }
1179:
1180:            private void updateBufferingState(boolean latestBufferingStateChange) {
1181:                if (buffering == latestBufferingStateChange)
1182:                    return;
1183:                boolean nowBuffering = false;
1184:                for (WrappedBuffer wrappedBuffer : wrappedBuffers.values()) {
1185:                    BufferedValueModel model = wrappedBuffer.buffer;
1186:                    nowBuffering = nowBuffering || model.isBuffering();
1187:                    if (!buffering && nowBuffering) {
1188:                        setBuffering(true);
1189:                        return;
1190:                    }
1191:                }
1192:                setBuffering(nowBuffering);
1193:            }
1194:
1195:            // Changed State *********************************************************
1196:
1197:            /**
1198:             * Answers whether one of the registered ValueModels has changed
1199:             * since the changed state has been reset last time.<p>
1200:             *
1201:             * <strong>Note:</strong> Unlike <code>#resetChanged</code> this method
1202:             * is not intended to be overridden by subclasses.
1203:             * If you want to track changes of other ValueModels, bean properties, or
1204:             * of submodels, register them by means of <code>#observeChanged</code>.
1205:             * Overriding <code>#isChanged</code> to include the changed state
1206:             * of submodels would return the correct changed value, but it would bypass
1207:             * the change notification from submodels to this model.
1208:             * Therefore submodels must be observed, which can be achieve using
1209:             * <code>#observeChanged</code>.<p>
1210:             *
1211:             * To reset the changed state invoke <code>#resetChanged</code>.
1212:             * In case you track the changed state of submodels override
1213:             * <code>#resetChanged</code> to reset the changed state in these
1214:             * submodels too.
1215:             *
1216:             * @return true if an observed property has changed since the last reset
1217:             *
1218:             * @see #observeChanged(ValueModel)
1219:             * @see #observeChanged(Object, String)
1220:             * @see #resetChanged()
1221:             */
1222:            public boolean isChanged() {
1223:                return changed;
1224:            }
1225:
1226:            /**
1227:             * Resets this model's changed state to <code>false</code>.
1228:             * Therefore it resets the changed states of the change tracker
1229:             * and the underlying bean adapter.<p>
1230:             *
1231:             * Subclasses may override this method to reset the changed state
1232:             * of submodels. The overriding method must invoke this super behavior.
1233:             * For example if you have a MainModel that is composed of
1234:             * two submodels Submodel1 and Submodel2, you may write:
1235:             * <pre>
1236:             * public void resetChanged() {
1237:             *     super.resetChanged();
1238:             *     getSubmodel1().resetChanged();
1239:             *     getSubmodel2().resetChanged();
1240:             * }
1241:             * </pre>
1242:             *
1243:             * @see #isChanged()
1244:             * @see #observeChanged(ValueModel)
1245:             * @see #observeChanged(Object, String)
1246:             */
1247:            public void resetChanged() {
1248:                setChanged(false);
1249:                beanAdapter.resetChanged();
1250:            }
1251:
1252:            protected void setChanged(boolean newValue) {
1253:                boolean oldValue = isChanged();
1254:                changed = newValue;
1255:                firePropertyChange(PROPERTYNAME_CHANGED, oldValue, newValue);
1256:            }
1257:
1258:            // Observing Changes in ValueModel and Bean Properties *******************
1259:
1260:            /**
1261:             * Observes the specified readable bound bean property in the given bean.
1262:             *
1263:             * @param bean           the bean to be observed
1264:             * @param propertyName   the name of the readable bound bean property
1265:             * @throws NullPointerException if the bean or propertyName is null
1266:             * @throws PropertyNotBindableException if this class can't add
1267:             *     the PropertyChangeListener from the bean
1268:             *
1269:             * @see #retractInterestFor(Object, String)
1270:             * @see #observeChanged(ValueModel)
1271:             */
1272:            public void observeChanged(Object bean, String propertyName) {
1273:                if (bean == null)
1274:                    throw new NullPointerException("The bean must not be null.");
1275:                if (propertyName == null)
1276:                    throw new NullPointerException(
1277:                            "The property name must not be null.");
1278:
1279:                BeanUtils.addPropertyChangeListener(bean, propertyName,
1280:                        changedUpdateHandler);
1281:            }
1282:
1283:            /**
1284:             * Observes value changes in the given ValueModel.
1285:             *
1286:             * @param valueModel   the ValueModel to observe
1287:             * @throws NullPointerException if the valueModel is null
1288:             *
1289:             * @see #retractInterestFor(ValueModel)
1290:             * @see #observeChanged(Object, String)
1291:             */
1292:            public void observeChanged(ValueModel valueModel) {
1293:                if (valueModel == null)
1294:                    throw new NullPointerException(
1295:                            "The ValueModel must not be null.");
1296:                valueModel.addValueChangeListener(changedUpdateHandler);
1297:            }
1298:
1299:            /**
1300:             * Retracts interest for the specified readable bound bean property
1301:             * in the given bean.
1302:             *
1303:             * @param bean           the bean to be observed
1304:             * @param propertyName   the name of the readable bound bean property
1305:             * @throws NullPointerException if the bean or propertyName is null
1306:             * @throws PropertyNotBindableException if this class can't remove
1307:             *     the PropertyChangeListener from the bean
1308:             *
1309:             * @see #observeChanged(Object, String)
1310:             * @see #retractInterestFor(ValueModel)
1311:             */
1312:            public void retractInterestFor(Object bean, String propertyName) {
1313:                if (bean == null)
1314:                    throw new NullPointerException("The bean must not be null.");
1315:                if (propertyName == null)
1316:                    throw new NullPointerException(
1317:                            "The property name must not be null.");
1318:
1319:                BeanUtils.removePropertyChangeListener(bean, propertyName,
1320:                        changedUpdateHandler);
1321:            }
1322:
1323:            /**
1324:             * Retracts interest for value changes in the given ValueModel.
1325:             *
1326:             * @param valueModel   the ValueModel to observe
1327:             * @throws NullPointerException if the valueModel is null
1328:             *
1329:             * @see #observeChanged(ValueModel)
1330:             * @see #retractInterestFor(Object, String)
1331:             */
1332:            public void retractInterestFor(ValueModel valueModel) {
1333:                if (valueModel == null)
1334:                    throw new NullPointerException(
1335:                            "The ValueModel must not be null.");
1336:                valueModel.removeValueChangeListener(changedUpdateHandler);
1337:            }
1338:
1339:            // Managing Bean Property Change Listeners *******************************
1340:
1341:            /**
1342:             * Adds a PropertyChangeListener to the list of bean listeners. The
1343:             * listener is registered for all bound properties of the target bean.<p>
1344:             *
1345:             * The listener will be notified if and only if this BeanAdapter's current
1346:             * bean changes a property. It'll not be notified if the bean changes.<p>
1347:             *
1348:             * If listener is <code>null</code>, no exception is thrown and
1349:             * no action is performed.
1350:             *
1351:             * @param listener      the PropertyChangeListener to be added
1352:             *
1353:             * @see #removeBeanPropertyChangeListener(PropertyChangeListener)
1354:             * @see #removeBeanPropertyChangeListener(String, PropertyChangeListener)
1355:             * @see #addBeanPropertyChangeListener(String, PropertyChangeListener)
1356:             * @see #getBeanPropertyChangeListeners()
1357:             */
1358:            public synchronized void addBeanPropertyChangeListener(
1359:                    PropertyChangeListener listener) {
1360:                beanAdapter.addBeanPropertyChangeListener(listener);
1361:            }
1362:
1363:            /**
1364:             * Removes a PropertyChangeListener from the list of bean listeners.
1365:             * This method should be used to remove PropertyChangeListeners that
1366:             * were registered for all bound properties of the target bean.<p>
1367:             *
1368:             * If listener is <code>null</code>, no exception is thrown and
1369:             * no action is performed.
1370:             *
1371:             * @param listener      the PropertyChangeListener to be removed
1372:             *
1373:             * @see #addBeanPropertyChangeListener(PropertyChangeListener)
1374:             * @see #addBeanPropertyChangeListener(String, PropertyChangeListener)
1375:             * @see #removeBeanPropertyChangeListener(String, PropertyChangeListener)
1376:             * @see #getBeanPropertyChangeListeners()
1377:             */
1378:            public synchronized void removeBeanPropertyChangeListener(
1379:                    PropertyChangeListener listener) {
1380:                beanAdapter.removeBeanPropertyChangeListener(listener);
1381:            }
1382:
1383:            /**
1384:             * Adds a PropertyChangeListener to the list of bean listeners for a
1385:             * specific property. The specified property may be user-defined.<p>
1386:             *
1387:             * The listener will be notified if and only if this BeanAdapter's
1388:             * current bean changes the specified property. It'll not be notified
1389:             * if the bean changes. If you want to observe property changes and
1390:             * bean changes, you may observe the ValueModel that adapts this property
1391:             * - as returned by <code>#getModel(String)</code>.<p>
1392:             *
1393:             * Note that if the bean is inheriting a bound property, then no event
1394:             * will be fired in response to a change in the inherited property.<p>
1395:             *
1396:             * If listener is <code>null</code>, no exception is thrown and
1397:             * no action is performed.
1398:             *
1399:             * @param propertyName      one of the property names listed above
1400:             * @param listener          the PropertyChangeListener to be added
1401:             *
1402:             * @see #removeBeanPropertyChangeListener(String, PropertyChangeListener)
1403:             * @see #addBeanPropertyChangeListener(String, PropertyChangeListener)
1404:             * @see #getBeanPropertyChangeListeners(String)
1405:             */
1406:            public synchronized void addBeanPropertyChangeListener(
1407:                    String propertyName, PropertyChangeListener listener) {
1408:                beanAdapter.addBeanPropertyChangeListener(propertyName,
1409:                        listener);
1410:            }
1411:
1412:            /**
1413:             * Removes a PropertyChangeListener from the listener list for a specific
1414:             * property. This method should be used to remove PropertyChangeListeners
1415:             * that were registered for a specific bound property.<p>
1416:             *
1417:             * If listener is <code>null</code>, no exception is thrown and
1418:             * no action is performed.
1419:             *
1420:             * @param propertyName      a valid property name
1421:             * @param listener          the PropertyChangeListener to be removed
1422:             *
1423:             * @see #addBeanPropertyChangeListener(String, PropertyChangeListener)
1424:             * @see #removeBeanPropertyChangeListener(PropertyChangeListener)
1425:             * @see #getBeanPropertyChangeListeners(String)
1426:             */
1427:            public synchronized void removeBeanPropertyChangeListener(
1428:                    String propertyName, PropertyChangeListener listener) {
1429:                beanAdapter.removeBeanPropertyChangeListener(propertyName,
1430:                        listener);
1431:            }
1432:
1433:            // Requesting Listener Sets ***********************************************
1434:
1435:            /**
1436:             * Returns an array of all the property change listeners
1437:             * registered on this component.
1438:             *
1439:             * @return all of this component's <code>PropertyChangeListener</code>s
1440:             *         or an empty array if no property change
1441:             *         listeners are currently registered
1442:             *
1443:             * @see #addBeanPropertyChangeListener(PropertyChangeListener)
1444:             * @see #removeBeanPropertyChangeListener(PropertyChangeListener)
1445:             * @see #getBeanPropertyChangeListeners(String)
1446:             * @see java.beans.PropertyChangeSupport#getPropertyChangeListeners()
1447:             */
1448:            public synchronized PropertyChangeListener[] getBeanPropertyChangeListeners() {
1449:                return beanAdapter.getBeanPropertyChangeListeners();
1450:            }
1451:
1452:            /**
1453:             * Returns an array of all the listeners which have been associated
1454:             * with the named property.
1455:             *
1456:             * @param propertyName   the name of the property to lookup listeners
1457:             * @return all of the <code>PropertyChangeListeners</code> associated with
1458:             *         the named property or an empty array if no listeners have
1459:             *         been added
1460:             *
1461:             * @see #addBeanPropertyChangeListener(String, PropertyChangeListener)
1462:             * @see #removeBeanPropertyChangeListener(String, PropertyChangeListener)
1463:             * @see #getBeanPropertyChangeListeners()
1464:             */
1465:            public synchronized PropertyChangeListener[] getBeanPropertyChangeListeners(
1466:                    String propertyName) {
1467:                return beanAdapter.getBeanPropertyChangeListeners(propertyName);
1468:            }
1469:
1470:            // Misc *******************************************************************
1471:
1472:            /**
1473:             * Removes the PropertyChangeHandler from the observed bean,
1474:             * if the bean is not <code>null</code>.
1475:             * Also removes all listeners from the bean that have been registered
1476:             * with <code>#addBeanPropertyChangeListener</code> before.<p>
1477:             *
1478:             * PresentationModels have a PropertyChangeListener registered with
1479:             * the target bean. Hence, a bean has a reference to all PresentationModels
1480:             * that hold it as bean. To avoid memory leaks it is recommended to remove
1481:             * this listener, if the bean lives much longer than the PresentationModel,
1482:             * enabling the garbage collector to remove the PresentationModel.
1483:             * To do so, you can call <code>setBean(null)</code> or set the
1484:             * bean channel's value to null.
1485:             * As an alternative you can use event listener lists in your beans
1486:             * that implement references with <code>WeakReference</code>.<p>
1487:             *
1488:             * Setting the bean to null has side-effects, for example the model
1489:             * fires a change event for the bound property <em>bean</em> and
1490:             * other properties.
1491:             * And the value of ValueModel's vent by this model may change.
1492:             * However, typically this is fine and setting the bean to null
1493:             * is the first choice for removing the reference from the bean to
1494:             * the PresentationModel.
1495:             * Another way to clear the reference from the target bean is to call
1496:             * this <code>#release</code> method. It has no side-effects, but
1497:             * the PresentationModel must not be used anymore once #release
1498:             * has been called.
1499:             *
1500:             * @see #setBean(Object)
1501:             * @see java.lang.ref.WeakReference
1502:             *
1503:             * @since 1.2
1504:             */
1505:            public void release() {
1506:                beanAdapter.release();
1507:            }
1508:
1509:            // Helper Class ***********************************************************
1510:
1511:            /**
1512:             * Holds a BufferedValueModel together with the names of the getter
1513:             * and setter. Used to look up models in <code>#getBufferedModel</code>.
1514:             * Also ensures that there are no two buffered models with different
1515:             * getter/setter pairs.
1516:             *
1517:             * @see PresentationModel#getBufferedModel(String)
1518:             * @see PresentationModel#getBufferedModel(String, String, String)
1519:             */
1520:            private static final class WrappedBuffer {
1521:
1522:                final BufferedValueModel buffer;
1523:                final String getterName;
1524:                final String setterName;
1525:
1526:                WrappedBuffer(BufferedValueModel buffer, String getterName,
1527:                        String setterName) {
1528:                    this .buffer = buffer;
1529:                    this .getterName = getterName;
1530:                    this .setterName = setterName;
1531:                }
1532:            }
1533:
1534:            // Event Handling and Forwarding Changes **********************************
1535:
1536:            /**
1537:             * Listens to changes of the bean, invoked the before and after methods,
1538:             * and forwards the bean change events.
1539:             */
1540:            private final class BeanChangeHandler implements 
1541:                    PropertyChangeListener {
1542:
1543:                /**
1544:                 * The target bean will change, changes, or has changed.
1545:                 *
1546:                 * @param evt   the property change event to be handled
1547:                 */
1548:                public void propertyChange(PropertyChangeEvent evt) {
1549:                    B oldBean = (B) evt.getOldValue();
1550:                    B newBean = (B) evt.getNewValue();
1551:                    String propertyName = evt.getPropertyName();
1552:                    if (BeanAdapter.PROPERTYNAME_BEFORE_BEAN
1553:                            .equals(propertyName)) {
1554:                        beforeBeanChange(oldBean, newBean);
1555:                    } else if (BeanAdapter.PROPERTYNAME_BEAN
1556:                            .equals(propertyName)) {
1557:                        firePropertyChange(PROPERTYNAME_BEAN, oldBean, newBean,
1558:                                true);
1559:                    } else if (BeanAdapter.PROPERTYNAME_AFTER_BEAN
1560:                            .equals(propertyName)) {
1561:                        afterBeanChange(oldBean, newBean);
1562:                    }
1563:                }
1564:            }
1565:
1566:            /**
1567:             * Updates the buffering state if a model buffering state changed.
1568:             */
1569:            private final class BufferingStateHandler implements 
1570:                    PropertyChangeListener {
1571:
1572:                /**
1573:                 * A registered BufferedValueModel has reported a change in its
1574:                 * <em>buffering</em> state. Update this model's buffering state.
1575:                 *
1576:                 * @param evt   describes the property change
1577:                 */
1578:                public void propertyChange(PropertyChangeEvent evt) {
1579:                    updateBufferingState(((Boolean) evt.getNewValue())
1580:                            .booleanValue());
1581:                }
1582:
1583:            }
1584:
1585:            /**
1586:             * Listens to model changes and updates the changed state.
1587:             */
1588:            private final class UpdateHandler implements  PropertyChangeListener {
1589:
1590:                /**
1591:                 * A registered ValueModel has changed.
1592:                 * Updates the changed state. If the property that changed is
1593:                 * 'changed' we assume that this is another changed state and
1594:                 * forward only changes to true. For all other property names,
1595:                 * we just update our changed state to true.
1596:                 *
1597:                 * @param evt   the event that describes the property change
1598:                 */
1599:                public void propertyChange(PropertyChangeEvent evt) {
1600:                    String propertyName = evt.getPropertyName();
1601:                    if (!PROPERTYNAME_CHANGED.equals(propertyName)
1602:                            || ((Boolean) evt.getNewValue()).booleanValue()) {
1603:                        setChanged(true);
1604:                    }
1605:                }
1606:            }
1607:
1608:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.