Source Code Cross Referenced for BeanAdapter.java in  » Swing-Library » jgoodies-data-binding » com » jgoodies » binding » beans » 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.beans 
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.beans;
0032:
0033:        import java.beans.PropertyChangeEvent;
0034:        import java.beans.PropertyChangeListener;
0035:        import java.beans.PropertyDescriptor;
0036:        import java.beans.PropertyVetoException;
0037:        import java.util.HashMap;
0038:        import java.util.LinkedList;
0039:        import java.util.List;
0040:        import java.util.Map;
0041:
0042:        import com.jgoodies.binding.value.AbstractValueModel;
0043:        import com.jgoodies.binding.value.ValueHolder;
0044:        import com.jgoodies.binding.value.ValueModel;
0045:
0046:        /**
0047:         * Converts multiple Java Bean properties into ValueModels.
0048:         * The bean properties must be single valued properties as described by the
0049:         * <a href="http://java.sun.com/products/javabeans/docs/spec.html">Java
0050:         * Bean Specification</a>. See below for a comparison with the more frequently
0051:         * used PresentationModel class and the rarely used PropertyAdapter.<p>
0052:         *
0053:         * ValueModels can be created for a property name using
0054:         * {@link #getValueModel(String)} or for a triple of (property name, getter
0055:         * name, setter name) using {@link #getValueModel(String, String, String)}.
0056:         * If you just specify the property name, the adapter uses the standard
0057:         * Java Bean introspection to lookup the available properties and how to
0058:         * read and write the property value. In case of custom readers and writers
0059:         * you may specify a custom BeanInfo class, or as a shortcut use the method
0060:         * that accepts the optional getter and setter name. If these are specified,
0061:         * introspection will be bypassed and a PropertyDescriptor will be
0062:         * created for the given property name, getter and setter name.
0063:         * <strong>Note: </strong> For each property name subsequent calls
0064:         * to these methods must use the same getter and setter names. Attempts
0065:         * to violate this constraint are rejected with an IllegalArgumentException.<p>
0066:         *
0067:         * Property values for a given property name can be read using
0068:         * {@link #getValue(String)}. To set a value for a for a property name
0069:         * invoke {@link #setValue(String, Object)}.<p>
0070:         *
0071:         * Optionally the BeanAdapter can observe changes in <em>bound
0072:         * properties</em> as described in section 7.4 of the Bean specification.
0073:         * The bean then must provide support for listening on properties as described
0074:         * in section 7.4 of this specification.
0075:         * You can enable this feature by setting the constructor parameter
0076:         * <code>observeChanges</code> to <code>true</code>.
0077:         * If the adapter observes changes, the ValueModels returned by
0078:         * <code>#getValueModel</code> will fire value change events,
0079:         * i.e. PropertyChangeEvents for the property <code>&quot;value&quot;</code>.
0080:         * Even if you ignore property changes, you can access the adapted
0081:         * property value via <code>#getValue()</code>.
0082:         * It's just that you won't be notified about changes.<p>
0083:         *
0084:         * In addition you can observe the bean's bound properties
0085:         * by registering PropertyChangeListeners with the bean using
0086:         * <code>#addBeanPropertyChangeListener</code>. These listeners will be removed
0087:         * from the old bean before the bean changes and will be re-added after
0088:         * the new bean has been set. Therefore these listeners will be notified
0089:         * about changes only if the current bean changes a property. They won't be
0090:         * notified if the bean changes - and in turn the property value. If you want
0091:         * to observes property changes caused by bean changes too, register with the
0092:         * adapting ValueModel as returned by <code>#getValueModel(String)</code>.<p>
0093:         *
0094:         * The BeanAdapter provides two access styles to the target bean
0095:         * that holds the adapted property: you can specify a bean directly,
0096:         * or you can use a <em>bean channel</em> to access the bean indirectly.
0097:         * In the latter case you specify a <code>ValueModel</code>
0098:         * that holds the bean that in turn holds the adapted properties.<p>
0099:         *
0100:         * If the adapted bean is <code>null</code> the BeanAdapter can
0101:         * neither read nor set a value. In this case <code>#getValue</code>
0102:         * returns <code>null</code> and <code>#setValue</code> will silently
0103:         * ignore the new value.<p>
0104:         *
0105:         * This adapter throws three PropertyChangeEvents if the bean changes:
0106:         * <em>beforeBean</em>, <em>bean</em> and <em>afterBean</em>. This is useful
0107:         * when sharing a bean channel and you must perform an operation before
0108:         * or after other listeners handle a bean change. Since you cannot rely
0109:         * on the order listeners will be notified, only the <em>beforeBean</em>
0110:         * and <em>afterBean</em> events are guaranteed to be fired before and
0111:         * after the bean change is fired.
0112:         * Note that <code>#getBean()</code> returns the new bean before
0113:         * any of these three PropertyChangeEvents is fired. Therefore listeners
0114:         * that handle these events must use the event's old and new value
0115:         * to determine the old and new bean.
0116:         * The order of events fired during a bean change is:<ol>
0117:         * <li>this adapter's bean channel fires a <em>value</em> change,
0118:         * <li>this adapter fires a <em>beforeBean</em> change,
0119:         * <li>this adapter fires the <em>bean</em> change,
0120:         * <li>this adapter fires an <em>afterBean</em> change.
0121:         * </ol><p>
0122:         *
0123:         * <strong>Note:</strong>
0124:         * BeanAdapters that observe changes have a PropertyChangeListener
0125:         * registered with the target bean. Hence, a bean has a reference
0126:         * to any BeanAdapter that observes it. To avoid memory leaks
0127:         * it is recommended to remove this listener if the bean lives much longer
0128:         * than the BeanAdapter, enabling the garbage collector to remove the adapter.
0129:         * To do so, you can call <code>setBean(null)</code> or set the
0130:         * bean channel's value to null.
0131:         * As an alternative you can use event listener lists in your beans
0132:         * that implement references with <code>WeakReference</code>.<p>
0133:         *
0134:         * Setting the bean to null has side-effects, for example the adapter
0135:         * fires a change event for the bound property <em>bean</em> and other properties.
0136:         * And the value of ValueModel's vended by this adapter may change.
0137:         * However, typically this is fine and setting the bean to null
0138:         * is the first choice for removing the reference from the bean to the adapter.
0139:         * Another way to clear the reference from the target bean is
0140:         * to call <code>#release</code>. It has no side-effects, but the adapter
0141:         * must not be used anymore once #release has been called.<p>
0142:         *
0143:         * <strong>Constraints:</strong> If property changes shall be observed,
0144:         * the bean class must support bound properties, i. e. it must provide
0145:         * the following pair of methods for registration of multicast property
0146:         * change event listeners:
0147:         * <pre>
0148:         * public void addPropertyChangeListener(PropertyChangeListener x);
0149:         * public void removePropertyChangeListener(PropertyChangeListener x);
0150:         * </pre>
0151:         *
0152:         * <strong>PropertyAdapter vs. BeanAdapter vs. PresentationModel</strong><br>
0153:         * Basically the BeanAdapter does for multiple properties what the
0154:         * {@link com.jgoodies.binding.beans.PropertyAdapter} does for a
0155:         * single bean property.
0156:         * If you adapt multiple properties of the same bean, you better use
0157:         * the BeanAdapter. It registers a single PropertyChangeListener with the bean,
0158:         * where multiple PropertyAdapters would register multiple listeners.
0159:         * If you adapt bean properties for an editor, you will typically use the
0160:         * {@link com.jgoodies.binding.PresentationModel}. The PresentationModel is
0161:         * more powerful than the BeanAdapter. It adds support for buffered models,
0162:         * and provides an extensible mechanism for observing the change state
0163:         * of the bean and related objects.<p>
0164:         *
0165:         * <strong>Basic Examples:</strong>
0166:         * <pre>
0167:         * // Direct access, ignores changes
0168:         * Address address = new Address()
0169:         * BeanAdapter adapter = new BeanAdapter(address);
0170:         * adapter.setValue("street", "Broadway");
0171:         * System.out.println(address.getStreet());        // Prints "Broadway"
0172:         * address.setStreet("Franz-Josef-Str.");
0173:         * System.out.println(adapter.getValue("street")); // Prints "Franz-Josef-Str."
0174:         *
0175:         *
0176:         * //Direct access, observes changes
0177:         * BeanAdapter adapter = new BeanAdapter(address, true);
0178:         *
0179:         *
0180:         * // Indirect access, ignores changes
0181:         * ValueHolder addressHolder = new ValueHolder(address1);
0182:         * BeanAdapter adapter = new BeanAdapter(addressHolder);
0183:         * adapter.setValue("street", "Broadway");         // Sets the street in address1
0184:         * System.out.println(address1.getStreet());       // Prints "Broadway"
0185:         * adapter.setBean(address2);
0186:         * adapter.setValue("street", "Robert-Koch-Str."); // Sets the street in address2
0187:         * System.out.println(address2.getStreet());       // Prints "Robert-Koch-Str."
0188:         *
0189:         *
0190:         * // Indirect access, observes changes
0191:         * ValueHolder addressHolder = new ValueHolder();
0192:         * BeanAdapter adapter = new BeanAdapter(addressHolder, true);
0193:         * addressHolder.setValue(address1);
0194:         * address1.setStreet("Broadway");
0195:         * System.out.println(adapter.getValue("street")); // Prints "Broadway"
0196:         *
0197:         *
0198:         * // Access through ValueModels
0199:         * Address address = new Address();
0200:         * BeanAdapter adapter = new BeanAdapter(address);
0201:         * ValueModel streetModel = adapter.getValueModel("street");
0202:         * ValueModel cityModel   = adapter.getValueModel("city");
0203:         * streetModel.setValue("Broadway");
0204:         * System.out.println(address.getStreet());        // Prints "Broadway"
0205:         * address.setCity("Hamburg");
0206:         * System.out.println(cityModel.getValue());       // Prints "Hamburg"
0207:         * </pre>
0208:         *
0209:         * <strong>Adapter Chain Example:</strong>
0210:         * <br>Builds an adapter chain from a domain model to the presentation layer.
0211:         * <pre>
0212:         * Country country = new Country();
0213:         * country.setName("Germany");
0214:         * country.setEuMember(true);
0215:         *
0216:         * BeanAdapter countryAdapter = new BeanAdapter(country, true);
0217:         *
0218:         * JTextField nameField = new JTextField();
0219:         * nameField.setDocument(new DocumentAdapter(
0220:         *     countryAdapter.getValueModel("name")));
0221:         *
0222:         * JCheckBox euMemberBox = new JCheckBox("Is EU Member");
0223:         * euMemberBox.setModel(new ToggleButtonAdapter(
0224:         *      countryAdapter.getValueModel("euMember")));
0225:         *
0226:         * // Using factory methods
0227:         * JTextField nameField   = Factory.createTextField(country, "name");
0228:         * JCheckBox  euMemberBox = Factory.createCheckBox (country, "euMember");
0229:         * euMemberBox.setText("Is EU Member");
0230:         * </pre><p>
0231:         *
0232:         * As of version 1.3 this class is no longer marked as final,
0233:         * but lacks the documentation for subclass constraints.
0234:         * I plan to introduce an interface that shall describe the semantics
0235:         * required by the PresentationModel class. Until then, this BeanAdapter
0236:         * implementation describes the semantics and all constraints.<p>
0237:         *
0238:         * TODO: Improve the class comment and focus on the main features.<p>
0239:         *
0240:         * TODO: Consider adding a feature to ensure that update notifications
0241:         * are performed in the event dispatch thread. In case the adapted bean
0242:         * is changed in a thread other than the event dispatch thread, such
0243:         * a feature would help complying with Swing's single thread rule.
0244:         * The feature could be implemented by an extended PropertyChangeSupport.<p>
0245:         *
0246:         * TODO: I plan to improve the support for adapting beans that do not fire
0247:         * PropertyChangeEvents. This affects the classes PropertyAdapter, BeanAdapter,
0248:         * and PresentationModel. Basically the PropertyAdapter and the BeanAdapter's
0249:         * internal SimplePropertyAdapter's shall be able to optionally self-fire
0250:         * a PropertyChangeEvent in case the bean does not. There are several
0251:         * downsides with self-firing events compared to bound bean properties.
0252:         * See <a href="https://binding.dev.java.net/issues/show_bug.cgi?id=49">Issue
0253:         * 49</a> for more information about the downsides.<p>
0254:         *
0255:         * The observeChanges constructor parameter shall be replaced by a more
0256:         * fine-grained choice to not observe (former observeChanges=false),
0257:         * to observe bound properties (former observeChanges=true), and a new
0258:         * setting for self-firing PropertyChangeEvents if a value is set.
0259:         * The latter case may be further splitted up to specify how the
0260:         * self-fired PropertyChangeEvent is created:
0261:         * <ol>
0262:         * <li>oldValue=null, newValue=null
0263:         * <li>oldValue=null, newValue=the value set
0264:         * <li>oldValue=value read before the set, newValue=the value set
0265:         * <li>oldValue=value read before the set, newValue=value read after the set
0266:         * </ol>
0267:         *
0268:         * @author  Karsten Lentzsch
0269:         * @version $Revision: 1.24 $
0270:         *
0271:         * @see     com.jgoodies.binding.beans.PropertyAdapter
0272:         * @see     ValueModel
0273:         * @see     ValueModel#getValue()
0274:         * @see     ValueModel#setValue(Object)
0275:         * @see     PropertyChangeEvent
0276:         * @see     PropertyChangeListener
0277:         * @see     java.beans.Introspector
0278:         * @see     java.beans.BeanInfo
0279:         * @see     PropertyDescriptor
0280:         *
0281:         * @param <B>  the type of the bean managed by this BeanAdapter
0282:         */
0283:        public class BeanAdapter<B> extends Model {
0284:
0285:            /**
0286:             * The property name used in the PropertyChangeEvent that is fired
0287:             * before the <em>bean</em> property fires its PropertyChangeEvent.
0288:             * Useful to perform an operation before listeners that handle the
0289:             * bean change are notified. See also the class comment.
0290:             */
0291:            public static final String PROPERTYNAME_BEFORE_BEAN = "beforeBean";
0292:
0293:            /**
0294:             * The name of the read-write bound property that holds the target bean.
0295:             *
0296:             * @see #getBean()
0297:             * @see #setBean(Object)
0298:             */
0299:            public static final String PROPERTYNAME_BEAN = "bean";
0300:
0301:            /**
0302:             * The property name used in the PropertyChangeEvent that is fired
0303:             * after the <em>bean</em> property fires its PropertyChangeEvent.
0304:             * Useful to perform an operation after listeners that handle the
0305:             * bean change are notified. See also the class comment.
0306:             */
0307:            public static final String PROPERTYNAME_AFTER_BEAN = "afterBean";
0308:
0309:            /**
0310:             * The name of the read-only bound bean property that
0311:             * indicates whether one of the observed properties has changed.
0312:             *
0313:             * @see #isChanged()
0314:             */
0315:            public static final String PROPERTYNAME_CHANGED = "changed";
0316:
0317:            // Fields *****************************************************************
0318:
0319:            /**
0320:             * Holds a ValueModel that holds the bean
0321:             * that in turn holds the adapted property.
0322:             *
0323:             * @see #getBean()
0324:             * @see #setBean(Object)
0325:             */
0326:            private final ValueModel beanChannel;
0327:
0328:            /**
0329:             * Specifies whether we observe property changes and in turn
0330:             * fire state changes.
0331:             *
0332:             * @see #getObserveChanges()
0333:             */
0334:            private final boolean observeChanges;
0335:
0336:            /**
0337:             * Maps property names to the associated SimplePropertyAdapters.
0338:             *
0339:             * @see #getValueModel(String)
0340:             * @see #getValueModel(String, String, String)
0341:             */
0342:            private final Map<String, SimplePropertyAdapter> propertyAdapters;
0343:
0344:            /**
0345:             * Refers to the IndirectPropertyChangeSupport that is used to redirect
0346:             * PropertyChangelisteners to the current target bean.
0347:             */
0348:            private IndirectPropertyChangeSupport indirectChangeSupport;
0349:
0350:            /**
0351:             * Refers to the old bean. Used as old value if the bean changes.
0352:             * Updated after a bean change in the BeanChangeHandler.
0353:             */
0354:            B storedOldBean;
0355:
0356:            /**
0357:             * Indicates whether a property in the current target been has changed.
0358:             * Will be reset to <code>false</code> every time the target bean changes.
0359:             *
0360:             * @see #isChanged()
0361:             * @see #setBean(Object)
0362:             */
0363:            private boolean changed = false;
0364:
0365:            /**
0366:             * The <code>PropertyChangeListener</code> used to handle changes
0367:             * in the bean properties. A new instance is created every time
0368:             * the target bean changes.
0369:             */
0370:            private PropertyChangeListener propertyChangeHandler;
0371:
0372:            // Instance creation ****************************************************
0373:
0374:            /**
0375:             * Constructs a BeanAdapter for the given bean;
0376:             * does not observe changes.<p>
0377:             *
0378:             * Installs a default bean channel that checks the identity not equity
0379:             * to ensure that listeners are reregistered properly if the old and
0380:             * new bean are equal but not the same.
0381:             *
0382:             * @param bean  the bean that owns the properties to adapt
0383:             */
0384:            public BeanAdapter(B bean) {
0385:                this (bean, false);
0386:            }
0387:
0388:            /**
0389:             * Constructs a BeanAdapter for the given bean;
0390:             * observes changes if specified.<p>
0391:             *
0392:             * Installs a default bean channel that checks the identity not equity
0393:             * to ensure that listeners are reregistered properly if the old and
0394:             * new bean are equal but not the same.
0395:             *
0396:             * @param bean             the bean that owns the properties to adapt
0397:             * @param observeChanges   <code>true</code> to observe changes of bound
0398:             *     or constrained properties, <code>false</code> to ignore changes
0399:             * @throws PropertyUnboundException  if <code>observeChanges</code>
0400:             *     is true but the property is unbound, i. e. the <code>bean</code>
0401:             *     does not provide a pair of methods to register a multicast
0402:             *     PropertyChangeListener
0403:             */
0404:            public BeanAdapter(B bean, boolean observeChanges) {
0405:                this (new ValueHolder(bean, true), observeChanges);
0406:            }
0407:
0408:            /**
0409:             * Constructs a BeanAdapter for the given bean channel;
0410:             * does not observe changes.<p>
0411:             *
0412:             * It is strongly recommended that the bean channel checks the identity
0413:             * not equity. This ensures that listeners are reregistered properly if
0414:             * the old and new bean are equal but not the same.
0415:             *
0416:             * @param beanChannel    the ValueModel that holds the bean
0417:             */
0418:            public BeanAdapter(ValueModel beanChannel) {
0419:                this (beanChannel, false);
0420:            }
0421:
0422:            /**
0423:             * Constructs a BeanAdapter for the given bean channel;
0424:             * observes changes if specified.<p>
0425:             *
0426:             * It is strongly recommended that the bean channel checks the identity
0427:             * not equity. This ensures that listeners are reregistered properly if
0428:             * the old and new bean are equal but not the same.
0429:             *
0430:             * @param beanChannel     the ValueModel that holds the bean
0431:             * @param observeChanges  <code>true</code> to observe changes of bound
0432:             *  or constrained properties, <code>false</code> to ignore changes
0433:             *
0434:             * @throws IllegalArgumentException    if the beanChannel is a ValueHolder
0435:             *     that has the identityCheck feature disabled
0436:             * @throws PropertyUnboundException    if <code>observeChanges</code>
0437:             *     is true but the property is unbound, i. e. the <code>bean</code>
0438:             *     does not provide a pair of methods to register a multicast
0439:             *     PropertyChangeListener
0440:             */
0441:            public BeanAdapter(ValueModel beanChannel, boolean observeChanges) {
0442:                this .beanChannel = beanChannel != null ? beanChannel
0443:                        : new ValueHolder(null, true);
0444:                checkBeanChannelIdentityCheck(beanChannel);
0445:
0446:                this .observeChanges = observeChanges;
0447:                this .propertyAdapters = new HashMap<String, SimplePropertyAdapter>();
0448:
0449:                this .beanChannel
0450:                        .addValueChangeListener(new BeanChangeHandler());
0451:
0452:                B initialBean = getBean();
0453:                if (initialBean != null) {
0454:                    if (observeChanges
0455:                            && !BeanUtils
0456:                                    .supportsBoundProperties(getBeanClass(initialBean)))
0457:                        throw new PropertyUnboundException(
0458:                                "The bean must provide support for listening on property changes "
0459:                                        + "as described in section 7.4.5 of the Java Bean Specification.");
0460:                    addChangeHandlerTo(initialBean);
0461:                }
0462:                storedOldBean = initialBean;
0463:            }
0464:
0465:            // Accessors ************************************************************
0466:
0467:            /**
0468:             * Returns the ValueModel that holds the bean that in turn holds
0469:             * the adapted properties. This bean channel is shared by the
0470:             * PropertyAdapters created by the factory method
0471:             * <code>#getValueModel</code>.
0472:             *
0473:             * @return the ValueModel that holds the bean that in turn
0474:             *     holds the adapted properties
0475:             *
0476:             * @see #getBean()
0477:             * @see #setBean(Object)
0478:             *
0479:             * @since 1.3
0480:             */
0481:            public ValueModel getBeanChannel() {
0482:                return beanChannel;
0483:            }
0484:
0485:            /**
0486:             * Returns the Java Bean that holds the adapted properties.
0487:             *
0488:             * @return the Bean that holds the adapted properties
0489:             *
0490:             * @see #setBean(Object)
0491:             */
0492:            public B getBean() {
0493:                return (B) beanChannel.getValue();
0494:            }
0495:
0496:            /**
0497:             * Sets a new Java Bean as holder of the adapted properties.
0498:             * Notifies any registered value listeners that are registered
0499:             * with the adapting ValueModels created in <code>#getValueModel</code>.
0500:             * Also notifies listeners that have been registered with this adapter
0501:             * to observe the bound property <em>bean</em>.<p>
0502:             *
0503:             * Resets the changed state to <code>false</code>.<p>
0504:             *
0505:             * If this adapter observes bean changes, the bean change handler
0506:             * will be removed from the former bean and will be added to the new bean.
0507:             * Hence, if the new bean is <code>null</code>, this adapter has no
0508:             * listener registered with a bean.
0509:             * And so, <code>setBean(null)</code> can be used as a clean release method
0510:             * that allows to use this adapter later again.
0511:             *
0512:             * @param newBean  the new holder of the adapted properties
0513:             *
0514:             * @see #getBean()
0515:             * @see #isChanged()
0516:             * @see #resetChanged()
0517:             * @see #release()
0518:             */
0519:            public void setBean(B newBean) {
0520:                beanChannel.setValue(newBean);
0521:                resetChanged();
0522:            }
0523:
0524:            /**
0525:             * Answers whether this adapter observes changes in the
0526:             * adapted Bean properties.
0527:             *
0528:             * @return true if this adapter observes changes, false if not
0529:             */
0530:            public boolean getObserveChanges() {
0531:                return observeChanges;
0532:            }
0533:
0534:            /*
0535:             * Sets whether changes in the bean's bound property shall be observed.
0536:             * As a requirement the bean must provide support for listenening
0537:             * on property changes.
0538:             *
0539:             * @param newValue  true to observe changes, false to ignore them
0540:            public void setObserveChanges(boolean newValue) {
0541:                if (newValue == getObserveChanges())
0542:                    return;
0543:                observeChanges = newValue;
0544:                Object bean = getBean();
0545:                removePropertyChangeHandler(bean);
0546:                addPropertyChangeHandler(bean);
0547:            }
0548:             */
0549:
0550:            // Accessing Property Values **********************************************
0551:            /**
0552:             * Returns the value of specified bean property, <code>null</code>
0553:             * if the current bean is <code>null</code>.<p>
0554:             *
0555:             * This operation is supported only for readable bean properties.
0556:             *
0557:             * @param propertyName  the name of the property to be read
0558:             * @return the value of the adapted bean property, null if the bean is null
0559:             *
0560:             * @throws NullPointerException           if propertyName is null
0561:             * @throws UnsupportedOperationException  if the property is write-only
0562:             * @throws PropertyNotFoundException      if the property could not be found
0563:             * @throws PropertyAccessException        if the value could not be read
0564:             */
0565:            public Object getValue(String propertyName) {
0566:                return getValueModel(propertyName).getValue();
0567:            }
0568:
0569:            /**
0570:             * Sets the given new value for the specified bean property. Does nothing
0571:             * if this adapter's bean is <code>null</code>. If the setter associated
0572:             * with the propertyName throws a PropertyVetoException, it is silently
0573:             * ignored.<p>
0574:             *
0575:             * Notifies the associated value change listeners if the bean reports
0576:             * a property change. Note that a bean may suppress PropertyChangeEvents
0577:             * if the old and new value are the same, or if the old and new value
0578:             * are equal.<p>
0579:             *
0580:             * This operation is supported only for writable bean properties.
0581:             *
0582:             * @param propertyName   the name of the property to set
0583:             * @param newValue       the value to set
0584:             *
0585:             * @throws NullPointerException           if propertyName is null
0586:             * @throws UnsupportedOperationException  if the property is read-only
0587:             * @throws PropertyNotFoundException      if the property could not be found
0588:             * @throws PropertyAccessException        if the new value could not be set
0589:             */
0590:            public void setValue(String propertyName, Object newValue) {
0591:                getValueModel(propertyName).setValue(newValue);
0592:            }
0593:
0594:            /**
0595:             * Sets a new value for the specified bean property. Does nothing if the
0596:             * bean is <code>null</code>. If the setter associated with the propertyName
0597:             * throws a PropertyVetoException, this methods throws the same exception.<p>
0598:             *
0599:             * Notifies the associated value change listeners if the bean reports
0600:             * a property change. Note that a bean may suppress PropertyChangeEvents
0601:             * if the old and new value are the same, or if the old and new value
0602:             * are equal.<p>
0603:             *
0604:             * This operation is supported only for writable bean properties.
0605:             *
0606:             * @param propertyName   the name of the property to set
0607:             * @param newValue       the value to set
0608:             *
0609:             * @throws NullPointerException           if propertyName is null
0610:             * @throws UnsupportedOperationException  if the property is read-only
0611:             * @throws PropertyNotFoundException      if the property could not be found
0612:             * @throws PropertyAccessException        if the new value could not be set
0613:             * @throws PropertyVetoException          if the bean setter
0614:             *     throws a PropertyVetoException
0615:             *
0616:             * @since 1.1
0617:             */
0618:            public void setVetoableValue(String propertyName, Object newValue)
0619:                    throws PropertyVetoException {
0620:                getValueModel(propertyName).setVetoableValue(newValue);
0621:            }
0622:
0623:            // Creating and Accessing Adapting ValueModels ****************************
0624:
0625:            /**
0626:             * Looks up and lazily creates a ValueModel that adapts
0627:             * the bound property with the specified name. Uses the
0628:             * Bean introspection to look up the getter and setter names.<p>
0629:             *
0630:             * Subsequent calls to this method with the same property name
0631:             * return the same ValueModel.<p>
0632:             *
0633:             * To prevent potential runtime errors this method eagerly looks up
0634:             * the associated PropertyDescriptor if the target bean is not null.<p>
0635:             *
0636:             * For each property name all calls to this method and to
0637:             * <code>#getValueModel(String, String, String)</code> must use
0638:             * the same getter and setter names. Attempts to violate this constraint
0639:             * will be rejected with an IllegalArgumentException. Especially once
0640:             * you've called this method you must not call
0641:             * <code>#getValueModel(String, String, String)</code> with a non-null
0642:             * getter or setter name. And vice versa, once you've called the latter
0643:             * method with a non-null getter or setter name, you must not call
0644:             * this method.<p>
0645:             *
0646:             * This method uses a return type of AbstractValueModel, not a ValueModel.
0647:             * This makes {@link #setVetoableValue(String, Object)} visible. It also
0648:             * makes the AbstractValueModel convenience type converters available,
0649:             * which can significantly shrink the source code necessary to read and
0650:             * write values from/to these models.
0651:             *
0652:             * @param propertyName   the name of the property to adapt
0653:             * @return a ValueModel that adapts the property with the specified name
0654:             *
0655:             * @throws NullPointerException       if propertyName is null
0656:             * @throws PropertyNotFoundException  if the property could not be found
0657:             * @throws IllegalArgumentException
0658:             *     if <code>#getValueModel(String, String, String)</code> has been
0659:             *     called before with the same property name and a non-null getter
0660:             *     or setter name
0661:             */
0662:            public SimplePropertyAdapter getValueModel(String propertyName) {
0663:                return getValueModel(propertyName, null, null);
0664:            }
0665:
0666:            /**
0667:             * Looks up and lazily creates a ValueModel that adapts the bound property
0668:             * with the specified name. Unlike <code>#getValueModel(String)</code>
0669:             * this method bypasses the Bean Introspection and uses the given getter
0670:             * and setter names to setup the access to the adapted Bean property.<p>
0671:             *
0672:             * Subsequent calls to this method with the same parameters
0673:             * will return the same ValueModel.<p>
0674:             *
0675:             * To prevent potential runtime errors this method eagerly looks up
0676:             * the associated PropertyDescriptor if the target bean is not null.<p>
0677:             *
0678:             * For each property name all calls to this method
0679:             * and to <code>#getValueModel(String)</code> must use the same
0680:             * getter and setter names. Attempts to violate this constraint
0681:             * will be rejected with an IllegalArgumentException. Especially
0682:             * once you've called this method with a non-null getter or setter name,
0683:             * you must not call <code>#getValueModel(String)</code>. And vice versa,
0684:             * once you've called the latter method you must not call this method
0685:             * with a non-null getter or setter name.<p>
0686:             *
0687:             * This method uses a return type of AbstractValueModel, not a ValueModel.
0688:             * This makes {@link #setVetoableValue(String, Object)} visible. It also
0689:             * makes the AbstractValueModel convenience type converters available,
0690:             * which can significantly shrink the source code necessary to read and
0691:             * write values from/to these models.
0692:             *
0693:             * @param propertyName   the name of the property to adapt
0694:             * @param getterName     the name of the method that reads the value
0695:             * @param setterName     the name of the method that sets the value
0696:             * @return a ValueModel that adapts the property with the specified name
0697:             *
0698:             * @throws NullPointerException       if propertyName is null
0699:             * @throws PropertyNotFoundException  if the property could not be found
0700:             * @throws IllegalArgumentException   if this method has been called before
0701:             *     with the same property name and different getter or setter names
0702:             */
0703:            public SimplePropertyAdapter getValueModel(String propertyName,
0704:                    String getterName, String setterName) {
0705:                if (propertyName == null)
0706:                    throw new NullPointerException(
0707:                            "The property name must not be null.");
0708:
0709:                SimplePropertyAdapter adaptingModel = getPropertyAdapter(propertyName);
0710:                if (adaptingModel == null) {
0711:                    adaptingModel = createPropertyAdapter(propertyName,
0712:                            getterName, setterName);
0713:                    propertyAdapters.put(propertyName, adaptingModel);
0714:                } else if (!equals(getterName, adaptingModel.getterName)
0715:                        || !equals(setterName, adaptingModel.setterName)) {
0716:                    throw new IllegalArgumentException(
0717:                            "You must not invoke this method twice "
0718:                                    + "with different getter and/or setter names.");
0719:                }
0720:                return adaptingModel;
0721:            }
0722:
0723:            /**
0724:             * Looks up and returns the SimplePropertyAdapter that adapts
0725:             * the bound property with the specified name.
0726:             *
0727:             * @param propertyName   the name of the adapted property
0728:             * @return a SimplePropertyAdapter that adapts the bound property
0729:             *    with the specified name or null, if none has been created before
0730:             */
0731:            SimplePropertyAdapter getPropertyAdapter(String propertyName) {
0732:                return propertyAdapters.get(propertyName);
0733:            }
0734:
0735:            /**
0736:             * Creates and returns a SimplePropertyAdapter that adapts
0737:             * the bound property with the specified name.
0738:             *
0739:             * @param propertyName   the name of the property to adapt
0740:             * @param getterName     the name of the method that reads the value
0741:             * @param setterName     the name of the method that sets the value
0742:             * @return a SimplePropertyAdapter that adapts the property
0743:             *     with the specified name
0744:             *
0745:             * @since 1.4
0746:             */
0747:            protected SimplePropertyAdapter createPropertyAdapter(
0748:                    String propertyName, String getterName, String setterName) {
0749:                return new SimplePropertyAdapter(propertyName, getterName,
0750:                        setterName);
0751:            }
0752:
0753:            // Accessing the Changed State ********************************************
0754:
0755:            /**
0756:             * Answers whether a bean property has changed since the changed state
0757:             * has been reset. The changed state is implicitly reset every time
0758:             * the target bean changes.
0759:             *
0760:             * @return true if a property of the current target bean
0761:             *     has changed since the last reset
0762:             */
0763:            public boolean isChanged() {
0764:                return changed;
0765:            }
0766:
0767:            /**
0768:             * Resets this tracker's changed state to <code>false</code>.
0769:             */
0770:            public void resetChanged() {
0771:                setChanged(false);
0772:            }
0773:
0774:            /**
0775:             * Sets the changed state to the given value. Invoked by the global
0776:             * PropertyChangeHandler that observes all bean changes. Also invoked
0777:             * by <code>#resetChanged</code>.
0778:             *
0779:             * @param newValue  the new changed state
0780:             */
0781:            private void setChanged(boolean newValue) {
0782:                boolean oldValue = isChanged();
0783:                changed = newValue;
0784:                firePropertyChange(PROPERTYNAME_CHANGED, oldValue, newValue);
0785:            }
0786:
0787:            // Managing Bean Property Change Listeners *******************************
0788:
0789:            /**
0790:             * Adds a PropertyChangeListener to the list of bean listeners. The
0791:             * listener is registered for all bound properties of the target bean.<p>
0792:             *
0793:             * The listener will be notified if and only if this BeanAdapter's current
0794:             * bean changes a property. It'll not be notified if the bean changes.<p>
0795:             *
0796:             * If listener is <code>null</code>, no exception is thrown and no action
0797:             * is performed.
0798:             *
0799:             * @param listener      the PropertyChangeListener to be added
0800:             *
0801:             * @see #removeBeanPropertyChangeListener(PropertyChangeListener)
0802:             * @see #removeBeanPropertyChangeListener(String, PropertyChangeListener)
0803:             * @see #addBeanPropertyChangeListener(String, PropertyChangeListener)
0804:             * @see #getBeanPropertyChangeListeners()
0805:             */
0806:            public synchronized void addBeanPropertyChangeListener(
0807:                    PropertyChangeListener listener) {
0808:                if (listener == null) {
0809:                    return;
0810:                }
0811:                if (indirectChangeSupport == null) {
0812:                    indirectChangeSupport = new IndirectPropertyChangeSupport(
0813:                            beanChannel);
0814:                }
0815:                indirectChangeSupport.addPropertyChangeListener(listener);
0816:            }
0817:
0818:            /**
0819:             * Removes a PropertyChangeListener from the list of bean listeners.
0820:             * This method should be used to remove PropertyChangeListeners that
0821:             * were registered for all bound properties of the target bean.<p>
0822:             *
0823:             * If listener is <code>null</code>, no exception is thrown and no action is performed.
0824:             *
0825:             * @param listener      the PropertyChangeListener to be removed
0826:             *
0827:             * @see #addBeanPropertyChangeListener(PropertyChangeListener)
0828:             * @see #addBeanPropertyChangeListener(String, PropertyChangeListener)
0829:             * @see #removeBeanPropertyChangeListener(String, PropertyChangeListener)
0830:             * @see #getBeanPropertyChangeListeners()
0831:             */
0832:            public synchronized void removeBeanPropertyChangeListener(
0833:                    PropertyChangeListener listener) {
0834:                if (listener == null || indirectChangeSupport == null) {
0835:                    return;
0836:                }
0837:                indirectChangeSupport.removePropertyChangeListener(listener);
0838:            }
0839:
0840:            /**
0841:             * Adds a PropertyChangeListener to the list of bean listeners for a
0842:             * specific property. The specified property may be user-defined.<p>
0843:             *
0844:             * The listener will be notified if and only if this BeanAdapter's
0845:             * current bean changes the specified property. It'll not be notified
0846:             * if the bean changes. If you want to observe property changes and
0847:             * bean changes, you may observe the ValueModel that adapts this property
0848:             * - as returned by <code>#getValueModel(String)</code>.<p>
0849:             *
0850:             * Note that if the bean is inheriting a bound property, then no event
0851:             * will be fired in response to a change in the inherited property.<p>
0852:             *
0853:             * If listener is <code>null</code>, no exception is thrown and no action is performed.
0854:             *
0855:             * @param propertyName      one of the property names listed above
0856:             * @param listener          the PropertyChangeListener to be added
0857:             *
0858:             * @see #removeBeanPropertyChangeListener(String, PropertyChangeListener)
0859:             * @see #addBeanPropertyChangeListener(String, PropertyChangeListener)
0860:             * @see #getBeanPropertyChangeListeners(String)
0861:             */
0862:            public synchronized void addBeanPropertyChangeListener(
0863:                    String propertyName, PropertyChangeListener listener) {
0864:                if (listener == null) {
0865:                    return;
0866:                }
0867:                if (indirectChangeSupport == null) {
0868:                    indirectChangeSupport = new IndirectPropertyChangeSupport(
0869:                            beanChannel);
0870:                }
0871:                indirectChangeSupport.addPropertyChangeListener(propertyName,
0872:                        listener);
0873:            }
0874:
0875:            /**
0876:             * Removes a PropertyChangeListener from the listener list for a specific
0877:             * property. This method should be used to remove PropertyChangeListeners
0878:             * that were registered for a specific bound property.<p>
0879:             *
0880:             * If listener is <code>null</code>, no exception is thrown and no action is performed.
0881:             *
0882:             * @param propertyName      a valid property name
0883:             * @param listener          the PropertyChangeListener to be removed
0884:             *
0885:             * @see #addBeanPropertyChangeListener(String, PropertyChangeListener)
0886:             * @see #removeBeanPropertyChangeListener(PropertyChangeListener)
0887:             * @see #getBeanPropertyChangeListeners(String)
0888:             */
0889:            public synchronized void removeBeanPropertyChangeListener(
0890:                    String propertyName, PropertyChangeListener listener) {
0891:                if (listener == null || indirectChangeSupport == null) {
0892:                    return;
0893:                }
0894:                indirectChangeSupport.removePropertyChangeListener(
0895:                        propertyName, listener);
0896:            }
0897:
0898:            // Requesting Listener Sets ***********************************************
0899:
0900:            /**
0901:             * Returns an array of all the property change listeners
0902:             * registered on this component.
0903:             *
0904:             * @return all of this component's <code>PropertyChangeListener</code>s
0905:             *         or an empty array if no property change
0906:             *         listeners are currently registered
0907:             *
0908:             * @see #addBeanPropertyChangeListener(PropertyChangeListener)
0909:             * @see #removeBeanPropertyChangeListener(PropertyChangeListener)
0910:             * @see #getBeanPropertyChangeListeners(String)
0911:             * @see java.beans.PropertyChangeSupport#getPropertyChangeListeners()
0912:             */
0913:            public synchronized PropertyChangeListener[] getBeanPropertyChangeListeners() {
0914:                if (indirectChangeSupport == null) {
0915:                    return new PropertyChangeListener[0];
0916:                }
0917:                return indirectChangeSupport.getPropertyChangeListeners();
0918:            }
0919:
0920:            /**
0921:             * Returns an array of all the listeners which have been associated
0922:             * with the named property.
0923:             *
0924:             * @param propertyName   the name of the property to lookup listeners
0925:             * @return all of the <code>PropertyChangeListeners</code> associated with
0926:             *         the named property or an empty array if no listeners have
0927:             *         been added
0928:             *
0929:             * @see #addBeanPropertyChangeListener(String, PropertyChangeListener)
0930:             * @see #removeBeanPropertyChangeListener(String, PropertyChangeListener)
0931:             * @see #getBeanPropertyChangeListeners()
0932:             */
0933:            public synchronized PropertyChangeListener[] getBeanPropertyChangeListeners(
0934:                    String propertyName) {
0935:                if (indirectChangeSupport == null) {
0936:                    return new PropertyChangeListener[0];
0937:                }
0938:                return indirectChangeSupport
0939:                        .getPropertyChangeListeners(propertyName);
0940:            }
0941:
0942:            // Releasing PropertyChangeListeners **************************************
0943:
0944:            /**
0945:             * Removes the PropertyChangeHandler from the observed bean, if the bean
0946:             * is not <code>null</code> and if bean property changes are observed.
0947:             * Also removes all listeners from the bean that have been registered
0948:             * with <code>#addBeanPropertyChangeListener</code> before.<p>
0949:             *
0950:             * BeanAdapters that observe changes have a PropertyChangeListener
0951:             * registered with the target bean. Hence, a bean has a reference
0952:             * to all BeanAdapters that observe it. To avoid memory leaks
0953:             * it is recommended to remove this listener if the bean lives much longer
0954:             * than the BeanAdapter, enabling the garbage collector to remove the adapter.
0955:             * To do so, you can call <code>setBean(null)</code> or set the
0956:             * bean channel's value to null.
0957:             * As an alternative you can use event listener lists in your beans
0958:             * that implement references with <code>WeakReference</code>.<p>
0959:             *
0960:             * Setting the bean to null has side-effects, for example the adapter
0961:             * fires a change event for the bound property <em>bean</em> and other properties.
0962:             * And the value of ValueModel's vended by this adapter may change.
0963:             * However, typically this is fine and setting the bean to null
0964:             * is the first choice for removing the reference from the bean to the adapter.
0965:             * Another way to clear the reference from the target bean is
0966:             * to call <code>#release</code>. It has no side-effects, but the adapter
0967:             * must not be used anymore once #release has been called.
0968:             *
0969:             * @see #setBean(Object)
0970:             * @see java.lang.ref.WeakReference
0971:             */
0972:            public synchronized void release() {
0973:                removeChangeHandlerFrom(getBean());
0974:                if (indirectChangeSupport != null) {
0975:                    indirectChangeSupport.removeAll();
0976:                }
0977:            }
0978:
0979:            // Changing the Bean & Adding and Removing the PropertyChangeHandler ******
0980:
0981:            private void setBean0(B oldBean, B newBean) {
0982:                firePropertyChange(PROPERTYNAME_BEFORE_BEAN, oldBean, newBean,
0983:                        true);
0984:                removeChangeHandlerFrom(oldBean);
0985:                forwardAllAdaptedValuesChanged(oldBean, newBean);
0986:                resetChanged();
0987:                addChangeHandlerTo(newBean);
0988:                firePropertyChange(PROPERTYNAME_BEAN, oldBean, newBean, true);
0989:                firePropertyChange(PROPERTYNAME_AFTER_BEAN, oldBean, newBean,
0990:                        true);
0991:            }
0992:
0993:            /**
0994:             * Iterates over all internal property adapters to notify them
0995:             * about a bean change from oldBean to newBean. These adapters then notify
0996:             * their observers to inform them about a value change - if any.<p>
0997:             *
0998:             * Iterates over a copy of the property adapters to avoid
0999:             * ConcurrentModifications that may be thrown if a listener creates
1000:             * a new SimplePropertyAdapter by requesting an adapting model using
1001:             * <code>#getValueModel</code>.
1002:             *
1003:             * @param oldBean   the bean before the change
1004:             * @param newBean   the bean after the change
1005:             */
1006:            private void forwardAllAdaptedValuesChanged(B oldBean, B newBean) {
1007:                List<SimplePropertyAdapter> copiedList = new LinkedList<SimplePropertyAdapter>(
1008:                        propertyAdapters.values());
1009:                for (SimplePropertyAdapter adapter : copiedList) {
1010:                    adapter.setBean0(oldBean, newBean);
1011:                }
1012:            }
1013:
1014:            /**
1015:             * Iterates over all internal property adapters to notify them
1016:             * about a value change in the bean. These adapters then notify
1017:             * their observers to inform them about a value change - if any.<p>
1018:             *
1019:             * Iterates over a copy of the property adapters to avoid
1020:             * ConcurrentModifications that may be thrown if a listener creates
1021:             * a new SimplePropertyAdapter by requesting an adapting model using
1022:             * <code>#getValueModel</code>.
1023:             */
1024:            private void forwardAllAdaptedValuesChanged() {
1025:                B currentBean = getBean();
1026:                List<SimplePropertyAdapter> copiedList = new LinkedList<SimplePropertyAdapter>(
1027:                        propertyAdapters.values());
1028:                for (SimplePropertyAdapter adapter : copiedList) {
1029:                    adapter.fireChange(currentBean);
1030:                }
1031:            }
1032:
1033:            /**
1034:             * Adds a property change listener to the given bean if we observe changes
1035:             * and the bean is not null. First checks whether the bean class
1036:             * supports <em>bound properties</em>, i.e. it provides a pair of methods
1037:             * to register multicast property change event listeners;
1038:             * see section 7.4.1 of the Java Beans specification for details.
1039:             *
1040:             * @param bean  the bean to add a property change handler.
1041:             * @throws PropertyUnboundException
1042:             *     if the bean does not support bound properties
1043:             * @throws PropertyNotBindableException
1044:             *     if the property change handler cannot be added successfully
1045:             *
1046:             * @see #removeChangeHandlerFrom(Object)
1047:             */
1048:            private void addChangeHandlerTo(B bean) {
1049:                if (!observeChanges || bean == null) {
1050:                    return;
1051:                }
1052:                propertyChangeHandler = new PropertyChangeHandler();
1053:                BeanUtils.addPropertyChangeListener(bean, getBeanClass(bean),
1054:                        propertyChangeHandler);
1055:            }
1056:
1057:            /**
1058:             * Removes the formerly added property change handler from the given bean
1059:             * if we observe changes and the bean is not null.
1060:             *
1061:             * @param bean  the bean to remove the property change handler from.
1062:             * @throws PropertyUnboundException
1063:             *     if the bean does not support bound properties
1064:             * @throws PropertyNotBindableException
1065:             *     if the property change handler cannot be removed successfully
1066:             *
1067:             * @see #addChangeHandlerTo(Object)
1068:             */
1069:            private void removeChangeHandlerFrom(B bean) {
1070:                if (!observeChanges || bean == null) {
1071:                    return;
1072:                }
1073:                BeanUtils.removePropertyChangeListener(bean,
1074:                        getBeanClass(bean), propertyChangeHandler);
1075:                propertyChangeHandler = null;
1076:            }
1077:
1078:            // Helper Methods to Get and Set a Property Value *************************
1079:
1080:            /**
1081:             * Returns the Java Bean class used by this adapter.
1082:             * The current implementation just returns the given bean's class.<p>
1083:             *
1084:             * A future version may return a type other than the concrete
1085:             * class of the given bean. This beanClass could be specified
1086:             * in a new set of constructors. This is useful if the beans
1087:             * are specified by public interfaces, and implemented by
1088:             * package private classes. In this case, the class of the given bean
1089:             * object shall be checked against the specified type.
1090:             *
1091:             * @param bean    the bean that may be used to lookup the class from
1092:             * @return the Java Bean class used for this adapter.
1093:             */
1094:            private Class<?> getBeanClass(B bean) {
1095:                return bean.getClass();
1096:                // TODO: A future version shall add a check like
1097:                // beanClass.isInstance(bean) if the beanClass
1098:                // has been specified in the constructor.
1099:            }
1100:
1101:            /**
1102:             * Returns the value of the specified property of the given bean,
1103:             * <code>null</code> if the bean is <code>null</code>.
1104:             *
1105:             * @param bean                the bean to read the value from
1106:             * @param propertyDescriptor  describes the property to be read
1107:             * @return the bean's property value
1108:             */
1109:            private Object getValue0(B bean,
1110:                    PropertyDescriptor propertyDescriptor) {
1111:                return bean == null ? null : BeanUtils.getValue(bean,
1112:                        propertyDescriptor);
1113:            }
1114:
1115:            /**
1116:             * Sets the given object as new value of the specified property of the
1117:             * given bean. Does nothing if the bean is null. This write operation is
1118:             * supported only for writable bean properties.
1119:             *
1120:             * @param bean                the bean that holds the adapted property
1121:             * @param propertyDescriptor  describes the property to be set
1122:             * @param newValue            the property value to be set
1123:             *
1124:             * @throws NullPointerException        if the bean is null
1125:             * @throws PropertyNotFoundException   if the property could not be found
1126:             * @throws PropertyAccessException     if the write access failed
1127:             * @throws PropertyVetoException       if the invoked bean setter
1128:             *     throws a PropertyVetoException
1129:             */
1130:            private void setValue0(B bean,
1131:                    PropertyDescriptor propertyDescriptor, Object newValue)
1132:                    throws PropertyVetoException {
1133:                BeanUtils.setValue(bean, propertyDescriptor, newValue);
1134:            }
1135:
1136:            /**
1137:             * Throws an IllegalArgumentException if the given ValueModel
1138:             * is a ValueHolder that has the identityCheck feature disabled.
1139:             */
1140:            private void checkBeanChannelIdentityCheck(ValueModel valueModel) {
1141:                if (!(valueModel instanceof  ValueHolder))
1142:                    return;
1143:
1144:                ValueHolder valueHolder = (ValueHolder) valueModel;
1145:                if (!valueHolder.isIdentityCheckEnabled())
1146:                    throw new IllegalArgumentException(
1147:                            "The bean channel must have the identity check enabled.");
1148:            }
1149:
1150:            // Helper Classes *********************************************************
1151:
1152:            /**
1153:             * Listens to changes of the bean.
1154:             */
1155:            private final class BeanChangeHandler implements 
1156:                    PropertyChangeListener {
1157:
1158:                /**
1159:                 * The bean has been changed. Uses the stored old bean instead of
1160:                 * the event's old value, because the latter can be null.
1161:                 * If the event's new value is null, the new bean is requested
1162:                 * from the bean channel.
1163:                 *
1164:                 * @param evt   the property change event to be handled
1165:                 */
1166:                public void propertyChange(PropertyChangeEvent evt) {
1167:                    B newBean = evt.getNewValue() != null ? (B) evt
1168:                            .getNewValue() : getBean();
1169:                    setBean0(storedOldBean, newBean);
1170:                    storedOldBean = newBean;
1171:                }
1172:            }
1173:
1174:            /**
1175:             * Listens to changes of all bean properties. Fires property changes
1176:             * if the associated property or an arbitrary set of properties has changed.
1177:             */
1178:            private final class PropertyChangeHandler implements 
1179:                    PropertyChangeListener {
1180:
1181:                /**
1182:                 * A bean property has been changed. Sets the changed state to true.
1183:                 * Checks whether the observed or multiple properties have changed.
1184:                 * If so, notifies all registered listeners about the change.
1185:                 *
1186:                 * @param evt   the property change event to be handled
1187:                 */
1188:                public void propertyChange(PropertyChangeEvent evt) {
1189:                    setChanged(true);
1190:                    String propertyName = evt.getPropertyName();
1191:                    if (propertyName == null) {
1192:                        forwardAllAdaptedValuesChanged();
1193:                    } else {
1194:                        SimplePropertyAdapter adapter = getPropertyAdapter(propertyName);
1195:                        if (adapter != null) {
1196:                            adapter.fireValueChange(evt.getOldValue(), evt
1197:                                    .getNewValue(), true);
1198:                        }
1199:                    }
1200:                }
1201:            }
1202:
1203:            /**
1204:             * Implements the access to the individual bean properties.
1205:             * All SimplePropertyAdapters created by this BeanAdapter
1206:             * share a single PropertyChangeListener that is used to
1207:             * fire value changes in this SimplePropertyAdapter.<p>
1208:             *
1209:             * This class is public to enable reflection access.
1210:             */
1211:            public class SimplePropertyAdapter extends AbstractValueModel {
1212:
1213:                /**
1214:                 * Holds the name of the adapted property.
1215:                 */
1216:                private final String propertyName;
1217:
1218:                /**
1219:                 * Holds the optional name of the property's getter.
1220:                 * Used to create the PropertyDescriptor.
1221:                 * Also used to reject potential misuse of
1222:                 * {@link BeanAdapter#getValueModel(String)} and
1223:                 * {@link BeanAdapter#getValueModel(String, String, String)}.
1224:                 * See the latter methods for details.
1225:                 */
1226:                final String getterName;
1227:
1228:                /**
1229:                 * Holds the optional name of the property's setter.
1230:                 * Used to create the PropertyDescriptor.
1231:                 * Also used to reject potential misuse of
1232:                 * {@link BeanAdapter#getValueModel(String)} and
1233:                 * {@link BeanAdapter#getValueModel(String, String, String)}.
1234:                 * See the latter methods for details.
1235:                 */
1236:                final String setterName;
1237:
1238:                /**
1239:                 * Describes the property accessor; basically a getter and setter.
1240:                 */
1241:                private PropertyDescriptor cachedPropertyDescriptor;
1242:
1243:                /**
1244:                 * Holds the bean class associated with the cached property descriptor.
1245:                 */
1246:                private Class<?> cachedBeanClass;
1247:
1248:                // Instance Creation --------------------------------------------------
1249:
1250:                /**
1251:                 * Constructs a SimplePropertyAdapter for the given property name,
1252:                 * getter and setter name.
1253:                 *
1254:                 * @param propertyName   the name of the property to adapt
1255:                 * @param getterName     the name of the method that reads the value
1256:                 * @param setterName     the name of the method that sets the value
1257:                 */
1258:                protected SimplePropertyAdapter(String propertyName,
1259:                        String getterName, String setterName) {
1260:                    this .propertyName = propertyName;
1261:                    this .getterName = getterName;
1262:                    this .setterName = setterName;
1263:
1264:                    // Eagerly check the existence of the property to adapt.
1265:                    B bean = getBean();
1266:                    if (bean != null) {
1267:                        getPropertyDescriptor(bean);
1268:                    }
1269:                }
1270:
1271:                // Implementing ValueModel --------------------------------------------
1272:
1273:                /**
1274:                 * Returns the value of the adapted bean property, or null
1275:                 * if the bean is null.
1276:                 *
1277:                 * @return the value of the adapted bean property,
1278:                 *    null if the bean is null
1279:                 */
1280:                public Object getValue() {
1281:                    B bean = getBean();
1282:                    return bean == null ? null : getValue0(bean,
1283:                            getPropertyDescriptor(bean));
1284:                }
1285:
1286:                /**
1287:                 * Sets the given object as new value of the adapted bean property.
1288:                 * Does nothing if the bean is <code>null</code>. If the bean setter
1289:                 * throws a PropertyVetoException, it is silently ignored.
1290:                 * This write operation is supported only for writable bean properties.<p>
1291:                 *
1292:                 * Notifies any registered value listener if the bean reports
1293:                 * a property change. Note that a bean may suppress PropertyChangeEvents
1294:                 * if the old and new value are the same, or if the old and new value
1295:                 * are equal.
1296:                 *
1297:                 * @param newValue   the value to set
1298:                 *
1299:                 * @throws UnsupportedOperationException if the property is read-only
1300:                 * @throws PropertyNotFoundException     if the property could not be found
1301:                 * @throws PropertyAccessException       if the new value could not be set
1302:                 */
1303:                public void setValue(Object newValue) {
1304:                    B bean = getBean();
1305:                    if (bean == null)
1306:                        return;
1307:                    try {
1308:                        setValue0(bean, getPropertyDescriptor(bean), newValue);
1309:                    } catch (PropertyVetoException e) {
1310:                        // Silently ignore that someone vetoed against this change
1311:                    }
1312:                }
1313:
1314:                /**
1315:                 * Sets the given object as new value of the adapted bean property.
1316:                 * Does nothing if the bean is <code>null</code>. If the bean setter
1317:                 * throws a PropertyVetoExeption, this method throws the same exception.
1318:                 * This write operation is supported only for writable bean properties.<p>
1319:                 *
1320:                 * Notifies any registered value listener if the bean reports
1321:                 * a property change. Note that a bean may suppress PropertyChangeEvents
1322:                 * if the old and new value are the same, or if the old and new value
1323:                 * are equal.
1324:                 *
1325:                 * @param newValue   the value to set
1326:                 *
1327:                 * @throws UnsupportedOperationException if the property is read-only
1328:                 * @throws PropertyNotFoundException     if the property could not be found
1329:                 * @throws PropertyAccessException       if the new value could not be set
1330:                 * @throws PropertyVetoException         if the invoked bean setter
1331:                 *     throws a PropertyVetoException
1332:                 *
1333:                 * @since 1.1
1334:                 */
1335:                public void setVetoableValue(Object newValue)
1336:                        throws PropertyVetoException {
1337:                    B bean = getBean();
1338:                    if (bean == null)
1339:                        return;
1340:                    setValue0(bean, getPropertyDescriptor(bean), newValue);
1341:                }
1342:
1343:                // Accessing the Cached Property Descriptor --------------------------
1344:
1345:                /**
1346:                 * Looks up, lazily initializes and returns a <code>PropertyDescriptor</code>
1347:                 * for the given Java Bean and name of the adapted property.<p>
1348:                 *
1349:                 * The cached PropertyDescriptor is considered invalid if the
1350:                 * bean's class has changed. In this case we recompute the
1351:                 * PropertyDescriptor.<p>
1352:                 *
1353:                 * If a getter name or setter name is available, these are used
1354:                 * to directly create a PropertyDescriptor. Otherwise, the standard
1355:                 * Java Bean introspection is used to determine the property descriptor.
1356:                 *
1357:                 * @param bean          the bean that holds the property
1358:                 * @return the <code>PropertyDescriptor</code>
1359:                 * @throws PropertyNotFoundException   if the property could not be found
1360:                 */
1361:                private PropertyDescriptor getPropertyDescriptor(B bean) {
1362:                    Class<?> beanClass = getBeanClass(bean);
1363:                    if ((cachedPropertyDescriptor == null)
1364:                            || (beanClass != cachedBeanClass)) {
1365:
1366:                        cachedPropertyDescriptor = BeanUtils
1367:                                .getPropertyDescriptor(beanClass, propertyName,
1368:                                        getterName, setterName);
1369:                        cachedBeanClass = beanClass;
1370:                    }
1371:                    return cachedPropertyDescriptor;
1372:                }
1373:
1374:                protected void fireChange(B currentBean) {
1375:                    Object newValue;
1376:                    if (currentBean == null) {
1377:                        newValue = null;
1378:                    } else {
1379:                        PropertyDescriptor propertyDescriptor = getPropertyDescriptor(currentBean);
1380:                        boolean isWriteOnly = null == propertyDescriptor
1381:                                .getReadMethod();
1382:                        newValue = isWriteOnly ? null : getValue0(currentBean,
1383:                                propertyDescriptor);
1384:                    }
1385:                    fireValueChange(null, newValue);
1386:                }
1387:
1388:                protected void setBean0(B oldBean, B newBean) {
1389:                    Object oldValue;
1390:                    Object newValue;
1391:                    if (oldBean == null) {
1392:                        oldValue = null;
1393:                    } else {
1394:                        PropertyDescriptor propertyDescriptor = getPropertyDescriptor(oldBean);
1395:                        boolean isWriteOnly = null == propertyDescriptor
1396:                                .getReadMethod();
1397:                        oldValue = isWriteOnly ? null : getValue0(oldBean,
1398:                                propertyDescriptor);
1399:                    }
1400:                    if (newBean == null) {
1401:                        newValue = null;
1402:                    } else {
1403:                        PropertyDescriptor propertyDescriptor = getPropertyDescriptor(newBean);
1404:                        boolean isWriteOnly = null == propertyDescriptor
1405:                                .getReadMethod();
1406:                        newValue = isWriteOnly ? null : getValue0(newBean,
1407:                                propertyDescriptor);
1408:                    }
1409:                    if (oldValue != null || newValue != null) {
1410:                        fireValueChange(oldValue, newValue, true);
1411:                    }
1412:                }
1413:
1414:            }
1415:
1416:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.