Source Code Cross Referenced for SelectionInList.java in  » Swing-Library » jgoodies-data-binding » com » jgoodies » binding » list » 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.list 
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.list;
0032:
0033:        import java.beans.PropertyChangeEvent;
0034:        import java.beans.PropertyChangeListener;
0035:        import java.util.Arrays;
0036:        import java.util.List;
0037:
0038:        import javax.swing.ListModel;
0039:        import javax.swing.event.ListDataEvent;
0040:        import javax.swing.event.ListDataListener;
0041:
0042:        import com.jgoodies.binding.PresentationModel;
0043:        import com.jgoodies.binding.beans.BeanAdapter;
0044:        import com.jgoodies.binding.value.ValueHolder;
0045:        import com.jgoodies.binding.value.ValueModel;
0046:
0047:        /**
0048:         * Represents a selection in a list of objects. Provides bound bean properties
0049:         * for the list, the selection, the selection index, and the selection empty
0050:         * state. The SelectionInList implements ValueModel with the selection as value.
0051:         * Selection changes fire an event only if the old and new value are not equal.
0052:         * If you need to compare the identity you can use and observe the selection
0053:         * index instead of the selection or value.<p>
0054:         *
0055:         * The SelectionInList uses three ValueModels to hold the list, the selection
0056:         * and selection index and provides bound bean properties for these models.
0057:         * You can access, observe and replace these ValueModels. This is useful
0058:         * to connect a SelectionInList with other ValueModels; for example you can
0059:         * use the SelectionInList's selection holder as bean channel for a
0060:         * PresentationModel. Since the SelectionInList is a ValueModel, it is often
0061:         * used as bean channel. See the Binding tutorial classes for examples on how
0062:         * to connect a SelectionInList with a PresentationModel.<p>
0063:         *
0064:         * This class also implements the {@link ListModel} interface that allows
0065:         * API users to observe fine grained changes in the structure and contents
0066:         * of the list. Hence instances of this class can be used directly as model of
0067:         * a JList. If you want to use a SelectionInList with a JComboBox or JTable,
0068:         * you can convert the SelectionInList to the associated component model
0069:         * interfaces using the adapter classes
0070:         * {@link com.jgoodies.binding.adapter.ComboBoxAdapter}
0071:         * and {@link com.jgoodies.binding.adapter.AbstractTableAdapter} respectively.
0072:         * These classes are part of the Binding library too.<p>
0073:         *
0074:         * The SelectionInList supports two list types as content of its list holder:
0075:         * <code>List</code> and <code>ListModel</code>. The two modes differ in how
0076:         * precise this class can fire events about changes to the content and structure
0077:         * of the list. If you use a List, this class can only report
0078:         * that the list changes completely; this is done by firing
0079:         * a PropertyChangeEvent for the <em>list</em> property.
0080:         * Also, a <code>ListDataEvent</code> is fired that reports a complete change.
0081:         * In contrast, if you use a ListModel it will report the same
0082:         * PropertyChangeEvent. But fine grained changes in the list
0083:         * model will be fired by this class to notify observes about changes in
0084:         * the content, added and removed elements.<p>
0085:         *
0086:         * If the list content doesn't change at all, or if it always changes
0087:         * completely, you can work well with both List content and ListModel content.
0088:         * But if the list structure or content changes, the ListModel reports more
0089:         * fine grained events to registered ListDataListeners, which in turn allows
0090:         * list views to chooser better user interface gestures: for example, a table
0091:         * with scroll pane may retain the current selection and scroll offset.<p>
0092:         *
0093:         * An example for using a ListModel in a SelectionInList is the asynchronous
0094:         * transport of list elements from a server to a client. Let's say you transport
0095:         * the list elements in portions of 10 elements to improve the application's
0096:         * responsiveness. The user can then select and work with the SelectionInList
0097:         * as soon as the ListModel gets populated. If at a later time more elements
0098:         * are added to the list model, the SelectionInList can retain the selection
0099:         * index (and selection) and will just report a ListDataEvent about
0100:         * the interval added. JList, JTable and JComboBox will then just add
0101:         * the new elements at the end of the list presentation.<p>
0102:         *
0103:         * If you want to combine List operations and the ListModel change reports,
0104:         * you may consider using an implementation that combines these two interfaces,
0105:         * for example {@link ArrayListModel} or {@link LinkedListModel}.<p>
0106:         *
0107:         * <strong>Important Note:</strong> If you change the ListModel instance,
0108:         * either by calling <code>#setListModel(ListModel)</code> or by setting
0109:         * a new value to the underlying list holder, you must ensure that
0110:         * the list holder throws a PropertyChangeEvent whenever the instance changes.
0111:         * This event is used to remove a ListDataListener from the old ListModel
0112:         * instance and is later used to add it to the new ListModel instance.
0113:         * It is easy to violate this constraint, just because Java's standard
0114:         * PropertyChangeSupport helper class that is used by many beans, checks
0115:         * a changed property value via <code>#equals</code>, not <code>==</code>.
0116:         * For example, if you change the SelectionInList's list model from an empty
0117:         * list <code>L1</code> to another empty list instance <code>L2</code>,
0118:         * the PropertyChangeSupport won't generate a PropertyChangeEvent,
0119:         * and so, the SelectionInList won't know about the change, which
0120:         * may lead to unexpected behavior.<p>
0121:         *
0122:         * This binding library provides some help for firing PropertyChangeEvents
0123:         * if the old ListModel and new ListModel are equal but not the same.
0124:         * Class {@link com.jgoodies.binding.beans.ExtendedPropertyChangeSupport}
0125:         * allows to permanently or individually check the identity (using
0126:         * <code>==</code>) instead of checking the equity (using <code>#equals</code>).
0127:         * Class {@link com.jgoodies.binding.beans.Model} uses this extended
0128:         * property change support. And class {@link ValueHolder} uses it too
0129:         * and can be configured to always test the identity.<p>
0130:         *
0131:         * Since version 1.0.2 this class provides public convenience methods
0132:         * for firing ListDataEvents, see the methods <code>#fireContentsChanged</code>,
0133:         * <code>#fireIntervalAdded</code>, and <code>#fireIntervalRemoved</code>.
0134:         * These are automatically invoked if the list holder holds a ListModel
0135:         * that fires these events. If on the other hand the underlying List or
0136:         * ListModel does not fire a required ListDataEvent, you can use these
0137:         * methods to notify presentations about a change. It is recommended
0138:         * to avoid sending duplicate ListDataEvents; hence check if the underlying
0139:         * ListModel fires the necessary events or not. Typically an underlying
0140:         * ListModel will fire the add and remove events; but often it'll lack
0141:         * an event if the (selected) contents has changed. A convenient way to
0142:         * indicate that change is <code>#fireSelectedContentsChanged</code>. See
0143:         * the tutorial's AlbumManagerModel for an example how to use this feature.<p>
0144:         *
0145:         * The SelectionInList is partially defined for Lists and ListModels
0146:         * that contain {@code null}. Setting the selection to {@code null}
0147:         * on a SelectionInList that contains {@code null} won't set the selection index
0148:         * to the index of the first {@code null} element. For details see the
0149:         * {@link #setSelection(Object)} JavaDocs. This is because the current
0150:         * implementation interprets a {@code null} selection as <em>unspecified</em>,
0151:         * which maps better to a cleared selection than to a concrete selection index.
0152:         * Anyway, as long as you work with the selection index and selection index
0153:         * holder, such a SelectionInList will work fine. This is the case if you bind
0154:         * a SelectionInList to a JList or JTable. Binding such a SelectionInList
0155:         * to a JComboBox won't synchronize the selection index if {@code null}
0156:         * is selected.<p>
0157:         *
0158:         * <strong>Constraints:</strong> The list holder holds instances of {@link List}
0159:         * or {@link ListModel}, the selection holder values of type {@code E}
0160:         * and the selection index holder of type {@code Integer}. The selection
0161:         * index holder must hold non-null index values; however, when firing
0162:         * an index value change event, both the old and new value may be null.
0163:         * If the ListModel changes, the underlying ValueModel must fire
0164:         * a PropertyChangeEvent.
0165:         *
0166:         * @author  Karsten Lentzsch
0167:         * @version $Revision: 1.36 $
0168:         *
0169:         * @see     ValueModel
0170:         * @see     List
0171:         * @see     ListModel
0172:         * @see     com.jgoodies.binding.adapter.ComboBoxAdapter
0173:         * @see     com.jgoodies.binding.adapter.AbstractTableAdapter
0174:         * @see     com.jgoodies.binding.beans.ExtendedPropertyChangeSupport
0175:         * @see     com.jgoodies.binding.beans.Model
0176:         * @see     com.jgoodies.binding.value.ValueHolder
0177:         *
0178:         * @param <E>  the type of the list elements and the selection
0179:         */
0180:        public final class SelectionInList<E> extends IndirectListModel<E>
0181:                implements  ValueModel {
0182:
0183:            // Constant Names for Bound Properties ************************************
0184:
0185:            /**
0186:             * The name of the bound read-write <em>selection</em> property.
0187:             */
0188:            public static final String PROPERTYNAME_SELECTION = "selection";
0189:
0190:            /**
0191:             * The name of the bound read-only <em>selectionEmpty</em> property.
0192:             */
0193:            public static final String PROPERTYNAME_SELECTION_EMPTY = "selectionEmpty";
0194:
0195:            /**
0196:             * The name of the bound read-write <em>selection holder</em> property.
0197:             */
0198:            public static final String PROPERTYNAME_SELECTION_HOLDER = "selectionHolder";
0199:
0200:            /**
0201:             * The name of the bound read-write <em>selectionIndex</em> property.
0202:             */
0203:            public static final String PROPERTYNAME_SELECTION_INDEX = "selectionIndex";
0204:
0205:            /**
0206:             * The name of the bound read-write <em>selection index holder</em> property.
0207:             */
0208:            public static final String PROPERTYNAME_SELECTION_INDEX_HOLDER = "selectionIndexHolder";
0209:
0210:            /**
0211:             * The name of the bound read-write <em>value</em> property.
0212:             */
0213:            public static final String PROPERTYNAME_VALUE = "value";
0214:
0215:            // ************************************************************************
0216:
0217:            /**
0218:             * A special index that indicates that we have no selection.
0219:             */
0220:            private static final int NO_SELECTION_INDEX = -1;
0221:
0222:            // Instance Fields ********************************************************
0223:
0224:            /**
0225:             * Holds the selection, an instance of <code>Object</code>.
0226:             */
0227:            private ValueModel selectionHolder;
0228:
0229:            /**
0230:             * Holds the selection index, an <code>Integer</code>.
0231:             */
0232:            private ValueModel selectionIndexHolder;
0233:
0234:            /**
0235:             * The <code>PropertyChangeListener</code> used to handle
0236:             * changes of the selection.
0237:             */
0238:            private final PropertyChangeListener selectionChangeHandler;
0239:
0240:            /**
0241:             * The <code>PropertyChangeListener</code> used to handle
0242:             * changes of the selection index.
0243:             */
0244:            private final PropertyChangeListener selectionIndexChangeHandler;
0245:
0246:            /**
0247:             * Duplicates the value of the selectionHolder.
0248:             * Used to provide better old values in PropertyChangeEvents
0249:             * fired after selectionIndex changes.
0250:             */
0251:            private E oldSelection;
0252:
0253:            /**
0254:             * Duplicates the value of the selectionIndexHolder.
0255:             * Used to provide better old values in PropertyChangeEvents
0256:             * fired after selectionIndex changes and selection changes.
0257:             */
0258:            private int oldSelectionIndex;
0259:
0260:            // Instance creation ****************************************************
0261:
0262:            /**
0263:             * Constructs a SelectionInList with an empty initial
0264:             * <code>ArrayListModel</code> using defaults for the selection holder
0265:             * and selection index holder.
0266:             */
0267:            public SelectionInList() {
0268:                this ((ListModel) new ArrayListModel<E>());
0269:            }
0270:
0271:            /**
0272:             * Constructs a SelectionInList on the given item array
0273:             * using defaults for the selection holder and selection index holder.
0274:             * The specified array will be converted to a List.<p>
0275:             *
0276:             * Changes to the list "write through" to the array, and changes
0277:             * to the array contents will be reflected in the list.
0278:             *
0279:             * @param listItems        the array of initial items
0280:             *
0281:             * @throws NullPointerException if <code>listItems</code> is <code>null</code>
0282:             */
0283:            public SelectionInList(E[] listItems) {
0284:                this (Arrays.asList(listItems));
0285:            }
0286:
0287:            /**
0288:             * Constructs a SelectionInList on the given item array and
0289:             * selection holder using a default selection index holder.
0290:             * The specified array will be converted to a List.<p>
0291:             *
0292:             * Changes to the list "write through" to the array, and changes
0293:             * to the array contents will be reflected in the list.
0294:             *
0295:             * @param listItems        the array of initial items
0296:             * @param selectionHolder  holds the selection
0297:             *
0298:             * @throws NullPointerException if <code>listItems</code> or
0299:             *     <code>selectionHolder</code> is <code>null</code>
0300:             */
0301:            public SelectionInList(E[] listItems, ValueModel selectionHolder) {
0302:                this (Arrays.asList(listItems), selectionHolder);
0303:            }
0304:
0305:            /**
0306:             * Constructs a SelectionInList on the given item array and
0307:             * selection holder using a default selection index holder.
0308:             * The specified array will be converted to a List.<p>
0309:             *
0310:             * Changes to the list "write through" to the array, and changes
0311:             * to the array contents will be reflected in the list.
0312:             *
0313:             * @param listItems        the array of initial items
0314:             * @param selectionHolder  holds the selection
0315:             * @param selectionIndexHolder  holds the selection index
0316:             *
0317:             * @throws NullPointerException if <code>listItems</code>,
0318:             *     <code>selectionHolder</code>, or <code>selectionIndexHolder</code>
0319:             *     is <code>null</code>
0320:             */
0321:            public SelectionInList(E[] listItems, ValueModel selectionHolder,
0322:                    ValueModel selectionIndexHolder) {
0323:                this (Arrays.asList(listItems), selectionHolder,
0324:                        selectionIndexHolder);
0325:            }
0326:
0327:            /**
0328:             * Constructs a SelectionInList on the given list
0329:             * using defaults for the selection holder and selection index holder.<p>
0330:             *
0331:             * <strong>Note:</strong> Favor <code>ListModel</code> over
0332:             * <code>List</code> when working with the SelectionInList.
0333:             * Why? The SelectionInList can work with both types. What's the
0334:             * difference? ListModel provides all list access features
0335:             * required by the SelectionInList's. In addition it reports more
0336:             * fine grained change events, instances of <code>ListDataEvents</code>.
0337:             * In contrast developer often create Lists and operate on them
0338:             * and the ListModel may be inconvenient for these operations.<p>
0339:             *
0340:             * A convenient solution for this situation is to use the
0341:             * <code>ArrayListModel</code> and <code>LinkedListModel</code> classes.
0342:             * These implement both List and ListModel, offer the standard List
0343:             * operations and report the fine grained ListDataEvents.
0344:             *
0345:             * @param list        the initial list
0346:             */
0347:            public SelectionInList(List<E> list) {
0348:                this (new ValueHolder(list, true));
0349:            }
0350:
0351:            /**
0352:             * Constructs a SelectionInList on the given list and
0353:             * selection holder using a default selection index holder.<p>
0354:             *
0355:             * <strong>Note:</strong> Favor <code>ListModel</code> over
0356:             * <code>List</code> when working with the SelectionInList.
0357:             * Why? The SelectionInList can work with both types. What's the
0358:             * difference? ListModel provides all list access features
0359:             * required by the SelectionInList's. In addition it reports more
0360:             * fine grained change events, instances of <code>ListDataEvents</code>.
0361:             * In contrast developer often create Lists and operate on them
0362:             * and the ListModel may be inconvenient for these operations.<p>
0363:             *
0364:             * A convenient solution for this situation is to use the
0365:             * <code>ArrayListModel</code> and <code>LinkedListModel</code> classes.
0366:             * These implement both List and ListModel, offer the standard List
0367:             * operations and report the fine grained ListDataEvents.
0368:             *
0369:             * @param list             the initial list
0370:             * @param selectionHolder  holds the selection
0371:             *
0372:             * @throws NullPointerException
0373:             *     if <code>selectionHolder</code> is <code>null</code>
0374:             */
0375:            public SelectionInList(List<E> list, ValueModel selectionHolder) {
0376:                this (new ValueHolder(list, true), selectionHolder);
0377:            }
0378:
0379:            /**
0380:             * Constructs a SelectionInList on the given list,
0381:             * selection holder, and selection index holder.<p>
0382:             *
0383:             * <strong>Note:</strong> Favor <code>ListModel</code> over
0384:             * <code>List</code> when working with the SelectionInList.
0385:             * Why? The SelectionInList can work with both types. What's the
0386:             * difference? ListModel provides all list access features
0387:             * required by the SelectionInList's. In addition it reports more
0388:             * fine grained change events, instances of <code>ListDataEvents</code>.
0389:             * In contrast developer often create Lists and operate on them
0390:             * and the ListModel may be inconvenient for these operations.<p>
0391:             *
0392:             * A convenient solution for this situation is to use the
0393:             * <code>ArrayListModel</code> and <code>LinkedListModel</code> classes.
0394:             * These implement both List and ListModel, offer the standard List
0395:             * operations and report the fine grained ListDataEvents.
0396:             *
0397:             * @param list                  the initial list
0398:             * @param selectionHolder       holds the selection
0399:             * @param selectionIndexHolder  holds the selection index
0400:             *
0401:             * @throws NullPointerException if <code>selectionHolder</code>,
0402:             *     or <code>selectionIndexHolder</code> is <code>null</code>
0403:             */
0404:            public SelectionInList(List<E> list, ValueModel selectionHolder,
0405:                    ValueModel selectionIndexHolder) {
0406:                this (new ValueHolder(list, true), selectionHolder,
0407:                        selectionIndexHolder);
0408:            }
0409:
0410:            /**
0411:             * Constructs a SelectionInList on the given list model
0412:             * using defaults for the selection holder and selection index holder.
0413:             *
0414:             * @param listModel        the initial list model
0415:             */
0416:            public SelectionInList(ListModel listModel) {
0417:                this (new ValueHolder(listModel, true));
0418:            }
0419:
0420:            /**
0421:             * Constructs a SelectionInList on the given list model
0422:             * and selection holder using a default selection index holder.
0423:             *
0424:             * @param listModel        the initial list model
0425:             * @param selectionHolder  holds the selection
0426:             *
0427:             * @throws NullPointerException
0428:             *     if <code>selectionHolder</code> is <code>null</code>
0429:             */
0430:            public SelectionInList(ListModel listModel,
0431:                    ValueModel selectionHolder) {
0432:                this (new ValueHolder(listModel, true), selectionHolder);
0433:            }
0434:
0435:            /**
0436:             * Constructs a SelectionInList on the given list model,
0437:             * selection holder, and selection index holder.
0438:             *
0439:             * @param listModel             the initial list model
0440:             * @param selectionHolder       holds the selection
0441:             * @param selectionIndexHolder  holds the selection index
0442:             *
0443:             * @throws NullPointerException if <code>selectionHolder</code>,
0444:             *     or <code>selectionIndexHolder</code> is <code>null</code>
0445:             */
0446:            public SelectionInList(ListModel listModel,
0447:                    ValueModel selectionHolder, ValueModel selectionIndexHolder) {
0448:                this (new ValueHolder(listModel, true), selectionHolder,
0449:                        selectionIndexHolder);
0450:            }
0451:
0452:            /**
0453:             * Constructs a SelectionInList on the given list holder
0454:             * using defaults for the selection holder and selection index holder.<p>
0455:             *
0456:             * <strong>Constraints:</strong>
0457:             * 1) The listHolder must hold instances of List or ListModel and
0458:             * 2) must report a value change whenever the value's identity changes.
0459:             * Note that many bean properties don't fire a PropertyChangeEvent
0460:             * if the old and new value are equal - and so would break this constraint.
0461:             * If you provide a ValueHolder, enable its identityCheck feature
0462:             * during construction. If you provide an adapted bean property from
0463:             * a bean that extends the JGoodies <code>Model</code> class,
0464:             * you can enable the identity check feature in the methods
0465:             * <code>#firePropertyChange</code> by setting the trailing boolean
0466:             * parameter to <code>true</code>.
0467:             *
0468:             * @param listHolder          holds the list or list model
0469:             *
0470:             * @throws NullPointerException
0471:             *     if <code>listHolder</code> is <code>null</code>
0472:             */
0473:            public SelectionInList(ValueModel listHolder) {
0474:                this (listHolder, new ValueHolder(null, true));
0475:            }
0476:
0477:            /**
0478:             * Constructs a SelectionInList on the given list holder,
0479:             * selection holder and selection index holder.<p>
0480:             *
0481:             * <strong>Constraints:</strong>
0482:             * 1) The listHolder must hold instances of List or ListModel and
0483:             * 2) must report a value change whenever the value's identity changes.
0484:             * Note that many bean properties don't fire a PropertyChangeEvent
0485:             * if the old and new value are equal - and so would break this constraint.
0486:             * If you provide a ValueHolder, enable its identityCheck feature
0487:             * during construction. If you provide an adapted bean property from
0488:             * a bean that extends the JGoodies <code>Model</code> class,
0489:             * you can enable the identity check feature in the methods
0490:             * <code>#firePropertyChange</code> by setting the trailing boolean
0491:             * parameter to <code>true</code>.
0492:             *
0493:             * @param listHolder             holds the list or list model
0494:             * @param selectionHolder        holds the selection
0495:             * @throws NullPointerException  if <code>listHolder</code>
0496:             *     or <code>selectionHolder</code> is <code>null</code>
0497:             */
0498:            public SelectionInList(ValueModel listHolder,
0499:                    ValueModel selectionHolder) {
0500:                this (listHolder, selectionHolder, new ValueHolder(Integer
0501:                        .valueOf(NO_SELECTION_INDEX)));
0502:            }
0503:
0504:            /**
0505:             * Constructs a SelectionInList on the given list holder,
0506:             * selection holder and selection index holder.<p>
0507:             *
0508:             * <strong>Constraints:</strong>
0509:             * 1) The listHolder must hold instances of List or ListModel and
0510:             * 2) must report a value change whenever the value's identity changes.
0511:             * Note that many bean properties don't fire a PropertyChangeEvent
0512:             * if the old and new value are equal - and so would break this constraint.
0513:             * If you provide a ValueHolder, enable its identityCheck feature
0514:             * during construction. If you provide an adapted bean property from
0515:             * a bean that extends the JGoodies <code>Model</code> class,
0516:             * you can enable the identity check feature in the methods
0517:             * <code>#firePropertyChange</code> by setting the trailing boolean
0518:             * parameter to <code>true</code>.
0519:             *
0520:             * @param listHolder               holds the list or list model
0521:             * @param selectionHolder          holds the selection
0522:             * @param selectionIndexHolder     holds the selection index
0523:             *
0524:             * @throws NullPointerException    if the <code>listModelHolder</code>,
0525:             *     <code>selectionHolder</code>, or <code>selectionIndexHolder</code>
0526:             *     is <code>null</code>
0527:             * @throws IllegalArgumentException if the listHolder is a ValueHolder
0528:             *     that doesn't check the identity when changing its value
0529:             * @throws ClassCastException if the listModelHolder contents
0530:             *     is neither a List nor a ListModel
0531:             */
0532:            public SelectionInList(ValueModel listHolder,
0533:                    ValueModel selectionHolder, ValueModel selectionIndexHolder) {
0534:                super (listHolder);
0535:                if (selectionHolder == null)
0536:                    throw new NullPointerException(
0537:                            "The selection holder must not be null.");
0538:                if (selectionIndexHolder == null)
0539:                    throw new NullPointerException(
0540:                            "The selection index holder must not be null.");
0541:
0542:                selectionChangeHandler = new SelectionChangeHandler();
0543:                selectionIndexChangeHandler = new SelectionIndexChangeHandler();
0544:
0545:                this .selectionHolder = selectionHolder;
0546:                this .selectionIndexHolder = selectionIndexHolder;
0547:                initializeSelectionIndex();
0548:
0549:                this .selectionHolder
0550:                        .addValueChangeListener(selectionChangeHandler);
0551:                this .selectionIndexHolder
0552:                        .addValueChangeListener(selectionIndexChangeHandler);
0553:            }
0554:
0555:            // ListModel Helper Code **************************************************
0556:
0557:            /**
0558:             * Notifies all registered ListDataListeners that the contents
0559:             * of the selected list item - if any - has changed.
0560:             * Useful to update a presentation after editing the selection.
0561:             * See the tutorial's AlbumManagerModel for an example how to use
0562:             * this feature.<p>
0563:             *
0564:             * If the list holder holds a ListModel, this SelectionInList listens
0565:             * to ListDataEvents fired by that ListModel, and forwards these events
0566:             * by invoking the associated <code>#fireXXX</code> method, which in turn
0567:             * notifies all registered ListDataListeners. Therefore if you fire
0568:             * ListDataEvents in an underlying ListModel, you don't need this method
0569:             * and should not use it to avoid sending duplicate ListDataEvents.
0570:             *
0571:             * @see ListModel
0572:             * @see ListDataListener
0573:             * @see ListDataEvent
0574:             *
0575:             * @since 1.0.2
0576:             */
0577:            public void fireSelectedContentsChanged() {
0578:                if (hasSelection()) {
0579:                    int selectionIndex = getSelectionIndex();
0580:                    fireContentsChanged(selectionIndex, selectionIndex);
0581:                }
0582:            }
0583:
0584:            // Accessing the List, Selection and Index ********************************
0585:
0586:            /**
0587:             * Looks up and returns the current selection using
0588:             * the current selection index. Returns <code>null</code> if
0589:             * no object is selected or if the list has no elements.
0590:             *
0591:             * @return the current selection, <code>null</code> if none is selected
0592:             */
0593:            public E getSelection() {
0594:                return getSafeElementAt(getSelectionIndex());
0595:            }
0596:
0597:            /**
0598:             * Sets the selection index to the index of the first list element
0599:             * that equals {@code newSelection}. If {@code newSelection}
0600:             * is {@code null}, it is interpreted as <em>unspecified</em>
0601:             * and the selection index is set to -1, and this SelectionInList
0602:             * has no selection. Does nothing if the list is empty or {@code null}.
0603:             *
0604:             * @param newSelection   the object to be set as new selection,
0605:             *     or {@code null} to set the selection index to -1
0606:             */
0607:            public void setSelection(E newSelection) {
0608:                if (!isEmpty()) {
0609:                    setSelectionIndex(indexOf(newSelection));
0610:                }
0611:            }
0612:
0613:            /**
0614:             * Checks and answers if an element is selected.
0615:             *
0616:             * @return true if an element is selected, false otherwise
0617:             */
0618:            public boolean hasSelection() {
0619:                return getSelectionIndex() != NO_SELECTION_INDEX;
0620:            }
0621:
0622:            /**
0623:             * Checks and answers whether the selection is empty or not.
0624:             * Unlike #hasSelection, the underlying property #selectionEmpty
0625:             * for this method is bound. I.e. you can observe this property
0626:             * using a PropertyChangeListener to update UI state.
0627:             *
0628:             * @return true if nothing is selected, false if there's a selection
0629:             * @see #clearSelection
0630:             * @see #hasSelection
0631:             */
0632:            public boolean isSelectionEmpty() {
0633:                return !hasSelection();
0634:            }
0635:
0636:            /**
0637:             * Clears the selection of this SelectionInList - if any.
0638:             */
0639:            public void clearSelection() {
0640:                setSelectionIndex(NO_SELECTION_INDEX);
0641:            }
0642:
0643:            /**
0644:             * Returns the selection index.
0645:             *
0646:             * @return the selection index
0647:             *
0648:             * @throws NullPointerException if the selection index holder
0649:             *     has a null Object set
0650:             */
0651:            public int getSelectionIndex() {
0652:                return ((Integer) getSelectionIndexHolder().getValue())
0653:                        .intValue();
0654:            }
0655:
0656:            /**
0657:             * Sets a new selection index. Does nothing if it is the same as before.
0658:             *
0659:             * @param newSelectionIndex   the selection index to be set
0660:             * @throws IndexOutOfBoundsException if the new selection index
0661:             *    is outside the bounds of the list
0662:             */
0663:            public void setSelectionIndex(int newSelectionIndex) {
0664:                int upperBound = getSize() - 1;
0665:                if (newSelectionIndex < NO_SELECTION_INDEX
0666:                        || newSelectionIndex > upperBound)
0667:                    throw new IndexOutOfBoundsException("The selection index "
0668:                            + newSelectionIndex + " must be in [-1, "
0669:                            + upperBound + "]");
0670:
0671:                oldSelectionIndex = getSelectionIndex();
0672:                if (oldSelectionIndex == newSelectionIndex)
0673:                    return;
0674:
0675:                getSelectionIndexHolder().setValue(
0676:                        Integer.valueOf(newSelectionIndex));
0677:            }
0678:
0679:            // Accessing the Holders for: List, Selection and Index *******************
0680:
0681:            /**
0682:             * Returns the selection holder.
0683:             *
0684:             * @return the selection holder
0685:             */
0686:            public ValueModel getSelectionHolder() {
0687:                return selectionHolder;
0688:            }
0689:
0690:            /**
0691:             * Sets a new selection holder.
0692:             * Does nothing if the new is the same as before.
0693:             * The selection remains unchanged and is still driven
0694:             * by the selection index holder. It's just that future
0695:             * index changes will update the new selection holder
0696:             * and that future selection holder changes affect the
0697:             * selection index.
0698:             *
0699:             * @param newSelectionHolder   the selection holder to set
0700:             *
0701:             * @throws NullPointerException if the new selection holder is null
0702:             */
0703:            public void setSelectionHolder(ValueModel newSelectionHolder) {
0704:                if (newSelectionHolder == null)
0705:                    throw new NullPointerException(
0706:                            "The new selection holder must not be null.");
0707:
0708:                ValueModel oldSelectionHolder = getSelectionHolder();
0709:                oldSelectionHolder
0710:                        .removeValueChangeListener(selectionChangeHandler);
0711:                selectionHolder = newSelectionHolder;
0712:                oldSelection = (E) newSelectionHolder.getValue();
0713:                newSelectionHolder
0714:                        .addValueChangeListener(selectionChangeHandler);
0715:                firePropertyChange(PROPERTYNAME_SELECTION_HOLDER,
0716:                        oldSelectionHolder, newSelectionHolder);
0717:            }
0718:
0719:            /**
0720:             * Returns the selection index holder.
0721:             *
0722:             * @return the selection index holder
0723:             */
0724:            public ValueModel getSelectionIndexHolder() {
0725:                return selectionIndexHolder;
0726:            }
0727:
0728:            /**
0729:             * Sets a new selection index holder.
0730:             * Does nothing if the new is the same as before.
0731:             *
0732:             * @param newSelectionIndexHolder   the selection index holder to set
0733:             *
0734:             * @throws NullPointerException if the new selection index holder is null
0735:             * @throws IllegalArgumentException if the value of the new selection index
0736:             *     holder is null
0737:             */
0738:            public void setSelectionIndexHolder(
0739:                    ValueModel newSelectionIndexHolder) {
0740:                if (newSelectionIndexHolder == null)
0741:                    throw new NullPointerException(
0742:                            "The new selection index holder must not be null.");
0743:
0744:                if (newSelectionIndexHolder.getValue() == null)
0745:                    throw new IllegalArgumentException(
0746:                            "The value of the new selection index holder must not be null.");
0747:
0748:                ValueModel oldSelectionIndexHolder = getSelectionIndexHolder();
0749:                if (equals(oldSelectionIndexHolder, newSelectionIndexHolder))
0750:                    return;
0751:
0752:                oldSelectionIndexHolder
0753:                        .removeValueChangeListener(selectionIndexChangeHandler);
0754:                selectionIndexHolder = newSelectionIndexHolder;
0755:                newSelectionIndexHolder
0756:                        .addValueChangeListener(selectionIndexChangeHandler);
0757:                oldSelectionIndex = getSelectionIndex();
0758:                oldSelection = getSafeElementAt(oldSelectionIndex);
0759:                firePropertyChange(PROPERTYNAME_SELECTION_INDEX_HOLDER,
0760:                        oldSelectionIndexHolder, newSelectionIndexHolder);
0761:            }
0762:
0763:            // ValueModel Implementation ********************************************
0764:
0765:            /**
0766:             * Returns the current selection, <code>null</code> if the selection index
0767:             * does not represent a selection in the list.
0768:             *
0769:             * @return the selected element - if any
0770:             */
0771:            public E getValue() {
0772:                return getSelection();
0773:            }
0774:
0775:            /**
0776:             * Sets the selection index to the index of the first list element
0777:             * that equals {@code newValue}. If {@code newValue}
0778:             * is {@code null}, it is interpreted as <em>unspecified</em>
0779:             * and the selection index is set to -1, and this SelectionInList
0780:             * has no selection. Does nothing if the list is empty or {@code null}.
0781:             *
0782:             * @param newValue   the object to be set as new selection,
0783:             *     or {@code null} to set the selection index to -1
0784:             */
0785:            public void setValue(Object newValue) {
0786:                setSelection((E) newValue);
0787:            }
0788:
0789:            /**
0790:             * Registers the given PropertyChangeListener with this model.
0791:             * The listener will be notified if the value has changed.<p>
0792:             *
0793:             * The PropertyChangeEvents delivered to the listener have the name
0794:             * set to "value". In other words, the listeners won't get notified
0795:             * when a PropertyChangeEvent is fired that has a null object as
0796:             * the name to indicate an arbitrary set of the event source's
0797:             * properties have changed.<p>
0798:             *
0799:             * In the rare case, where you want to notify a PropertyChangeListener
0800:             * even with PropertyChangeEvents that have no property name set,
0801:             * you can register the listener with #addPropertyChangeListener,
0802:             * not #addValueChangeListener.
0803:             *
0804:             * @param l the listener to add
0805:             *
0806:             * @see ValueModel
0807:             */
0808:            public void addValueChangeListener(PropertyChangeListener l) {
0809:                addPropertyChangeListener(PROPERTYNAME_VALUE, l);
0810:            }
0811:
0812:            /**
0813:             * Removes the given PropertyChangeListener from the model.
0814:             *
0815:             * @param l the listener to remove
0816:             */
0817:            public void removeValueChangeListener(PropertyChangeListener l) {
0818:                removePropertyChangeListener(PROPERTYNAME_VALUE, l);
0819:            }
0820:
0821:            /**
0822:             * Notifies all listeners that have registered interest for
0823:             * notification on this event type.  The event instance
0824:             * is lazily created using the parameters passed into
0825:             * the fire method.
0826:             *
0827:             * @param oldValue   the value before the change
0828:             * @param newValue   the value after the change
0829:             *
0830:             * @see java.beans.PropertyChangeSupport
0831:             */
0832:            void fireValueChange(Object oldValue, Object newValue) {
0833:                firePropertyChange(PROPERTYNAME_VALUE, oldValue, newValue);
0834:            }
0835:
0836:            // Misc ******************************************************************
0837:
0838:            /**
0839:             * Removes the internal listeners from the list holder, selection holder,
0840:             * selection index holder. If the current list is a ListModel, the internal
0841:             * ListDataListener is removed from the list model. This SelectionInList
0842:             * must not be used after calling <code>#release</code>.<p>
0843:             *
0844:             * To avoid memory leaks it is recommended to invoke this method,
0845:             * if the list holder, selection holder, or selection index holder
0846:             * live much longer than this SelectionInList.
0847:             * Instead of releasing the SelectionInList, you typically make
0848:             * the list holder, selection holder, and selection index holder
0849:             * obsolete by releasing the PresentationModel or BeanAdapter that has
0850:             * created them before.<p>
0851:             *
0852:             * As an alternative you may use ValueModels that in turn use
0853:             * event listener lists implemented using <code>WeakReference</code>.<p>
0854:             *
0855:             * Basically this release method performs the reverse operation
0856:             * performed during the SelectionInList construction.
0857:             *
0858:             * @see PresentationModel#release()
0859:             * @see BeanAdapter#release()
0860:             * @see java.lang.ref.WeakReference
0861:             *
0862:             * @since 1.2
0863:             */
0864:            @Override
0865:            public void release() {
0866:                super .release();
0867:                selectionHolder
0868:                        .removeValueChangeListener(selectionChangeHandler);
0869:                selectionIndexHolder
0870:                        .removeValueChangeListener(selectionIndexChangeHandler);
0871:                selectionHolder = null;
0872:                selectionIndexHolder = null;
0873:                oldSelection = null;
0874:            }
0875:
0876:            // Helper Code ***********************************************************
0877:
0878:            private E getSafeElementAt(int index) {
0879:                return (index < 0 || index >= getSize()) ? null
0880:                        : getElementAt(index);
0881:            }
0882:
0883:            /**
0884:             * Returns the index in the list of the first occurrence of the specified
0885:             * element, or -1 if the element is {@code null} or the list does not
0886:             * contain this element.<p>
0887:             *
0888:             * {@code null} is mapped to -1, because the current implementation
0889:             * interprets a null selection as <em>unspecified</em>.
0890:             *
0891:             * @param element  the element to search for
0892:             * @return the index in the list of the first occurrence of the
0893:             *     given element, or -1 if the element is {@code null} or
0894:             *     the list does not contain this element.
0895:             */
0896:            private int indexOf(Object element) {
0897:                return indexOf(getListHolder().getValue(), element);
0898:            }
0899:
0900:            /**
0901:             * Returns the index in the list of the first occurrence of the specified
0902:             * element, or -1 if the element is {@code null} or the list does not
0903:             * contain this element.<p>
0904:             *
0905:             * {@code null} is mapped to -1, because the current implementation
0906:             * interprets a null selection as <em>unspecified</em>.
0907:             *
0908:             * @param aList    the List or ListModel used to look up the element
0909:             * @param element  the element to search for
0910:             * @return the index in the list of the first occurrence of the
0911:             *     given element, or -1 if the element is {@code null} or
0912:             *     the list does not contain this element.
0913:             */
0914:            private int indexOf(Object aList, Object element) {
0915:                if (element == null) {
0916:                    return NO_SELECTION_INDEX;
0917:                } else if (getSize(aList) == 0) {
0918:                    return NO_SELECTION_INDEX;
0919:                }
0920:                if (aList instanceof  List) {
0921:                    return ((List<?>) aList).indexOf(element);
0922:                }
0923:
0924:                // Search the first occurrence of element in the list model.
0925:                ListModel listModel = (ListModel) aList;
0926:                int size = listModel.getSize();
0927:                for (int index = 0; index < size; index++) {
0928:                    if (element.equals(listModel.getElementAt(index)))
0929:                        return index;
0930:                }
0931:                return NO_SELECTION_INDEX;
0932:            }
0933:
0934:            /**
0935:             * Sets the index according to the selection, unless the selection
0936:             * is {@code null}.
0937:             * Also initializes the copied selection and selection index.
0938:             * This method is invoked by the constructors to synchronize
0939:             * the selection and index. No listeners are installed yet.<p>
0940:             *
0941:             * An initial selection of {@code null} may indicate that the selection
0942:             * is unspecified. This happens for example, if the selection holder
0943:             * adapts a bean property via a PresentationModel, but the bean
0944:             * is {@code null}. In this case, the current semantics decides to not
0945:             * set the selection index - even if null is a list element.<p>
0946:             *
0947:             * This leads to an inconsistency. If we construct a SelectionInList
0948:             * with {1, 2, 3} and initial selection 1, the selection index is set.
0949:             * If we construct {null, 2, 3} and initial selection null, the selection
0950:             * index is not set.<p>
0951:             *
0952:             * TODO: Discuss whether we want to set the selection index if the
0953:             * initial selection is {@code null}.
0954:             */
0955:            private void initializeSelectionIndex() {
0956:                E selectionValue = (E) selectionHolder.getValue();
0957:                if (selectionValue != null) {
0958:                    setSelectionIndex(indexOf(selectionValue));
0959:                }
0960:                oldSelection = selectionValue;
0961:                oldSelectionIndex = getSelectionIndex();
0962:            }
0963:
0964:            // Overriding Superclass Behavior *****************************************
0965:
0966:            /**
0967:             * Creates and returns the ListDataListener used to observe
0968:             * changes in the underlying ListModel. It is re-registered
0969:             * in <code>#updateListModel</code>.
0970:             *
0971:             * @return the ListDataListener that handles changes
0972:             *     in the underlying ListModel
0973:             */
0974:            @Override
0975:            protected ListDataListener createListDataChangeHandler() {
0976:                return new ListDataChangeHandler();
0977:            }
0978:
0979:            /**
0980:             * Removes the list data change handler from the old list in case
0981:             * it is a <code>ListModel</code> and adds it to new one in case
0982:             * it is a <code>ListModel</code>.
0983:             * It then fires a property change for the list and a contents change event
0984:             * for the list content. Finally it tries to restore the previous selection
0985:             * - if any.<p>
0986:             *
0987:             * Since version 1.1 the selection will be restored after
0988:             * the list content change has been indicated. This is because some
0989:             * listeners may clear the selection in a side-effect.
0990:             * For example a JTable that is bound to this SelectionInList
0991:             * via an AbstractTableAdapter and a SingleSelectionAdapter
0992:             * will clear the selection if the new list has a size other
0993:             * than the old list.
0994:             *
0995:             * @param oldList   the old list content
0996:             * @param oldSize   the size of the old List content
0997:             * @param newList   the new list content
0998:             *
0999:             * @see javax.swing.JTable#tableChanged(javax.swing.event.TableModelEvent)
1000:             */
1001:            @Override
1002:            protected void updateList(Object oldList, int oldSize,
1003:                    Object newList) {
1004:                boolean hadSelection = hasSelection();
1005:                Object oldSelectionHolderValue = hadSelection ? getSelectionHolder()
1006:                        .getValue()
1007:                        : null;
1008:                super .updateList(oldList, oldSize, newList);
1009:                if (hadSelection) {
1010:                    setSelectionIndex(indexOf(newList, oldSelectionHolderValue));
1011:                }
1012:            }
1013:
1014:            // Event Handlers *********************************************************
1015:
1016:            /**
1017:             * Handles ListDataEvents in the list model.
1018:             * In addition to the ListDataChangeHandler in IndirectListModel,
1019:             * this class also updates the selection index.
1020:             */
1021:            private final class ListDataChangeHandler implements 
1022:                    ListDataListener {
1023:
1024:                /**
1025:                 * Sent after the indices in the index0, index1
1026:                 * interval have been inserted in the data model.
1027:                 * The new interval includes both index0 and index1.
1028:                 *
1029:                 * @param evt  a <code>ListDataEvent</code> encapsulating the
1030:                 *    event information
1031:                 */
1032:                public void intervalAdded(ListDataEvent evt) {
1033:                    int index0 = evt.getIndex0();
1034:                    int index1 = evt.getIndex1();
1035:                    int index = getSelectionIndex();
1036:                    fireIntervalAdded(index0, index1);
1037:                    // If the added elements are after the index; do nothing.
1038:                    if (index >= index0) {
1039:                        setSelectionIndex(index + (index1 - index0 + 1));
1040:                    }
1041:                }
1042:
1043:                /**
1044:                 * Sent after the indices in the index0, index1 interval
1045:                 * have been removed from the data model.  The interval
1046:                 * includes both index0 and index1.
1047:                 *
1048:                 * @param evt  a <code>ListDataEvent</code> encapsulating the
1049:                 *    event information
1050:                 */
1051:                public void intervalRemoved(ListDataEvent evt) {
1052:                    int index0 = evt.getIndex0();
1053:                    int index1 = evt.getIndex1();
1054:                    int index = getSelectionIndex();
1055:                    fireIntervalRemoved(index0, index1);
1056:                    if (index < index0) {
1057:                        // The removed elements are after the index; do nothing.
1058:                    } else if (index <= index1) {
1059:                        setSelectionIndex(NO_SELECTION_INDEX);
1060:                    } else {
1061:                        setSelectionIndex(index - (index1 - index0 + 1));
1062:                    }
1063:                }
1064:
1065:                /**
1066:                 * Sent when the contents of the list has changed in a way
1067:                 * that's too complex to characterize with the previous
1068:                 * methods. For example, this is sent when an item has been
1069:                 * replaced. Index0 and index1 bracket the change.
1070:                 *
1071:                 * @param evt  a <code>ListDataEvent</code> encapsulating the
1072:                 *    event information
1073:                 */
1074:                public void contentsChanged(ListDataEvent evt) {
1075:                    fireContentsChanged(evt.getIndex0(), evt.getIndex1());
1076:                    updateSelectionContentsChanged(evt.getIndex0(), evt
1077:                            .getIndex1());
1078:                }
1079:
1080:                private void updateSelectionContentsChanged(int first, int last) {
1081:                    if (first < 0)
1082:                        return;
1083:                    int selectionIndex = getSelectionIndex();
1084:                    if (first <= selectionIndex && (selectionIndex <= last)) {
1085:                        // need to synch directly on the holder because the
1086:                        // usual methods for setting selection/-index check for
1087:                        // equality
1088:                        getSelectionHolder().setValue(
1089:                                getElementAt(selectionIndex));
1090:                    }
1091:                }
1092:
1093:            }
1094:
1095:            /**
1096:             * Listens to changes of the selection.
1097:             */
1098:            private final class SelectionChangeHandler implements 
1099:                    PropertyChangeListener {
1100:
1101:                /**
1102:                 * The selection has been changed. Updates the selection index holder's
1103:                 * value and notifies registered listeners about the changes - if any -
1104:                 * in the selection index, selection empty, selection, and value.<p>
1105:                 *
1106:                 * Adjusts the selection holder's value and the old selection index
1107:                 * before any event is fired. This ensures that the event old and
1108:                 * new values are consistent with the SelectionInList's state.<p>
1109:                 *
1110:                 * The current implementation assumes that the event sources
1111:                 * provides a non-{@code null} new value. An arbitrary selection holder
1112:                 * may fire change events where the new and/or old value is
1113:                 * {@code null} to indicate that it is unknown, unspecified,
1114:                 * or difficult to compute (now).<p>
1115:                 *
1116:                 * TODO: Consider getting the new selection safely from the selection
1117:                 * holder in case the new value is {@code null}. See the commented
1118:                 * code section below.
1119:                 *
1120:                 * @param evt   the property change event to be handled
1121:                 */
1122:                public void propertyChange(PropertyChangeEvent evt) {
1123:                    E oldValue = (E) evt.getOldValue();
1124:                    E newSelection = (E) evt.getNewValue();
1125:                    //            if (newSelection == null) {
1126:                    //                newSelection = (E) selectionHolder.getValue();
1127:                    //            }
1128:                    int newSelectionIndex = indexOf(newSelection);
1129:                    if (newSelectionIndex != oldSelectionIndex) {
1130:                        selectionIndexHolder
1131:                                .removeValueChangeListener(selectionIndexChangeHandler);
1132:                        selectionIndexHolder.setValue(Integer
1133:                                .valueOf(newSelectionIndex));
1134:                        selectionIndexHolder
1135:                                .addValueChangeListener(selectionIndexChangeHandler);
1136:                    }
1137:                    int theOldSelectionIndex = oldSelectionIndex;
1138:                    oldSelectionIndex = newSelectionIndex;
1139:                    oldSelection = newSelection;
1140:                    firePropertyChange(PROPERTYNAME_SELECTION_INDEX,
1141:                            theOldSelectionIndex, newSelectionIndex);
1142:                    firePropertyChange(PROPERTYNAME_SELECTION_EMPTY,
1143:                            theOldSelectionIndex == NO_SELECTION_INDEX,
1144:                            newSelectionIndex == NO_SELECTION_INDEX);
1145:                    /*
1146:                     * Implementation Note: The following two lines fire the
1147:                     * PropertyChangeEvents for the 'selection' and 'value' properties.
1148:                     * If the old and new value are equal, no event is fired.
1149:                     *
1150:                     * TODO: Consider using ==, not equals to check for changes.
1151:                     * That would enable API users to use the selection holder with
1152:                     * beans that must be checked with ==, not equals.
1153:                     * However, the SelectionInList's List would still use equals
1154:                     * to find the index of an element.
1155:                     */
1156:                    firePropertyChange(PROPERTYNAME_SELECTION, oldValue,
1157:                            newSelection);
1158:                    fireValueChange(oldValue, newSelection);
1159:                }
1160:            }
1161:
1162:            /**
1163:             * Listens to changes of the selection index.
1164:             */
1165:            private final class SelectionIndexChangeHandler implements 
1166:                    PropertyChangeListener {
1167:
1168:                /**
1169:                 * The selection index has been changed. Updates the selection holder
1170:                 * value and notifies registered listeners about changes - if any -
1171:                 * in the selection index, selection empty, selection, and value.<p>
1172:                 *
1173:                 * Handles null old values in the index PropertyChangeEvent.
1174:                 * Ignores null new values in this events, because the selection
1175:                 * index value must always be a non-null value.<p>
1176:                 *
1177:                 * Adjusts the selection holder's value and the old selection index
1178:                 * before any event is fired. This ensures that the event old and
1179:                 * new values are consistent with the SelectionInList's state.
1180:                 *
1181:                 * @param evt   the property change event to be handled
1182:                 */
1183:                public void propertyChange(PropertyChangeEvent evt) {
1184:                    int newSelectionIndex = getSelectionIndex();
1185:                    E theOldSelection = oldSelection;
1186:                    //E oldSelection = getSafeElementAt(oldSelectionIndex);
1187:                    E newSelection = getSafeElementAt(newSelectionIndex);
1188:                    /*
1189:                     * Implementation Note: The following conditional suppresses
1190:                     * value change events if the old and new selection are equal.
1191:                     *
1192:                     * TODO: Consider using ==, not equals to check for changes.
1193:                     * That would enable API users to use the selection holder with
1194:                     * beans that must be checked with ==, not equals.
1195:                     * However, the SelectionInList's List would still use equals
1196:                     * to find the index of an element.
1197:                     */
1198:                    if (!SelectionInList.this .equals(theOldSelection,
1199:                            newSelection)) {
1200:                        selectionHolder
1201:                                .removeValueChangeListener(selectionChangeHandler);
1202:                        selectionHolder.setValue(newSelection);
1203:                        selectionHolder
1204:                                .addValueChangeListener(selectionChangeHandler);
1205:                    }
1206:                    int theOldSelectionIndex = oldSelectionIndex;
1207:                    oldSelectionIndex = newSelectionIndex;
1208:                    oldSelection = newSelection;
1209:                    firePropertyChange(PROPERTYNAME_SELECTION_INDEX,
1210:                            theOldSelectionIndex, newSelectionIndex);
1211:                    firePropertyChange(PROPERTYNAME_SELECTION_EMPTY,
1212:                            theOldSelectionIndex == NO_SELECTION_INDEX,
1213:                            newSelectionIndex == NO_SELECTION_INDEX);
1214:                    firePropertyChange(PROPERTYNAME_SELECTION, theOldSelection,
1215:                            newSelection);
1216:                    fireValueChange(theOldSelection, newSelection);
1217:                }
1218:            }
1219:
1220:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.