Source Code Cross Referenced for Bindings.java in  » Swing-Library » jgoodies-data-binding » com » jgoodies » binding » adapter » 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.adapter 
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.adapter;
0032:
0033:        import java.awt.Color;
0034:        import java.awt.Component;
0035:        import java.awt.KeyboardFocusManager;
0036:        import java.awt.event.FocusAdapter;
0037:        import java.awt.event.FocusEvent;
0038:        import java.beans.PropertyChangeEvent;
0039:        import java.beans.PropertyChangeListener;
0040:        import java.beans.PropertyChangeListenerProxy;
0041:        import java.beans.PropertyChangeSupport;
0042:        import java.lang.ref.ReferenceQueue;
0043:        import java.lang.ref.WeakReference;
0044:
0045:        import javax.swing.*;
0046:        import javax.swing.text.JTextComponent;
0047:
0048:        import com.jgoodies.binding.beans.PropertyConnector;
0049:        import com.jgoodies.binding.list.SelectionInList;
0050:        import com.jgoodies.binding.value.BufferedValueModel;
0051:        import com.jgoodies.binding.value.ComponentValueModel;
0052:        import com.jgoodies.binding.value.ValueModel;
0053:
0054:        /**
0055:         * Consists only of static methods that bind Swing components to ValueModels.
0056:         * This is one of two helper classes that assist you in establishing a binding:
0057:         * 1) this Bindings class binds components that have been created before;
0058:         * it wraps ValueModels with the adapters from package
0059:         * <code>com.jgoodies.binding.adapter</code>.
0060:         * 2) the BasicComponentFactory creates Swing components that are then
0061:         * bound using this Bindings class.<p>
0062:         *
0063:         * If you have an existing factory that vends Swing components,
0064:         * you can use this Bindings class to bind them to ValueModels.
0065:         * If you don't have such a factory, you can use the BasicComponentFactory
0066:         * or a custom subclass to create and bind Swing components.<p>
0067:         *
0068:         * The binding process for JCheckBox, JRadioButton, and other AbstractButtons
0069:         * retains the former enablement state. Before the new (adapting) model
0070:         * is set, the enablement is requested from the model, not the button.
0071:         * This enablement is set after the new model has been set.<p>
0072:         *
0073:         * TODO: Consider adding binding methods for JProgressBar,
0074:         * JSlider, JSpinner, and JTabbedPane.<p>
0075:         *
0076:         * TODO: Consider adding connection methods for pairs of bean properties.
0077:         * In addition to the PropertyConnector's {@code connect} method,
0078:         * this could add boolean operators such as: not, and, or, nor.
0079:         *
0080:         * @author  Karsten Lentzsch
0081:         * @version $Revision: 1.32 $
0082:         *
0083:         * @see com.jgoodies.binding.value.ValueModel
0084:         * @see BasicComponentFactory
0085:         */
0086:        public final class Bindings {
0087:
0088:            /**
0089:             * A JTextComponent client property key used to store and retrieve
0090:             * the BufferedValueModel associated with text components that
0091:             * commit on focus lost.
0092:             *
0093:             * @see #bind(JTextArea, ValueModel, boolean)
0094:             * @see #bind(JTextField, ValueModel, boolean)
0095:             * @see #flushImmediately()
0096:             * @see BufferedValueModel
0097:             */
0098:            private static final String COMMIT_ON_FOCUS_LOST_MODEL_KEY = "commitOnFocusListModel";
0099:
0100:            /**
0101:             * A JComponent client property key used to store
0102:             * and retrieve an associated ComponentValueModel.
0103:             *
0104:             * @see #addComponentPropertyHandler(JComponent, ValueModel)
0105:             * @see #removeComponentPropertyHandler(JComponent)
0106:             * @see ComponentValueModel
0107:             */
0108:            private static final String COMPONENT_VALUE_MODEL_KEY = "componentValueModel";
0109:
0110:            /**
0111:             * A JComponent client property key used to store
0112:             * and retrieve an associated ComponentPropertyHandler.
0113:             *
0114:             * @see #addComponentPropertyHandler(JComponent, ValueModel)
0115:             * @see #removeComponentPropertyHandler(JComponent)
0116:             */
0117:            private static final String COMPONENT_PROPERTY_HANDLER_KEY = "componentPropertyHandler";
0118:
0119:            /**
0120:             * Triggers a commit in the shared focus lost trigger
0121:             * if focus is lost permanently. Shared among all components
0122:             * that are configured to commit on focus lost.
0123:             *
0124:             * @see #createCommitOnFocusLostModel(ValueModel, Component)
0125:             */
0126:            static final FocusLostHandler FOCUS_LOST_HANDLER = new FocusLostHandler();
0127:
0128:            /**
0129:             * Holds a weak trigger that is shared by BufferedValueModels
0130:             * that commit on permanent focus change.
0131:             *
0132:             * @see #createCommitOnFocusLostModel(ValueModel, Component)
0133:             */
0134:            static final WeakTrigger FOCUS_LOST_TRIGGER = new WeakTrigger();
0135:
0136:            private Bindings() {
0137:                // Suppresses default constructor; prevents instantiation.
0138:            }
0139:
0140:            // Binding Methods ********************************************************
0141:
0142:            /**
0143:             * Binds a JCheckBox to the given ValueModel and retains the enablement
0144:             * state. The bound check box is selected if and only if the model's value
0145:             * equals <code>Boolean.TRUE</code>.<p>
0146:             *
0147:             * The value model is converted to the required interface
0148:             * ToggleButtonModel using a ToggleButtonAdapter.
0149:             *
0150:             * @param checkBox       the check box to be bound
0151:             * @param valueModel     the model that provides a Boolean value
0152:             *
0153:             * @throws NullPointerException if the checkBox or valueModel
0154:             *     is <code>null</code>
0155:             */
0156:            public static void bind(JCheckBox checkBox, ValueModel valueModel) {
0157:                boolean enabled = checkBox.getModel().isEnabled();
0158:                checkBox.setModel(new ToggleButtonAdapter(valueModel));
0159:                checkBox.setEnabled(enabled);
0160:
0161:                addComponentPropertyHandler(checkBox, valueModel);
0162:            }
0163:
0164:            /**
0165:             * Binds a JCheckBoxMenuItem to the given ValueModel and retains
0166:             * the enablement state. The bound menu item is selected if and only if
0167:             * the model's value equals <code>Boolean.TRUE</code>.<p>
0168:             *
0169:             * <strong>Note:</strong> For users of the JGoodies UIF (user interface
0170:             * framework) the recommended way to create and bind check box menu items
0171:             * is the class <code>com.jgoodies.uif.ToggleAction</code>.<p>
0172:             *
0173:             * The value model is converted to the required interface
0174:             * <code>ToggleButtonModel</code> using a <code>ToggleButtonAdapter</code>.
0175:             *
0176:             * @param checkBoxMenuItem  the check box menu item to be bound
0177:             * @param valueModel        the model that provides a Boolean value
0178:             *
0179:             * @throws NullPointerException if the menu item or valueModel
0180:             *     is <code>null</code>
0181:             */
0182:            public static void bind(JCheckBoxMenuItem checkBoxMenuItem,
0183:                    ValueModel valueModel) {
0184:                boolean enabled = checkBoxMenuItem.getModel().isEnabled();
0185:                checkBoxMenuItem.setModel(new ToggleButtonAdapter(valueModel));
0186:                checkBoxMenuItem.setEnabled(enabled);
0187:
0188:                addComponentPropertyHandler(checkBoxMenuItem, valueModel);
0189:            }
0190:
0191:            /**
0192:             * Binds a JColorChooser to the given Color-typed ValueModel.
0193:             * The ValueModel must be of type Color and must
0194:             * allow read-access to its value.<p>
0195:             *
0196:             * Also, it is strongly recommended (though not required) that
0197:             * the underlying ValueModel provides only non-null values.
0198:             * This is so because the ColorSelectionModel behavior is undefined
0199:             * for <code>null</code> values and it may have unpredictable results.
0200:             * To avoid these problems, you may bind the ColorChooser with
0201:             * a default color using {@link #bind(JColorChooser, ValueModel, Color)}.<p>
0202:             *
0203:             * Since the color chooser is considered a container, not a single
0204:             * component, it is not synchronized with the valueModel's
0205:             * {@link ComponentValueModel} properties - if any.<p>
0206:             *
0207:             * <strong>Note:</strong> There's a bug in Java 1.4.2, Java 5 and Java 6
0208:             * that affects this binding. The BasicColorChooserUI doesn't listen
0209:             * to changes in the selection model, and so the preview panel won't
0210:             * update if the selected color changes. As a workaround you can use
0211:             * {@link BasicComponentFactory#createColorChooser(ValueModel)},
0212:             * or you could use a Look&amp;Feel that fixes the bug mentioned above.
0213:             *
0214:             * @param colorChooser  the color chooser to be bound
0215:             * @param valueModel    the model that provides non-<code>null</code>
0216:             *     Color values.
0217:             *
0218:             * @throws NullPointerException if the color chooser or value model
0219:             *     is <code>null</code>
0220:             *
0221:             * @see #bind(JColorChooser, ValueModel, Color)
0222:             *
0223:             * @since 1.0.3
0224:             */
0225:            public static void bind(JColorChooser colorChooser,
0226:                    ValueModel valueModel) {
0227:                colorChooser.setSelectionModel(new ColorSelectionAdapter(
0228:                        valueModel));
0229:            }
0230:
0231:            /**
0232:             * Binds a JColorChooser to the given Color-typed ValueModel.
0233:             * The ValueModel must be of type Color and must allow read-access
0234:             * to its value. The default color will be used if the valueModel
0235:             * returns <code>null</code>.<p>
0236:             *
0237:             * Since the color chooser is considered a container, not a single
0238:             * component, it is not synchronized with the valueModel's
0239:             * {@link ComponentValueModel} properties - if any.<p>
0240:             *
0241:             * <strong>Note:</strong> There's a bug in Java 1.4.2, Java 5 and Java 6
0242:             * that affects this binding. The BasicColorChooserUI doesn't listen
0243:             * to changes in the selection model, and so the preview panel won't
0244:             * update if the selected color changes. As a workaround you can use
0245:             * {@link BasicComponentFactory#createColorChooser(ValueModel)},
0246:             * or you could use a Look&amp;Feel that fixes the bug mentioned above.
0247:             *
0248:             * @param colorChooser  the color chooser to be bound
0249:             * @param valueModel    the model that provides non-<code>null</code>
0250:             *     Color values.
0251:             * @param defaultColor  the color used if the valueModel returns null
0252:             *
0253:             * @throws NullPointerException if the color chooser, value model,
0254:             *     or default color is <code>null</code>
0255:             *
0256:             * @since 1.1
0257:             */
0258:            public static void bind(JColorChooser colorChooser,
0259:                    ValueModel valueModel, Color defaultColor) {
0260:                if (defaultColor == null)
0261:                    throw new NullPointerException(
0262:                            "The default color must not be null.");
0263:
0264:                colorChooser.setSelectionModel(new ColorSelectionAdapter(
0265:                        valueModel, defaultColor));
0266:            }
0267:
0268:            /**
0269:             * Binds a non-editable JComboBox to the given SelectionInList using
0270:             * the SelectionInList's ListModel as list data provider and the
0271:             * SelectionInList's selection index holder for the combo box model's
0272:             * selected item.<p>
0273:             *
0274:             * There are a couple of other possibilities to bind a JComboBox.
0275:             * See the constructors and the class comment of the
0276:             * {@link ComboBoxAdapter}.<p>
0277:             *
0278:             * Since version 2.0 the combo's <em>enabled</em> and <em>visible</em>
0279:             * state is synchronized with the selectionInList's selection holder,
0280:             * if it's a {@link ComponentValueModel}.
0281:             *
0282:             * @param comboBox         the combo box to be bound
0283:             * @param selectionInList  provides the list and selection;
0284:             *     if the selection holder is a ComponentValueModel, it is synchronized
0285:             *     with the comboBox properties <em>visible</em> and <em>enabled</em>
0286:             * @param <E>              the type of the combo box items
0287:             *
0288:             * @throws NullPointerException  if the combo box or the selectionInList
0289:             *     is <code>null</code>
0290:             *
0291:             * @see ComboBoxAdapter
0292:             *
0293:             * @since 1.0.1
0294:             */
0295:            public static <E> void bind(JComboBox comboBox,
0296:                    SelectionInList<E> selectionInList) {
0297:                if (selectionInList == null)
0298:                    throw new NullPointerException(
0299:                            "The SelectionInList must not be null.");
0300:
0301:                comboBox.setModel(new ComboBoxAdapter<E>(selectionInList));
0302:
0303:                addComponentPropertyHandler(comboBox, selectionInList
0304:                        .getSelectionHolder());
0305:            }
0306:
0307:            /**
0308:             * Binds the given JFormattedTextField to the specified ValueModel.
0309:             * Synchronized the ValueModel's value with the text field's value
0310:             * by means of a PropertyConnector.
0311:             *
0312:             * @param textField   the JFormattedTextField that is to be bound
0313:             * @param valueModel  the model that provides the value
0314:             *
0315:             * @throws NullPointerException if the text field or valueModel
0316:             *     is <code>null</code>
0317:             */
0318:            public static void bind(JFormattedTextField textField,
0319:                    ValueModel valueModel) {
0320:                bind(textField, "value", valueModel);
0321:            }
0322:
0323:            /**
0324:             * Binds the given JLabel to the specified ValueModel.
0325:             *
0326:             * @param label       a label that shall be bound to the given value model
0327:             * @param valueModel  the model that provides the value
0328:             *
0329:             * @throws NullPointerException if the label or valueModel is <code>null</code>
0330:             */
0331:            public static void bind(JLabel label, ValueModel valueModel) {
0332:                bind(label, "text", valueModel);
0333:            }
0334:
0335:            /**
0336:             * Binds a JList to the given SelectionInList using the SelectionInList's
0337:             * ListModel as list data provider and the SelectionInList's selection
0338:             * index holder for the selection model.<p>
0339:             *
0340:             * Since version 2.0 the list's <em>enabled</em> and <em>visible</em>
0341:             * state is synchronized with the selectionInList's selection holder,
0342:             * if it's a {@link ComponentValueModel}.
0343:             *
0344:             * @param list             the list to be bound
0345:             * @param selectionInList  provides the list and selection;
0346:             *     if the selection holder is a ComponentValueModel, it is synchronized
0347:             *     with the list properties <em>visible</em> and <em>enabled</em>
0348:             * @param <E>              the type of the list items
0349:             *
0350:             * @throws NullPointerException  if the list or the selectionInList
0351:             *     is <code>null</code>
0352:             */
0353:            public static <E> void bind(JList list,
0354:                    SelectionInList<E> selectionInList) {
0355:                if (selectionInList == null)
0356:                    throw new NullPointerException(
0357:                            "The SelectionInList must not be null.");
0358:
0359:                list.setModel(selectionInList);
0360:                list.setSelectionModel(new SingleListSelectionAdapter(
0361:                        selectionInList.getSelectionIndexHolder()));
0362:
0363:                addComponentPropertyHandler(list, selectionInList
0364:                        .getSelectionHolder());
0365:            }
0366:
0367:            /**
0368:             * Binds a JRadioButton to the given ValueModel and retains the enablement
0369:             * state. The bound radio button is selected if and only if the model's
0370:             * value equals the specified choice value.<p>
0371:             *
0372:             * The model is converted to the required interface
0373:             * ToggleButtonModel using a RadioButtonAdapter.
0374:             *
0375:             * @param radioButton  the radio button to be bound to the given model
0376:             * @param model        the model that provides the current choice
0377:             * @param choice       this button's value
0378:             *
0379:             * @throws NullPointerException if the valueModel is <code>null</code>
0380:             */
0381:            public static void bind(JRadioButton radioButton, ValueModel model,
0382:                    Object choice) {
0383:                boolean enabled = radioButton.getModel().isEnabled();
0384:                radioButton.setModel(new RadioButtonAdapter(model, choice));
0385:                radioButton.setEnabled(enabled);
0386:
0387:                addComponentPropertyHandler(radioButton, model);
0388:            }
0389:
0390:            /**
0391:             * Binds a JRadioButtonMenuItem to the given ValueModel and retains
0392:             * the enablement state. The bound menu item is selected if and only if
0393:             * the model's value equals the specified choice.<p>
0394:             *
0395:             * <strong>Note:</strong> For users of the JGoodies UIF (user interface
0396:             * framework) the recommended way to create and bind radio button menu items
0397:             * is the class <code>com.jgoodies.uif.ToggleAction</code>.<p>
0398:             *
0399:             * The model is converted to the required interface
0400:             * ToggleButtonModel using a RadioButtonAdapter.
0401:             *
0402:             * @param radioButtonMenuItem  the radio item to be bound to the given model
0403:             * @param model        the model that provides the current choice
0404:             * @param choice       this button's value
0405:             *
0406:             * @throws NullPointerException if the valueModel is <code>null</code>
0407:             */
0408:            public static void bind(JRadioButtonMenuItem radioButtonMenuItem,
0409:                    ValueModel model, Object choice) {
0410:                boolean enabled = radioButtonMenuItem.getModel().isEnabled();
0411:                radioButtonMenuItem.setModel(new RadioButtonAdapter(model,
0412:                        choice));
0413:                radioButtonMenuItem.setEnabled(enabled);
0414:
0415:                addComponentPropertyHandler(radioButtonMenuItem, model);
0416:            }
0417:
0418:            /**
0419:             * Binds a text area to the given ValueModel.
0420:             * The model is updated on every character typed.<p>
0421:             *
0422:             * TODO: Consider changing the semantics to commit on focus lost.
0423:             * This would be consistent with the text component vending factory methods
0424:             * in the BasicComponentFactory that have no boolean parameter.
0425:             *
0426:             * @param textArea           the text area to be bound to the value model
0427:             * @param valueModel         the model that provides the text value
0428:             *
0429:             * @throws NullPointerException if the text component or valueModel
0430:             *     is <code>null</code>
0431:             */
0432:            public static void bind(JTextArea textArea, ValueModel valueModel) {
0433:                bind(textArea, valueModel, false);
0434:            }
0435:
0436:            /**
0437:             * Binds a text area to the given ValueModel.
0438:             * The model can be updated on focus lost or on every character typed.
0439:             * The DocumentAdapter used in this binding doesn't filter newlines.
0440:             *
0441:             * @param textArea           the text area to be bound to the value model
0442:             * @param valueModel         the model that provides the text value
0443:             * @param commitOnFocusLost  true to commit text changes on focus lost,
0444:             *     false to commit text changes on every character typed
0445:             *
0446:             * @throws NullPointerException if the text component or valueModel
0447:             *     is <code>null</code>
0448:             */
0449:            public static void bind(JTextArea textArea, ValueModel valueModel,
0450:                    boolean commitOnFocusLost) {
0451:                if (valueModel == null)
0452:                    throw new NullPointerException(
0453:                            "The value model must not be null.");
0454:
0455:                ValueModel textModel;
0456:                if (commitOnFocusLost) {
0457:                    textModel = createCommitOnFocusLostModel(valueModel,
0458:                            textArea);
0459:                    textArea.putClientProperty(COMMIT_ON_FOCUS_LOST_MODEL_KEY,
0460:                            textModel);
0461:                } else {
0462:                    textModel = valueModel;
0463:                }
0464:                TextComponentConnector connector = new TextComponentConnector(
0465:                        textModel, textArea);
0466:                connector.updateTextComponent();
0467:                addComponentPropertyHandler(textArea, valueModel);
0468:            }
0469:
0470:            /**
0471:             * Bind a text fields or password field to the given ValueModel.
0472:             * The model is updated on every character typed.<p>
0473:             *
0474:             * <strong>Security Note: </strong> If you use this method to bind a
0475:             * JPasswordField, the field's password will be requested as Strings
0476:             * from the field and will be held as String by the given ValueModel.
0477:             * These password String could potentially be observed in a security fraud.
0478:             * For stronger security it is recommended to request a character array
0479:             * from the JPasswordField and clear the array after use by setting
0480:             * each character to zero. Method {@link JPasswordField#getPassword()}
0481:             * return's the field's password as a character array.<p>
0482:             *
0483:             * TODO: Consider changing the semantics to commit on focus lost.
0484:             * This would be consistent with the text component vending factory methods
0485:             * in the BasicComponentFactory that have no boolean parameter.
0486:             *
0487:             * @param textField          the text field to be bound to the value model
0488:             * @param valueModel         the model that provides the text value
0489:             *
0490:             * @throws NullPointerException if the text component or valueModel
0491:             *     is <code>null</code>
0492:             *
0493:             * @see JPasswordField#getPassword()
0494:             */
0495:            public static void bind(JTextField textField, ValueModel valueModel) {
0496:                bind(textField, valueModel, false);
0497:            }
0498:
0499:            /**
0500:             * Binds a text field or password field to the given ValueModel.
0501:             * The model can be updated on focus lost or on every character typed.<p>
0502:             *
0503:             * <strong>Security Note: </strong> If you use this method to bind a
0504:             * JPasswordField, the field's password will be requested as Strings
0505:             * from the field and will be held as String by the given ValueModel.
0506:             * These password String could potentially be observed in a security fraud.
0507:             * For stronger security it is recommended to request a character array
0508:             * from the JPasswordField and clear the array after use by setting
0509:             * each character to zero. Method {@link JPasswordField#getPassword()}
0510:             * return's the field's password as a character array.
0511:             *
0512:             * @param textField          the text field to be bound to the value model
0513:             * @param valueModel         the model that provides the text value
0514:             * @param commitOnFocusLost  true to commit text changes on focus lost,
0515:             *     false to commit text changes on every character typed
0516:             *
0517:             * @throws NullPointerException if the text component or valueModel
0518:             *     is <code>null</code>
0519:             *
0520:             * @see JPasswordField#getPassword()
0521:             */
0522:            public static void bind(JTextField textField,
0523:                    ValueModel valueModel, boolean commitOnFocusLost) {
0524:                if (valueModel == null)
0525:                    throw new NullPointerException(
0526:                            "The value model must not be null.");
0527:
0528:                ValueModel textModel;
0529:                if (commitOnFocusLost) {
0530:                    textModel = createCommitOnFocusLostModel(valueModel,
0531:                            textField);
0532:                    textField.putClientProperty(COMMIT_ON_FOCUS_LOST_MODEL_KEY,
0533:                            textModel);
0534:                } else {
0535:                    textModel = valueModel;
0536:                }
0537:                TextComponentConnector connector = new TextComponentConnector(
0538:                        textModel, textField);
0539:                connector.updateTextComponent();
0540:                addComponentPropertyHandler(textField, valueModel);
0541:            }
0542:
0543:            /**
0544:             * Binds the specified property of the given JComponent to the specified
0545:             * ValueModel. Synchronizes the ValueModel's value with the component's
0546:             * property by means of a PropertyConnector.
0547:             *
0548:             * @param component    the JComponent that is to be bound
0549:             * @param propertyName the name of the property to be bound
0550:             * @param valueModel   the model that provides the value
0551:             *
0552:             * @throws NullPointerException if the component or value model
0553:             *     or property name is <code>null</code>
0554:             *
0555:             * @since 1.2
0556:             */
0557:            public static void bind(JComponent component, String propertyName,
0558:                    ValueModel valueModel) {
0559:                if (component == null)
0560:                    throw new NullPointerException(
0561:                            "The component must not be null.");
0562:                if (valueModel == null)
0563:                    throw new NullPointerException(
0564:                            "The value model must not be null.");
0565:                if (propertyName == null)
0566:                    throw new NullPointerException(
0567:                            "The property name must not be null.");
0568:
0569:                PropertyConnector.connectAndUpdate(valueModel, component,
0570:                        propertyName);
0571:
0572:                addComponentPropertyHandler(component, valueModel);
0573:            }
0574:
0575:            // Updating Component State on ComponentValueModel Changes ****************
0576:
0577:            /**
0578:             * If the given model is a ComponentValueModel, a component property handler
0579:             * is registered with this model. This handler updates the component state
0580:             * if the ComponentValueModel indicates a change in one of its properties,
0581:             * for example: <em>visible</em>, <em>enabled</em>, and <em>editable</em>.<p>
0582:             *
0583:             * Also the ComponentValueModel and the component handler are stored
0584:             * as client properties with the component. This way they can be removed
0585:             * later using <code>#removeComponentPropertyHandler</code>.
0586:             *
0587:             * @param component   the component where the handler is registered
0588:             * @param valueModel  the model to observe
0589:             *
0590:             * @see #removeComponentPropertyHandler(JComponent)
0591:             * @see ComponentValueModel
0592:             *
0593:             * @since 1.1
0594:             */
0595:            public static void addComponentPropertyHandler(
0596:                    JComponent component, ValueModel valueModel) {
0597:                if (!(valueModel instanceof  ComponentValueModel)) {
0598:                    return;
0599:                }
0600:                ComponentValueModel cvm = (ComponentValueModel) valueModel;
0601:                PropertyChangeListener componentHandler = new ComponentPropertyHandler(
0602:                        component);
0603:                cvm.addPropertyChangeListener(componentHandler);
0604:                component.putClientProperty(COMPONENT_VALUE_MODEL_KEY, cvm);
0605:                component.putClientProperty(COMPONENT_PROPERTY_HANDLER_KEY,
0606:                        componentHandler);
0607:
0608:                component.setEnabled(cvm.isEnabled());
0609:                component.setVisible(cvm.isVisible());
0610:                if (component instanceof  JTextComponent) {
0611:                    ((JTextComponent) component).setEditable(cvm.isEditable());
0612:                }
0613:            }
0614:
0615:            /**
0616:             * If the given component holds a ComponentValueModel and
0617:             * a ComponentPropertyHandler in its client properties,
0618:             * the handler is removed as listener from the model,
0619:             * and the model and handler are removed from the client properties.
0620:             *
0621:             * @param component
0622:             *
0623:             * @see #addComponentPropertyHandler(JComponent, ValueModel)
0624:             * @see ComponentValueModel
0625:             *
0626:             * @since 1.1
0627:             */
0628:            public static void removeComponentPropertyHandler(
0629:                    JComponent component) {
0630:                ComponentValueModel componentValueModel = (ComponentValueModel) component
0631:                        .getClientProperty(COMPONENT_VALUE_MODEL_KEY);
0632:                PropertyChangeListener componentHandler = (PropertyChangeListener) component
0633:                        .getClientProperty(COMPONENT_PROPERTY_HANDLER_KEY);
0634:                if ((componentValueModel != null) && (componentHandler != null)) {
0635:                    componentValueModel
0636:                            .removePropertyChangeListener(componentHandler);
0637:                    component
0638:                            .putClientProperty(COMPONENT_VALUE_MODEL_KEY, null);
0639:                    component.putClientProperty(COMPONENT_PROPERTY_HANDLER_KEY,
0640:                            null);
0641:                } else if ((componentValueModel == null)
0642:                        && (componentHandler == null)) {
0643:                    return;
0644:                } else if (componentValueModel != null) {
0645:                    throw new IllegalStateException(
0646:                            "The component has a ComponentValueModel stored, "
0647:                                    + "but lacks the ComponentPropertyHandler.");
0648:                } else {
0649:                    throw new IllegalStateException(
0650:                            "The component has a ComponentPropertyHandler stored, "
0651:                                    + "but lacks the ComponentValueModel.");
0652:                }
0653:            }
0654:
0655:            // Misc *******************************************************************
0656:
0657:            /**
0658:             * Commits a pending edit - if any. Useful to ensure that edited values
0659:             * in bound text components that commit on focus-lost are committed
0660:             * before an operation is performed that uses the value to be committed
0661:             * after a focus lost.<p>
0662:             *
0663:             * For example, before you save a form, a value that has been edited
0664:             * shall be committed, so the validation can check whether the save
0665:             * is allowed or not.
0666:             *
0667:             * @since 1.2
0668:             */
0669:            public static void commitImmediately() {
0670:                FOCUS_LOST_TRIGGER.triggerCommit();
0671:            }
0672:
0673:            /**
0674:             * Flushes a pending edit in the focused text component - if any.
0675:             * Useful to revert edited values in bound text components that
0676:             * commit on focus-lost. This operation can be performed on an escape
0677:             * key event like the Cancel action in the JFormattedTextField.<p>
0678:             *
0679:             * Returns whether an edit has been reset. Useful to decide whether
0680:             * a key event shall be consumed or not.
0681:             *
0682:             * @return {@code true} if a pending edit has been reset,
0683:             *     {@code false} if the focused component isn't buffering or
0684:             *     doesn't buffer at all
0685:             *
0686:             * @see #isFocusOwnerBuffering()
0687:             *
0688:             * @since 2.0.1
0689:             */
0690:            public static boolean flushImmediately() {
0691:                boolean buffering = isFocusOwnerBuffering();
0692:                if (buffering) {
0693:                    FOCUS_LOST_TRIGGER.triggerFlush();
0694:                }
0695:                return buffering;
0696:            }
0697:
0698:            /**
0699:             * Checks and answers whether the focus owner is a component
0700:             * that buffers a pending edit. Useful to enable or disable
0701:             * a text component Action that resets the edited value.<p>
0702:             *
0703:             * See also the JFormattedTextField's internal {@code CancelAction}.
0704:             *
0705:             * @return {@code true} if the focus owner is a JTextComponent
0706:             *     that commits on focus-lost and is buffering
0707:             *
0708:             * @see #flushImmediately()
0709:             *
0710:             * @since 2.0.1
0711:             */
0712:            public static boolean isFocusOwnerBuffering() {
0713:                Component focusOwner = KeyboardFocusManager
0714:                        .getCurrentKeyboardFocusManager().getFocusOwner();
0715:                if (!(focusOwner instanceof  JComponent)) {
0716:                    return false;
0717:                }
0718:                Object value = ((JComponent) focusOwner)
0719:                        .getClientProperty(COMMIT_ON_FOCUS_LOST_MODEL_KEY);
0720:                if (!(value instanceof  BufferedValueModel)) {
0721:                    return false;
0722:                }
0723:                BufferedValueModel commitOnFocusLostModel = (BufferedValueModel) value;
0724:                return commitOnFocusLostModel.isBuffering();
0725:            }
0726:
0727:            // Helper Code ************************************************************
0728:
0729:            /**
0730:             * Creates and returns a ValueModel that commits its value
0731:             * if the given component looses the focus permanently.
0732:             * It wraps the underlying ValueModel with a BufferedValueModel
0733:             * and delays the value commit until this class' shared FOCUS_LOST_TRIGGER
0734:             * commits. This happens, because this class' shared FOCUS_LOST_HANDLER
0735:             * is registered with the specified component.
0736:             *
0737:             * @param valueModel  the model that provides the value
0738:             * @param component   the component that looses the focus
0739:             * @return a buffering ValueModel that commits on focus lost
0740:             *
0741:             * @throws NullPointerException if the value model is <code>null</code>
0742:             */
0743:            private static ValueModel createCommitOnFocusLostModel(
0744:                    ValueModel valueModel, Component component) {
0745:                if (valueModel == null)
0746:                    throw new NullPointerException(
0747:                            "The value model must not be null.");
0748:
0749:                ValueModel model = new BufferedValueModel(valueModel,
0750:                        FOCUS_LOST_TRIGGER);
0751:                component.addFocusListener(FOCUS_LOST_HANDLER);
0752:                return model;
0753:            }
0754:
0755:            // Helper Classes *********************************************************
0756:
0757:            /**
0758:             * Triggers a commit event on permanent focus lost.
0759:             */
0760:            private static final class FocusLostHandler extends FocusAdapter {
0761:
0762:                /**
0763:                 * Triggers a commit event if the focus lost is permanent.
0764:                 *
0765:                 * @param evt   the focus lost event
0766:                 */
0767:                @Override
0768:                public void focusLost(FocusEvent evt) {
0769:                    if (!evt.isTemporary()) {
0770:                        FOCUS_LOST_TRIGGER.triggerCommit();
0771:                    }
0772:                }
0773:            }
0774:
0775:            /**
0776:             * Listens to property changes in a ComponentValueModel and
0777:             * updates the associated component state.
0778:             *
0779:             * @see ComponentValueModel
0780:             */
0781:            private static final class ComponentPropertyHandler implements 
0782:                    PropertyChangeListener {
0783:
0784:                private final JComponent component;
0785:
0786:                private ComponentPropertyHandler(JComponent component) {
0787:                    this .component = component;
0788:                }
0789:
0790:                public void propertyChange(PropertyChangeEvent evt) {
0791:                    String propertyName = evt.getPropertyName();
0792:                    ComponentValueModel model = (ComponentValueModel) evt
0793:                            .getSource();
0794:                    if (ComponentValueModel.PROPERTYNAME_ENABLED
0795:                            .equals(propertyName)) {
0796:                        component.setEnabled(model.isEnabled());
0797:                    } else if (ComponentValueModel.PROPERTYNAME_VISIBLE
0798:                            .equals(propertyName)) {
0799:                        component.setVisible(model.isVisible());
0800:                    } else if (ComponentValueModel.PROPERTYNAME_EDITABLE
0801:                            .equals(propertyName)) {
0802:                        if (component instanceof  JTextComponent) {
0803:                            ((JTextComponent) component).setEditable(model
0804:                                    .isEditable());
0805:                        }
0806:                    }
0807:                }
0808:            }
0809:
0810:            // Helper Code for a Weak Trigger *****************************************
0811:
0812:            /**
0813:             * Unlike the Trigger class, this implementation uses WeakReferences
0814:             * to store value change listeners.
0815:             */
0816:            private static final class WeakTrigger implements  ValueModel {
0817:
0818:                private final transient WeakPropertyChangeSupport changeSupport;
0819:
0820:                private Boolean value;
0821:
0822:                // Instance Creation ******************************************************
0823:
0824:                /**
0825:                 * Constructs a WeakTrigger set to neutral.
0826:                 */
0827:                WeakTrigger() {
0828:                    value = null;
0829:                    changeSupport = new WeakPropertyChangeSupport(this );
0830:                }
0831:
0832:                // ValueModel Implementation **********************************************
0833:
0834:                /**
0835:                 * Returns a Boolean that indicates the current trigger state.
0836:                 *
0837:                 * @return a Boolean that indicates the current trigger state
0838:                 */
0839:                public Object getValue() {
0840:                    return value;
0841:                }
0842:
0843:                /**
0844:                 * Sets a new Boolean value and rejects all non-Boolean values.
0845:                 * Fires no change event if the new value is equal to the
0846:                 * previously set value.<p>
0847:                 *
0848:                 * This method is not intended to be used by API users.
0849:                 * Instead you should trigger commit and flush events by invoking
0850:                 * <code>#triggerCommit</code> or <code>#triggerFlush</code>.
0851:                 *
0852:                 * @param newValue  the Boolean value to be set
0853:                 * @throws IllegalArgumentException   if the newValue is not a Boolean
0854:                 */
0855:                public void setValue(Object newValue) {
0856:                    if ((newValue != null) && !(newValue instanceof  Boolean))
0857:                        throw new IllegalArgumentException(
0858:                                "Trigger values must be of type Boolean.");
0859:
0860:                    Object oldValue = value;
0861:                    value = (Boolean) newValue;
0862:                    fireValueChange(oldValue, newValue);
0863:                }
0864:
0865:                // Change Management ****************************************************
0866:
0867:                /**
0868:                 * Registers the given PropertyChangeListener with this model.
0869:                 * The listener will be notified if the value has changed.<p>
0870:                 *
0871:                 * The PropertyChangeEvents delivered to the listener have the name
0872:                 * set to "value". In other words, the listeners won't get notified
0873:                 * when a PropertyChangeEvent is fired that has a null object as
0874:                 * the name to indicate an arbitrary set of the event source's
0875:                 * properties have changed.<p>
0876:                 *
0877:                 * In the rare case, where you want to notify a PropertyChangeListener
0878:                 * even with PropertyChangeEvents that have no property name set,
0879:                 * you can register the listener with #addPropertyChangeListener,
0880:                 * not #addValueChangeListener.
0881:                 *
0882:                 * @param listener the listener to add
0883:                 *
0884:                 * @see ValueModel
0885:                 */
0886:                public void addValueChangeListener(
0887:                        PropertyChangeListener listener) {
0888:                    if (listener == null) {
0889:                        return;
0890:                    }
0891:                    changeSupport.addPropertyChangeListener("value", listener);
0892:                }
0893:
0894:                /**
0895:                 * Removes the given PropertyChangeListener from the model.
0896:                 *
0897:                 * @param listener the listener to remove
0898:                 */
0899:                public void removeValueChangeListener(
0900:                        PropertyChangeListener listener) {
0901:                    if (listener == null) {
0902:                        return;
0903:                    }
0904:                    changeSupport.removePropertyChangeListener("value",
0905:                            listener);
0906:                }
0907:
0908:                /**
0909:                 * Notifies all listeners that have registered interest for
0910:                 * notification on this event type.  The event instance
0911:                 * is lazily created using the parameters passed into
0912:                 * the fire method.
0913:                 *
0914:                 * @param oldValue   the value before the change
0915:                 * @param newValue   the value after the change
0916:                 *
0917:                 * @see java.beans.PropertyChangeSupport
0918:                 */
0919:                private void fireValueChange(Object oldValue, Object newValue) {
0920:                    changeSupport.firePropertyChange("value", oldValue,
0921:                            newValue);
0922:                }
0923:
0924:                // Triggering *************************************************************
0925:
0926:                /**
0927:                 * Triggers a commit event in models that share this Trigger.
0928:                 * Sets the value to {@code Boolean.TRUE} and ensures that listeners
0929:                 * are notified about a value change to this new value. If necessary
0930:                 * the value is temporarily set to {@code null}. This way it minimizes
0931:                 * the number of PropertyChangeEvents fired by this Trigger.
0932:                 */
0933:                void triggerCommit() {
0934:                    if (Boolean.TRUE.equals(getValue())) {
0935:                        setValue(null);
0936:                    }
0937:                    setValue(Boolean.TRUE);
0938:                }
0939:
0940:                /**
0941:                 * Triggers a flush event in models that share this Trigger.
0942:                 * Sets the value to {@code Boolean.FALSE} and ensures that listeners
0943:                 * are notified about a value change to this new value. If necessary
0944:                 * the value is temporarily set to {@code null}. This way it minimizes
0945:                 * the number of PropertyChangeEvents fired by this Trigger.
0946:                 */
0947:                void triggerFlush() {
0948:                    if (Boolean.FALSE.equals(getValue())) {
0949:                        setValue(null);
0950:                    }
0951:                    setValue(Boolean.FALSE);
0952:                }
0953:
0954:            }
0955:
0956:            /**
0957:             * Differs from its superclass {@link PropertyChangeSupport} in that it
0958:             * uses WeakReferences for registering listeners. It wraps registered
0959:             * PropertyChangeListeners with instances of WeakPropertyChangeListener
0960:             * and cleans up a list of stale references when firing an event.<p>
0961:             *
0962:             * TODO: Merge this WeakPropertyChangeSupport with the
0963:             * ExtendedPropertyChangeSupport.
0964:             */
0965:            private static final class WeakPropertyChangeSupport extends
0966:                    PropertyChangeSupport {
0967:
0968:                // Instance Creation ******************************************************
0969:
0970:                /**
0971:                 * Constructs a  WeakPropertyChangeSupport object.
0972:                 *
0973:                 * @param sourceBean  The bean to be given as the source for any events.
0974:                 */
0975:                WeakPropertyChangeSupport(Object sourceBean) {
0976:                    super (sourceBean);
0977:                }
0978:
0979:                // Managing Property Change Listeners **********************************
0980:
0981:                /** {@inheritDoc} */
0982:                @Override
0983:                public synchronized void addPropertyChangeListener(
0984:                        PropertyChangeListener listener) {
0985:                    if (listener == null)
0986:                        return;
0987:                    if (listener instanceof  PropertyChangeListenerProxy) {
0988:                        PropertyChangeListenerProxy proxy = (PropertyChangeListenerProxy) listener;
0989:                        // Call two argument add method.
0990:                        addPropertyChangeListener(proxy.getPropertyName(),
0991:                                (PropertyChangeListener) proxy.getListener());
0992:                    } else {
0993:                        super 
0994:                                .addPropertyChangeListener(new WeakPropertyChangeListener(
0995:                                        listener));
0996:                    }
0997:                }
0998:
0999:                /** {@inheritDoc} */
1000:                @Override
1001:                public synchronized void addPropertyChangeListener(
1002:                        String propertyName, PropertyChangeListener listener) {
1003:                    if (listener == null)
1004:                        return;
1005:                    super .addPropertyChangeListener(propertyName,
1006:                            new WeakPropertyChangeListener(propertyName,
1007:                                    listener));
1008:                }
1009:
1010:                /** {@inheritDoc} */
1011:                @Override
1012:                public synchronized void removePropertyChangeListener(
1013:                        PropertyChangeListener listener) {
1014:                    if (listener == null)
1015:                        return;
1016:                    if (listener instanceof  PropertyChangeListenerProxy) {
1017:                        PropertyChangeListenerProxy proxy = (PropertyChangeListenerProxy) listener;
1018:                        // Call two argument remove method.
1019:                        removePropertyChangeListener(proxy.getPropertyName(),
1020:                                (PropertyChangeListener) proxy.getListener());
1021:                        return;
1022:                    }
1023:                    PropertyChangeListener[] listeners = getPropertyChangeListeners();
1024:                    WeakPropertyChangeListener wpcl;
1025:                    for (int i = listeners.length - 1; i >= 0; i--) {
1026:                        if (listeners[i] instanceof  PropertyChangeListenerProxy)
1027:                            continue;
1028:                        wpcl = (WeakPropertyChangeListener) listeners[i];
1029:                        if (wpcl.get() == listener) {
1030:                            // TODO: Should we call here the #clear() method of wpcl???
1031:                            super .removePropertyChangeListener(wpcl);
1032:                            break;
1033:                        }
1034:                    }
1035:                }
1036:
1037:                /** {@inheritDoc} */
1038:                @Override
1039:                public synchronized void removePropertyChangeListener(
1040:                        String propertyName, PropertyChangeListener listener) {
1041:                    if (listener == null)
1042:                        return;
1043:                    PropertyChangeListener[] listeners = getPropertyChangeListeners(propertyName);
1044:                    WeakPropertyChangeListener wpcl;
1045:                    for (int i = listeners.length - 1; i >= 0; i--) {
1046:                        wpcl = (WeakPropertyChangeListener) listeners[i];
1047:                        if (wpcl.get() == listener) {
1048:                            // TODO: Should we call here the #clear() method of wpcl???
1049:                            super .removePropertyChangeListener(propertyName,
1050:                                    wpcl);
1051:                            break;
1052:                        }
1053:                    }
1054:                }
1055:
1056:                // Firing Events **********************************************************
1057:
1058:                /**
1059:                 * Fires the specified PropertyChangeEvent to any registered listeners.
1060:                 *
1061:                 * @param evt  The PropertyChangeEvent object.
1062:                 *
1063:                 * @see PropertyChangeSupport#firePropertyChange(PropertyChangeEvent)
1064:                 */
1065:                @Override
1066:                public void firePropertyChange(PropertyChangeEvent evt) {
1067:                    cleanUp();
1068:                    super .firePropertyChange(evt);
1069:                }
1070:
1071:                /**
1072:                 * Reports a bound property update to any registered listeners.
1073:                 *
1074:                 * @param propertyName  The programmatic name of the property
1075:                 *      that was changed.
1076:                 * @param oldValue  The old value of the property.
1077:                 * @param newValue  The new value of the property.
1078:                 *
1079:                 * @see PropertyChangeSupport#firePropertyChange(String, Object, Object)
1080:                 */
1081:                @Override
1082:                public void firePropertyChange(String propertyName,
1083:                        Object oldValue, Object newValue) {
1084:                    cleanUp();
1085:                    super .firePropertyChange(propertyName, oldValue, newValue);
1086:                }
1087:
1088:                static final ReferenceQueue<PropertyChangeListener> QUEUE = new ReferenceQueue<PropertyChangeListener>();
1089:
1090:                private static void cleanUp() {
1091:                    WeakPropertyChangeListener wpcl;
1092:                    while ((wpcl = (WeakPropertyChangeListener) QUEUE.poll()) != null) {
1093:                        wpcl.removeListener();
1094:                    }
1095:                }
1096:
1097:                void removeWeakPropertyChangeListener(
1098:                        WeakPropertyChangeListener l) {
1099:                    if (l.propertyName == null) {
1100:                        super .removePropertyChangeListener(l);
1101:                    } else {
1102:                        super .removePropertyChangeListener(l.propertyName, l);
1103:                    }
1104:                }
1105:
1106:                /**
1107:                 * Wraps a PropertyChangeListener to make it weak.
1108:                 */
1109:                private final class WeakPropertyChangeListener extends
1110:                        WeakReference<PropertyChangeListener> implements 
1111:                        PropertyChangeListener {
1112:
1113:                    final String propertyName;
1114:
1115:                    private WeakPropertyChangeListener(
1116:                            PropertyChangeListener delegate) {
1117:                        this (null, delegate);
1118:                    }
1119:
1120:                    private WeakPropertyChangeListener(String propertyName,
1121:                            PropertyChangeListener delegate) {
1122:                        super (delegate, QUEUE);
1123:                        this .propertyName = propertyName;
1124:                    }
1125:
1126:                    /** {@inheritDoc} */
1127:                    public void propertyChange(PropertyChangeEvent evt) {
1128:                        PropertyChangeListener delegate = get();
1129:                        if (delegate != null) {
1130:                            delegate.propertyChange(evt);
1131:                        }
1132:                    }
1133:
1134:                    void removeListener() {
1135:                        removeWeakPropertyChangeListener(this);
1136:                    }
1137:                }
1138:            }
1139:
1140:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.