Source Code Cross Referenced for EventBroadcaster.java in  » IDE-Netbeans » web.core » org » netbeans » swing » outline » 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 » IDE Netbeans » web.core » org.netbeans.swing.outline 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003:         *
0004:         * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * The contents of this file are subject to the terms of either the GNU
0007:         * General Public License Version 2 only ("GPL") or the Common
0008:         * Development and Distribution License("CDDL") (collectively, the
0009:         * "License"). You may not use this file except in compliance with the
0010:         * License. You can obtain a copy of the License at
0011:         * http://www.netbeans.org/cddl-gplv2.html
0012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013:         * specific language governing permissions and limitations under the
0014:         * License.  When distributing the software, include this License Header
0015:         * Notice in each file and include the License file at
0016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
0017:         * particular file as subject to the "Classpath" exception as provided
0018:         * by Sun in the GPL Version 2 section of the License file that
0019:         * accompanied this code. If applicable, add the following below the
0020:         * License Header, with the fields enclosed by brackets [] replaced by
0021:         * your own identifying information:
0022:         * "Portions Copyrighted [year] [name of copyright owner]"
0023:         *
0024:         * Contributor(s):
0025:         *
0026:         * The Original Software is NetBeans. The Initial Developer of the Original
0027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028:         * Microsystems, Inc. All Rights Reserved.
0029:         *
0030:         * If you wish your version of this file to be governed by only the CDDL
0031:         * or only the GPL Version 2, indicate your decision by adding
0032:         * "[Contributor] elects to include this software in this distribution
0033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034:         * single choice of license, a recipient has the option to distribute
0035:         * your version of this file under either the CDDL, the GPL Version 2 or
0036:         * to extend the choice of license to its licensees as provided above.
0037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
0038:         * Version 2 license, then the option applies only if the new code is
0039:         * made subject to such option by the copyright holder.
0040:         */
0041:
0042:        package org.netbeans.swing.outline;
0043:
0044:        import java.util.ArrayList;
0045:        import java.util.Arrays;
0046:        import java.util.List;
0047:        import javax.swing.SwingUtilities;
0048:        import javax.swing.event.TableModelEvent;
0049:        import javax.swing.event.TableModelListener;
0050:        import javax.swing.event.TreeExpansionEvent;
0051:        import javax.swing.event.TreeExpansionListener;
0052:        import javax.swing.event.TreeModelEvent;
0053:        import javax.swing.event.TreeModelListener;
0054:        import javax.swing.table.TableModel;
0055:        import javax.swing.tree.AbstractLayoutCache;
0056:        import javax.swing.tree.ExpandVetoException;
0057:        import javax.swing.tree.TreeModel;
0058:        import javax.swing.tree.TreePath;
0059:
0060:        /** Responsible for handling tree model events from the user-supplied treemodel
0061:         * portion of a DefaultOutlineModel, translating them into appropriate 
0062:         * TableModelEvents and refiring these events to listeners on the table model.
0063:         * <p>
0064:         * This class could be (and originally was) incorporated directly into 
0065:         * DefaultOutlineModel, but is separated for better readability and separation
0066:         * of concerns.
0067:         *
0068:         * @author  Tim Boudreau
0069:         */
0070:        final class EventBroadcaster implements  TableModelListener,
0071:                TreeModelListener, ExtTreeWillExpandListener,
0072:                TreeExpansionListener {
0073:
0074:            /** Debugging constant for whether logging should be enabled */
0075:            static boolean log = false;
0076:
0077:            /** Debugging message counter to differentiate log entries */
0078:            private int logcount = 0;
0079:
0080:            /** The model we will proxy */
0081:            private DefaultOutlineModel model;
0082:
0083:            /** The last event sent to treeWillExpand/Collapse, used to compare against the
0084:             * next value sent to treeExpanded/Collapse */
0085:            private TreeExpansionEvent inProgressEvent = null;
0086:
0087:            /** A TableModelEvent generated in treeWillExpand/Collapse (so, generated when
0088:             * data about the rows/columns in the tree model is still in sync with the
0089:             * TableModel), which will be fired from treeExpanded/Collapsed if the
0090:             * expansion event is not vetoed */
0091:            private TableModelEvent pendingExpansionEvent = null;
0092:
0093:            /** Are we in the middle of firing multiple TableModelEvents for a single
0094:             * TreeModelEvent. */
0095:            private boolean inMultiEvent = false;
0096:
0097:            //Some constants we use to have a single method handle all translated
0098:            //event firing
0099:            private static final int NODES_CHANGED = 0;
0100:            private static final int NODES_INSERTED = 1;
0101:            private static final int NODES_REMOVED = 2;
0102:            private static final int STRUCTURE_CHANGED = 3;
0103:
0104:            //XXX deleteme - string version of the avoid constants debug output:
0105:            private static final String[] types = new String[] {
0106:                    "nodesChanged", "nodesInserted", "nodesRemoved",
0107:                    "structureChanged" }; //NOI18N
0108:
0109:            /** List of table model listeners */
0110:            private List tableListeners = new ArrayList();
0111:
0112:            /** List of tree model listeners */
0113:            private List treeListeners = new ArrayList();
0114:
0115:            /** Creates a new instance of EventBroadcaster which will
0116:             * produce events for the passed DefaultOutlineModel model.  */
0117:            public EventBroadcaster(DefaultOutlineModel model) {
0118:                setModel(model);
0119:            }
0120:
0121:            /** Debug logging */
0122:            private void log(String method, Object o) {
0123:                if (log) {
0124:                    if (o instanceof  TableModelEvent) {
0125:                        //TableModelEvents just give their hash code in toString()
0126:                        o = tableModelEventToString((TableModelEvent) o);
0127:                    }
0128:                    System.err
0129:                            .println("EB-"
0130:                                    + (logcount++)
0131:                                    + " "
0132:                                    + method
0133:                                    + ":"
0134:                                    + (o instanceof  String ? (String) o : o
0135:                                            .toString()));
0136:                }
0137:            }
0138:
0139:            //***************** Bean properties/convenience getters & setters ************    
0140:            /** Flag which is set to true while multiple TableModelEvents generated
0141:             * from a single TreeModelEvent are being fired, so clients can avoid
0142:             * any model queries until all pending changes have been fired.  The
0143:             * main thing to avoid is any mid-process repaints, which can only happen
0144:             * if the response to an event will be to call paintImmediately(). 
0145:             * <p>
0146:             * This value is guaranteed to be true for the first of a group of
0147:             * related events, and false if tested in response to the final event.
0148:             */
0149:            public boolean areMoreEventsPending() {
0150:                return inMultiEvent;
0151:            }
0152:
0153:            /** Get the outline model for which this broadcaster will proxy events*/
0154:            private DefaultOutlineModel getModel() {
0155:                return model;
0156:            }
0157:
0158:            /** Set the outline model this broadcaster will proxy events for */
0159:            private void setModel(DefaultOutlineModel model) {
0160:                this .model = model;
0161:            }
0162:
0163:            /** Convenience getter for the proxied model's layout cache */
0164:            private AbstractLayoutCache getLayout() {
0165:                return getModel().getLayout();
0166:            }
0167:
0168:            /** Convenience getter for the proxied model's TreePathSupport */
0169:            private TreePathSupport getTreePathSupport() {
0170:                return getModel().getTreePathSupport();
0171:            }
0172:
0173:            /** Convenience getter for the proxied model's user-supplied TreeModel */
0174:            private TreeModel getTreeModel() {
0175:                return getModel().getTreeModel();
0176:            }
0177:
0178:            /** Convenience getter for the proxied model's user-supplied TableModel (in
0179:             * practice, an instance of ProxyTableModel driven by the tree model and a
0180:             * RowModel) */
0181:            private TableModel getTableModel() {
0182:                return getModel().getTableModel();
0183:            }
0184:
0185:            //******************* Event source implementation **************************
0186:
0187:            /** Add a table model listener.  All events fired by this EventBroadcaster
0188:             * will have the OutlineModel as the event source */
0189:            public synchronized void addTableModelListener(TableModelListener l) {
0190:                tableListeners.add(l);
0191:            }
0192:
0193:            /** Add a tree model listener.  All events fired by this EventBroadcaster
0194:             * will have the OutlineModel as the event source */
0195:            public synchronized void addTreeModelListener(TreeModelListener l) {
0196:                treeListeners.add(l);
0197:            }
0198:
0199:            /** Remove a table model listener.  */
0200:            public synchronized void removeTableModelListener(
0201:                    TableModelListener l) {
0202:                tableListeners.remove(l);
0203:            }
0204:
0205:            /** Remove a tree model listener.  */
0206:            public synchronized void removeTreeModelListener(TreeModelListener l) {
0207:                treeListeners.remove(l);
0208:            }
0209:
0210:            /** Fire a table change to the list of listeners supplied. The event should
0211:             * already have its source set to be the OutlineModel we're proxying for. */
0212:            private void fireTableChange(TableModelEvent e,
0213:                    TableModelListener[] listeners) {
0214:                //Event may be null for offscreen info, etc.
0215:                if (e == null) {
0216:                    return;
0217:                }
0218:
0219:                assert (e.getSource() == getModel());
0220:
0221:                log("fireTableChange", e);
0222:
0223:                for (int i = 0; i < listeners.length; i++) {
0224:                    listeners[i].tableChanged(e);
0225:                }
0226:            }
0227:
0228:            /** Convenience method to fire a single table change to all listeners */
0229:            private void fireTableChange(TableModelEvent e) {
0230:                //Event may be null for offscreen info, etc.
0231:                if (e == null) {
0232:                    return;
0233:                }
0234:                inMultiEvent = false;
0235:                TableModelListener[] listeners = getTableModelListeners();
0236:                fireTableChange(e, getTableModelListeners());
0237:            }
0238:
0239:            /** Fires multiple table model events, setting the inMultiEvent flag
0240:             * as appropriate. */
0241:            private void fireTableChange(TableModelEvent[] e) {
0242:                //Event may be null for offscreen info, etc.
0243:                if (e == null || e.length == 0) {
0244:                    return;
0245:                }
0246:
0247:                TableModelListener[] listeners = getTableModelListeners();
0248:                inMultiEvent = e.length > 1;
0249:                try {
0250:                    for (int i = 0; i < e.length; i++) {
0251:                        fireTableChange(e[i], listeners);
0252:                        if (i == e.length - 1) {
0253:                            inMultiEvent = false;
0254:                        }
0255:                    }
0256:                } finally {
0257:                    inMultiEvent = false;
0258:                }
0259:            }
0260:
0261:            /** Fetch an array of the currently registered table model listeners */
0262:            private TableModelListener[] getTableModelListeners() {
0263:                TableModelListener[] listeners = null;
0264:                synchronized (this ) {
0265:                    listeners = new TableModelListener[tableListeners.size()];
0266:
0267:                    listeners = (TableModelListener[]) tableListeners
0268:                            .toArray(listeners);
0269:                }
0270:                return listeners;
0271:            }
0272:
0273:            /** Fire the passed TreeModelEvent of the specified type to all
0274:             * registered TreeModelListeners.  The passed event should already have
0275:             * its source set to be the model. */
0276:            private synchronized void fireTreeChange(TreeModelEvent e, int type) {
0277:                //Event may be null for offscreen info, etc.
0278:                if (e == null) {
0279:                    return;
0280:                }
0281:                assert (e.getSource() == getModel());
0282:
0283:                TreeModelListener[] listeners = null;
0284:                synchronized (this ) {
0285:                    listeners = new TreeModelListener[treeListeners.size()];
0286:                    listeners = (TreeModelListener[]) treeListeners
0287:                            .toArray(listeners);
0288:                }
0289:
0290:                log("fireTreeChange-" + types[type], e);
0291:
0292:                //Now refire it to any listeners
0293:                for (int i = 0; i < listeners.length; i++) {
0294:                    switch (type) {
0295:                    case NODES_CHANGED:
0296:                        listeners[i].treeNodesChanged(e);
0297:                        break;
0298:                    case NODES_INSERTED:
0299:                        listeners[i].treeNodesInserted(e);
0300:                        break;
0301:                    case NODES_REMOVED:
0302:                        listeners[i].treeNodesRemoved(e);
0303:                        break;
0304:                    case STRUCTURE_CHANGED:
0305:                        listeners[i].treeStructureChanged(e);
0306:                        break;
0307:                    default:
0308:                        assert false;
0309:                    }
0310:                }
0311:            }
0312:
0313:            //******************* Event listener implementations ************************    
0314:
0315:            /** Process a change event from the user-supplied tree model.  This
0316:             * method will throw an assertion failure if it receives any event type
0317:             * other than TableModelEvent.UPDATE - the ProxyTableModel should never,
0318:             * ever fire structural changes - only the tree model is allowed to do
0319:             * that. */
0320:            public void tableChanged(TableModelEvent e) {
0321:                assert SwingUtilities.isEventDispatchThread();
0322:                //The *ONLY* time we should see events here is due to user
0323:                //data entry.  The ProxyTableModel should never change out
0324:                //from under us - all structural changes happen through the
0325:                //table model.
0326:                assert (e.getType() == e.UPDATE) : "Table model should only fire "
0327:                        + "updates, never structural changes";
0328:
0329:                fireTableChange(translateEvent(e));
0330:            }
0331:
0332:            /** Process a change event from the user-supplied tree model.
0333:             * Order of operations: 
0334:             * <ol><li>Refire the same tree event with the OutlineModel we're
0335:             *   proxying as the source</li>
0336:             * <li>Create one or more table model events (more than one if the
0337:             * incoming event affects discontiguous rows) reflecting the effect
0338:             * of the tree change</li>
0339:             * <li>Call the method with the same signature as this one on the
0340:             * layout cache, so it will update its state appropriately</li>
0341:             * <li>Fire the generated TableModelEvent(s)</li></ol>
0342:             */
0343:            public void treeNodesChanged(TreeModelEvent e) {
0344:                assert SwingUtilities.isEventDispatchThread();
0345:
0346:                fireTreeChange(translateEvent(e), NODES_CHANGED);
0347:
0348:                TableModelEvent[] events = translateEvent(e, NODES_CHANGED);
0349:                getLayout().treeNodesChanged(e);
0350:                fireTableChange(events);
0351:            }
0352:
0353:            /** Process a node insertion event from the user-supplied tree model 
0354:             * Order of operations: 
0355:             * <ol><li>Refire the same tree event with the OutlineModel we're
0356:             *   proxying as the source</li>
0357:             * <li>Create one or more table model events (more than one if the
0358:             * incoming event affects discontiguous rows) reflecting the effect
0359:             * of the tree change</li>
0360:             * <li>Call the method with the same signature as this one on the
0361:             * layout cache, so it will update its state appropriately</li>
0362:             * <li>Fire the generated TableModelEvent(s)</li></ol>
0363:             */
0364:            public void treeNodesInserted(TreeModelEvent e) {
0365:                assert SwingUtilities.isEventDispatchThread();
0366:
0367:                fireTreeChange(translateEvent(e), NODES_INSERTED);
0368:
0369:                TableModelEvent[] events = translateEvent(e, NODES_INSERTED);
0370:                getLayout().treeNodesInserted(e);
0371:                fireTableChange(events);
0372:            }
0373:
0374:            /** Process a node removal event from the user-supplied tree model 
0375:             * Order of operations: 
0376:             * <ol><li>Refire the same tree event with the OutlineModel we're
0377:             *   proxying as the source</li>
0378:             * <li>Create one or more table model events (more than one if the
0379:             * incoming event affects discontiguous rows) reflecting the effect
0380:             * of the tree change</li>
0381:             * <li>Call the method with the same signature as this one on the
0382:             * layout cache, so it will update its state appropriately</li>
0383:             * <li>Fire the generated TableModelEvent(s)</li></ol>
0384:             */
0385:            public void treeNodesRemoved(TreeModelEvent e) {
0386:                assert SwingUtilities.isEventDispatchThread();
0387:
0388:                fireTreeChange(e, NODES_REMOVED);
0389:
0390:                TableModelEvent[] events = translateEvent(e, NODES_REMOVED);
0391:                getLayout().treeNodesRemoved(e);
0392:                fireTableChange(events);
0393:            }
0394:
0395:            /** Process a structural change event from the user-supplied tree model.
0396:             * This will result in a generic &quot;something changed&quot; 
0397:             * TableModelEvent being fired.  */
0398:            public void treeStructureChanged(TreeModelEvent e) {
0399:                assert SwingUtilities.isEventDispatchThread();
0400:
0401:                getLayout().treeStructureChanged(e);
0402:                fireTreeChange(e, STRUCTURE_CHANGED);
0403:
0404:                //If it's a structural change, we need to dump all our info about the
0405:                //existing tree structure - it can be bogus now.  Similar to JTree,
0406:                //this will have the effect of collapsing all expanded paths.  The
0407:                //TreePathSupport takes care of dumping the layout cache's copy of
0408:                //such data
0409:                getTreePathSupport().clear();
0410:
0411:                //We will just fire a "Something happened. Go figure out what." event.
0412:                fireTableChange(new TableModelEvent(getModel()));
0413:            }
0414:
0415:            /** Receives a TreeWillCollapse event and constructs a TableModelEvent
0416:             * based on the pending changes while the model still reflects the unchanged
0417:             * state */
0418:            public void treeWillCollapse(TreeExpansionEvent event)
0419:                    throws ExpandVetoException {
0420:                assert SwingUtilities.isEventDispatchThread();
0421:
0422:                log("treeWillCollapse", event);
0423:
0424:                //Construct the TableModelEvent here, before data structures have
0425:                //changed.  We will fire it from TreeCollapsed if the change is 
0426:                //not vetoed.
0427:                pendingExpansionEvent = translateEvent(event, false);
0428:                log("treeWillCollapse generated ", pendingExpansionEvent);
0429:                inProgressEvent = event;
0430:            }
0431:
0432:            /** Receives a TreeWillExpand event and constructs a TableModelEvent
0433:             * based on the pending changes while the model still reflects the unchanged
0434:             * state */
0435:            public void treeWillExpand(TreeExpansionEvent event)
0436:                    throws ExpandVetoException {
0437:                assert SwingUtilities.isEventDispatchThread();
0438:
0439:                log("treeWillExpand", event);
0440:
0441:                //Construct the TableModelEvent here, before data structures have
0442:                //changed.  We will fire it from TreeExpanded if the change is not
0443:                //vetoed
0444:                pendingExpansionEvent = translateEvent(event, true);
0445:
0446:                log("treeWillExpand generated", pendingExpansionEvent);
0447:                inProgressEvent = event;
0448:            }
0449:
0450:            public void treeCollapsed(TreeExpansionEvent event) {
0451:                assert SwingUtilities.isEventDispatchThread();
0452:
0453:                log("treeExpanded", event);
0454:
0455:                //FixedHeightLayoutCache tests if the event is null.
0456:                //Don't know how it could be, but there's probably a reason...
0457:                if (event != null) {
0458:                    TreePath path = event.getPath();
0459:
0460:                    //Tell the layout about the change
0461:                    if (path != null && getTreePathSupport().isVisible(path)) {
0462:                        getLayout().setExpandedState(path, false);
0463:                    }
0464:                }
0465:
0466:                log("about to fire", pendingExpansionEvent);
0467:
0468:                //Now fire a change on the owning row so its display is updated (it
0469:                //may have just become an expandable node)
0470:                TreePath path = event.getPath();
0471:                int row = getLayout().getRowForPath(path);
0472:                TableModelEvent evt = new TableModelEvent(getModel(), row, row,
0473:                        0, TableModelEvent.UPDATE);
0474:                fireTableChange(new TableModelEvent[] { evt,
0475:                        pendingExpansionEvent });
0476:
0477:                pendingExpansionEvent = null;
0478:                inProgressEvent = null;
0479:            }
0480:
0481:            /** Updates the layout to mark the descendants of the events path as also
0482:             * expanded if they were the last it was expanded, then fires a table change. */
0483:            public void treeExpanded(TreeExpansionEvent event) {
0484:                assert SwingUtilities.isEventDispatchThread();
0485:
0486:                log("treeExpanded", event);
0487:
0488:                //Mysterious how the event could be null, but JTree tests it
0489:                //so we will too.
0490:                if (event != null) {
0491:                    updateExpandedDescendants(event.getPath());
0492:                }
0493:
0494:                log("about to fire", pendingExpansionEvent);
0495:
0496:                //Now fire a change on the owning row so its display is updated (it
0497:                //may have just become an expandable node)
0498:                TreePath path = event.getPath();
0499:                int row = getLayout().getRowForPath(path);
0500:                TableModelEvent evt = new TableModelEvent(getModel(), row, row,
0501:                        0, TableModelEvent.UPDATE);
0502:                fireTableChange(new TableModelEvent[] { evt,
0503:                        pendingExpansionEvent });
0504:
0505:                pendingExpansionEvent = null;
0506:                inProgressEvent = null;
0507:            }
0508:
0509:            /** Messaged if the tree expansion event (for which we will have already
0510:             * constructed a TableModelEvent) was vetoed;  disposes of the constructed
0511:             * TableModelEvent in that circumstance. */
0512:            public void treeExpansionVetoed(TreeExpansionEvent event,
0513:                    ExpandVetoException exception) {
0514:                assert SwingUtilities.isEventDispatchThread();
0515:
0516:                log("treeExpansionVetoed", exception);
0517:
0518:                //Make sure the event that was vetoed is the one we're interested in
0519:                if (event == inProgressEvent) {
0520:                    //If so, delete the expansion event we thought we were going
0521:                    //to use in treeExpanded/treeCollapsed, so that it doesn't
0522:                    //stick around forever holding references to objects from the
0523:                    //model
0524:                    pendingExpansionEvent = null;
0525:                    inProgressEvent = null;
0526:                }
0527:            }
0528:
0529:            //******************* Support routines for handling events ******************
0530:            //do I date myself by using the word "routines"? :-)
0531:
0532:            /** Re&euml;expand descendants of a newly expanded path which were
0533:             * expanded the last time their parent was expanded */
0534:            private void updateExpandedDescendants(TreePath path) {
0535:                getLayout().setExpandedState(path, true);
0536:
0537:                TreePath[] descendants = getTreePathSupport()
0538:                        .getExpandedDescendants(path);
0539:
0540:                if (descendants.length > 0) {
0541:                    for (int i = 0; i < descendants.length; i++) {
0542:                        getLayout().setExpandedState(descendants[i], true);
0543:                    }
0544:                }
0545:            }
0546:
0547:            //******************* Event translation routines ****************************
0548:
0549:            /** Creates a TableModelEvent identical to the original except that the
0550:             * column index has been shifted by +1.  This is used to refire events
0551:             * from the ProxyTableModel (generated by RowModel.setValueFor()) as 
0552:             * change events on the OutlineModel. */
0553:            private TableModelEvent translateEvent(TableModelEvent e) {
0554:                TableModelEvent nue = new TableModelEvent(getModel(), e
0555:                        .getFirstRow(), e.getLastRow(), e.getColumn() + 1, e
0556:                        .getType());
0557:                return nue;
0558:            }
0559:
0560:            /** Creates an identical TreeModelEvent with the model we are proxying
0561:             * as the event source */
0562:            private TreeModelEvent translateEvent(TreeModelEvent e) {
0563:                //Create a new TreeModelEvent with us as the source
0564:                TreeModelEvent nue = new TreeModelEvent(getModel(),
0565:                        e.getPath(), e.getChildIndices(), e.getChildren());
0566:                return nue;
0567:            }
0568:
0569:            /** Tranlates a TreeModelEvent into one or more contiguous TableModelEvents 
0570:             */
0571:            private TableModelEvent[] translateEvent(TreeModelEvent e, int type) {
0572:
0573:                TreePath path = e.getTreePath();
0574:                int row = getLayout().getRowForPath(path);
0575:
0576:                //If the node is not expanded, we simply fire a change
0577:                //event for the parent
0578:                boolean inClosedNode = !getLayout().isExpanded(path);
0579:                if (inClosedNode) {
0580:                    //If the node is closed, no expensive checks are needed - just
0581:                    //fire a change on the parent node in case it needs to update
0582:                    //its display
0583:                    if (row != -1) {
0584:                        switch (type) {
0585:                        case NODES_CHANGED:
0586:                        case NODES_INSERTED:
0587:                        case NODES_REMOVED:
0588:                            return new TableModelEvent[] { new TableModelEvent(
0589:                                    getModel(), row, row, 0,
0590:                                    TableModelEvent.UPDATE) };
0591:                        default:
0592:                            assert false : "Unknown event type " + type;
0593:                        }
0594:                    }
0595:                    //In a closed node that is not visible, no event needed
0596:                    return new TableModelEvent[0];
0597:                }
0598:
0599:                boolean discontiguous = isDiscontiguous(e);
0600:
0601:                Object[] blocks;
0602:                if (discontiguous) {
0603:                    blocks = getContiguousIndexBlocks(e, type == NODES_REMOVED);
0604:                    log("discontiguous " + types[type] + " event",
0605:                            blocks.length + " blocks");
0606:                } else {
0607:                    blocks = new Object[] { e.getChildIndices() };
0608:                }
0609:
0610:                TableModelEvent[] result = new TableModelEvent[blocks.length];
0611:                for (int i = 0; i < blocks.length; i++) {
0612:
0613:                    int[] currBlock = (int[]) blocks[i];
0614:                    switch (type) {
0615:                    case NODES_CHANGED:
0616:                        result[i] = createTableChangeEvent(e, currBlock);
0617:                        break;
0618:                    case NODES_INSERTED:
0619:                        result[i] = createTableInsertionEvent(e, currBlock);
0620:                        break;
0621:                    case NODES_REMOVED:
0622:                        result[i] = createTableDeletionEvent(e, currBlock);
0623:                        break;
0624:                    default:
0625:                        assert false : "Unknown event type: " + type;
0626:                    }
0627:                }
0628:                log("translateEvent", e);
0629:                log("generated table events", new Integer(result.length));
0630:                if (log) {
0631:                    for (int i = 0; i < result.length; i++) {
0632:                        log("  Event " + i, result[i]);
0633:                    }
0634:                }
0635:                return result;
0636:            }
0637:
0638:            /** Translates tree expansion event into an appropriate TableModelEvent
0639:             * indicating the number of rows added/removed at the appropriate index */
0640:            private TableModelEvent translateEvent(TreeExpansionEvent e,
0641:                    boolean expand) {
0642:                //PENDING:  This code should be profiled - the descendent paths search
0643:                //is not cheap, and it might be less expensive (at least if the table
0644:                //does not have expensive painting logic) to simply fire a generic
0645:                //"something changed" table model event and be done with it.
0646:
0647:                TreePath path = e.getPath();
0648:
0649:                //Add one because it is a child of the row.
0650:                int firstRow = getLayout().getRowForPath(path) + 1;
0651:                if (firstRow == -1) {
0652:                    //This does not mean nothing happened, it may just be that we are
0653:                    //a large model tree, and the FixedHeightLayoutCache says the
0654:                    //change happened in a row that is not showing.
0655:
0656:                    //TODO:  Just to make the table scrollbar adjust itself appropriately,
0657:                    //we may want to look up the number of children in the model and
0658:                    //fire an event that says that that many rows were added.  Waiting
0659:                    //to see if anybody actually will use this (i.e. fires changes in
0660:                    //offscreen nodes as a normal part of usage
0661:                    return null;
0662:                }
0663:
0664:                //Get all the expanded descendants of the path that was expanded/collapsed
0665:                TreePath[] paths = getTreePathSupport().getExpandedDescendants(
0666:                        path);
0667:
0668:                //Start with the number of children of whatever was expanded/collapsed
0669:                int count = getTreeModel().getChildCount(
0670:                        path.getLastPathComponent());
0671:
0672:                //Iterate any of the expanded children, adding in their child counts
0673:                for (int i = 0; i < paths.length; i++) {
0674:                    count += getTreeModel().getChildCount(
0675:                            paths[i].getLastPathComponent());
0676:                }
0677:
0678:                //Now we can calculate the last row affected for real
0679:                int lastRow = firstRow + count - 1;
0680:
0681:                //Construct a table model event reflecting this data
0682:                TableModelEvent result = new TableModelEvent(getModel(),
0683:                        firstRow, lastRow, TableModelEvent.ALL_COLUMNS,
0684:                        expand ? TableModelEvent.INSERT
0685:                                : TableModelEvent.DELETE);
0686:
0687:                return result;
0688:            }
0689:
0690:            /** Create a change TableModelEvent for the passed TreeModelEvent and the 
0691:             * contiguous subrange of the TreeModelEvent's getChildIndices() value */
0692:            private TableModelEvent createTableChangeEvent(TreeModelEvent e,
0693:                    int[] indices) {
0694:                TableModelEvent result = null;
0695:                TreePath path = e.getTreePath();
0696:                int row = getLayout().getRowForPath(path);
0697:
0698:                int first = indices[0];
0699:                int last = indices[indices.length - 1];
0700:
0701:                //TODO - does not need to be ALL_COLUMNS, but we need a way to determine
0702:                //which column index is the tree
0703:                result = new TableModelEvent(getModel(), first, last,
0704:                        TableModelEvent.ALL_COLUMNS, TableModelEvent.UPDATE);
0705:
0706:                return result;
0707:            }
0708:
0709:            /** Create an insertion TableModelEvent for the passed TreeModelEvent and the 
0710:             * contiguous subrange of the TreeModelEvent's getChildIndices() value */
0711:            private TableModelEvent createTableInsertionEvent(TreeModelEvent e,
0712:                    int[] indices) {
0713:                TableModelEvent result = null;
0714:
0715:                log("createTableInsertionEvent", e);
0716:
0717:                TreePath path = e.getTreePath();
0718:                int row = getLayout().getRowForPath(path);
0719:
0720:                boolean realInsert = getLayout().isExpanded(path);
0721:
0722:                if (realInsert) {
0723:                    if (indices.length == 1) {
0724:                        //Only one index to change, fire a simple event.  It
0725:                        //will be the first index in the array + the row +
0726:                        //1 because the 0th child of a node is 1 greater than
0727:                        //its row index
0728:                        int affectedRow = row + indices[0] + 1;
0729:                        result = new TableModelEvent(getModel(), affectedRow,
0730:                                affectedRow, TableModelEvent.ALL_COLUMNS,
0731:                                TableModelEvent.INSERT);
0732:
0733:                    } else {
0734:                        //Find the first and last indices.  Add one since it is at 
0735:                        //minimum the first index after the affected row, since it
0736:                        //is a child of it.
0737:                        int lowest = indices[0] + 1;
0738:                        int highest = indices[indices.length - 1] + 1;
0739:                        result = new TableModelEvent(getModel(), row + lowest,
0740:                                row + highest, TableModelEvent.ALL_COLUMNS,
0741:                                TableModelEvent.INSERT);
0742:
0743:                    }
0744:                } else {
0745:                    //Nodes were inserted in an unexpanded parent.  Just fire
0746:                    //a change for that row and column so that it gets repainted
0747:                    //in case the node there changed from leaf to non-leaf
0748:                    result = new TableModelEvent(getModel(), row, row,
0749:                            TableModelEvent.ALL_COLUMNS); //TODO - specify only the tree column
0750:                }
0751:                return result;
0752:            }
0753:
0754:            /** Create a deletion TableModelEvent for the passed TreeModelEvent and the 
0755:             * contiguous subrange of the TreeModelEvent's getChildIndices() value */
0756:            private TableModelEvent createTableDeletionEvent(TreeModelEvent e,
0757:                    int[] indices) {
0758:                TableModelEvent result = null;
0759:
0760:                log("createTableDeletionEvent "
0761:                        + Arrays.asList(toArrayOfInteger(indices)), e);
0762:
0763:                TreePath path = e.getTreePath();
0764:                int row = getLayout().getRowForPath(path);
0765:                if (row == -1) {
0766:                    //XXX could calculate based on last visible row?
0767:                    return null;
0768:                }
0769:
0770:                int countRemoved = indices.length;
0771:
0772:                //Get the subset of the children in the event that correspond
0773:                //to the passed indices
0774:                Object[] children = getChildrenForIndices(e, indices);
0775:
0776:                for (int i = 0; i < children.length; i++) {
0777:                    TreePath childPath = path.pathByAddingChild(children[i]);
0778:                    if (getTreePathSupport().isExpanded(childPath)) {
0779:
0780:                        int visibleChildren = getLayout().getVisibleChildCount(
0781:                                childPath);
0782:
0783:                        if (log) {
0784:                            log(childPath + " has ", new Integer(
0785:                                    visibleChildren));
0786:                        }
0787:
0788:                        countRemoved += visibleChildren;
0789:                    }
0790:                    getTreePathSupport().removePath(path);
0791:                }
0792:
0793:                //Add in the first index, and add one to it since the 0th
0794:                //will have the row index of its parent + 1
0795:                int firstRow = row + indices[0] + 1;
0796:
0797:                log("firstRow", new Integer(firstRow));
0798:                /*
0799:                if (countRemoved == 1) {
0800:                    System.err.println("Only one removed: " + (row + indices[0] + 1));
0801:                    result = new TableModelEvent (getModel(), firstRow, firstRow, 
0802:                        TableModelEvent.ALL_COLUMNS, 
0803:                        TableModelEvent.DELETE);
0804:                } else {
0805:                 */
0806:                System.err.println("Count removed is " + countRemoved);
0807:
0808:                int lastRow = firstRow + (countRemoved - 1);
0809:
0810:                System.err.println("TableModelEvent: fromRow: " + firstRow
0811:                        + " toRow: " + lastRow);
0812:
0813:                result = new TableModelEvent(getModel(), firstRow, lastRow,
0814:                        TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE);
0815:                //}
0816:
0817:                /* //old code
0818:                
0819:                    //Okay, one or more nodes was removed.  The event's tree path
0820:                    //will be the parent.  Now we need to find out about any children
0821:                    //that were also removed so we can create a TreeModelEvent with
0822:                    //the right number of removed rows.
0823:                    
0824:                    //Note there is a slight impedance mismatch between TreeModel and
0825:                    //TableModel here - if we're using a large model layout cache,
0826:                    //we don't actually know what was offscreen - the data is already
0827:                    //gone from the model, so even if we know it was expanded, we
0828:                    //can't find out how many children it had.
0829:                    
0830:                    //The only thing this really affects is the scrollbar, and in
0831:                    //fact, the standard JTable UIs will update it correctly, since
0832:                    //the scrollbar will read getRowCount() to calculate its position.
0833:                    //In theory, this could break on a hyper-efficient TableUI that
0834:                    //attempted to manage scrollbar position *only* based on the
0835:                    //content of table model events.  That's pretty unlikely; but if
0836:                    //it happens, the solution is for Outline.getPreferredSize() to
0837:                    //proxy the preferred size from the layout cache
0838:                    
0839:                    TreePath path = e.getTreePath();
0840:                    boolean lastRemoveWasExpanded = getTreePathSupport().isExpanded(path);
0841:                    int countRemoved = 1;
0842:                    
0843:                    //See if it's expanded - if it wasn't we're just going to blow
0844:                    //away one row anyway
0845:                    if (lastRemoveWasExpanded) {
0846:                        Object[] kids = e.getChildren();
0847:                        
0848:                        //TranslateEvent uses countRemoved to set the TableModelEvent
0849:                        countRemoved = kids.length;
0850:                        
0851:                        //Iterate the removed children
0852:                        for (int i=0; i < kids.length; i++) {
0853:                            //Get the child's path
0854:                            TreePath childPath = path.pathByAddingChild(kids[i]);
0855:                            
0856:                            //If it's not expanded, we don't care
0857:                            if (getTreePathSupport().isExpanded(childPath)) {
0858:                                //Find the number of *visible* children.  This may not
0859:                                //be all the children, but it's the best information we have.
0860:                                int visibleChildren = 
0861:                                    getLayout().getVisibleChildCount(childPath);
0862:
0863:                                //add in the number of visible children
0864:                                countRemoved += visibleChildren;
0865:                            }
0866:                            //Kill any references to the dead path to avoid memory leaks
0867:                            getTreePathSupport().removePath(childPath);
0868:                        }
0869:                    }
0870:                    
0871:                    //Tell the layout what happened, now that we've mined it for data
0872:                    //about the visible children of the removed paths
0873:                    getLayout().treeNodesRemoved(e);        
0874:                    
0875:                        boolean realRemove = lastRemoveWasExpanded;//getLayout().isExpanded(path);
0876:                        if (realRemove) {
0877:                            System.err.println("Nodes removed from open countainer");
0878:                            int[] indices = e.getChildIndices();
0879:                            
0880:                            //Comments in FixedHeightLayoutCache suggest we cannot
0881:                            //assume array is sorted, though it should be
0882:                            Arrays.sort(indices);
0883:                            if (indices.length == 0) {
0884:                                //well, that's a little weird
0885:                                return null;
0886:                            } else if (countRemoved == 1) {
0887:                                System.err.println("Only one removed: " + (row + indices[0] + 1));
0888:                                return new TableModelEvent (this, row + indices[0] + 1,
0889:                                    row + indices[0] + 1, TableModelEvent.ALL_COLUMNS, 
0890:                                    TableModelEvent.DELETE);
0891:                            }
0892:                            System.err.println("Count removed is " + countRemoved);
0893:                            
0894:                            //Add in the first index, and add one to it since the 0th
0895:                            //will have the row index of its parent + 1
0896:                            int firstRow = row + indices[0] + 1;
0897:                            int lastRow = firstRow + (countRemoved - 1);
0898:                            
0899:                            System.err.println("TableModelEvent: fromRow: " + firstRow + " toRow: " + lastRow);
0900:                             
0901:                            return new TableModelEvent (this, firstRow, lastRow,
0902:                                TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE);
0903:                        } else {
0904:                            System.err.println("Nodes removed from a closed container. Change for row " + row);
0905:                            //Nodes were removed in an unexpanded parent.  Just fire
0906:                            //a change for that row and column so that it gets repainted
0907:                            //in case the node there changed from leaf to non-leaf
0908:                            TableModelEvent evt = new TableModelEvent (this, row, row, 0); //XXX 0 may not be tree column
0909:                            System.err.println(" Returning " + evt);
0910:                            return evt;
0911:                        }        
0912:                 */
0913:
0914:                return result;
0915:            }
0916:
0917:            //**************** Static utility routines *****************************    
0918:
0919:            /** Determine if the indices referred to by a TreeModelEvent are
0920:             * contiguous.  If they are not, we will need to generate multiple
0921:             * TableModelEvents for each contiguous block */
0922:            private static boolean isDiscontiguous(TreeModelEvent e) {
0923:                int[] indices = e.getChildIndices();
0924:                if (indices.length == 1) {
0925:                    return false;
0926:                }
0927:                Arrays.sort(indices);
0928:                int lastVal = indices[0];
0929:                for (int i = 1; i < indices.length; i++) {
0930:                    if (indices[i] != lastVal + 1) {
0931:                        return true;
0932:                    } else {
0933:                        lastVal++;
0934:                    }
0935:                }
0936:                return false;
0937:            }
0938:
0939:            /** Returns an array of int[]s each one representing a contiguous set of 
0940:             * indices in the tree model events child indices - each of which can be
0941:             * fired as a single TableModelEvent.  The length of the return value is
0942:             * the number of TableModelEvents required to represent this TreeModelEvent.
0943:             * If reverseOrder is true (needed for remove events, where the last indices
0944:             * must be removed first or the indices of later removals will be changed),
0945:             * the returned int[]s will be sorted in reverse order, and the order in
0946:             * which they are returned will also be from highest to lowest. */
0947:            private static Object[] getContiguousIndexBlocks(TreeModelEvent e,
0948:                    boolean reverseOrder) {
0949:                int[] indices = e.getChildIndices();
0950:
0951:                //Quick check if there's only one index
0952:                if (indices.length == 1) {
0953:                    return new Object[] { indices };
0954:                }
0955:
0956:                //The array of int[]s we'll return
0957:                ArrayList al = new ArrayList();
0958:
0959:                //Sort the indices as requested
0960:                if (reverseOrder) {
0961:                    inverseSort(indices);
0962:                } else {
0963:                    Arrays.sort(indices);
0964:                }
0965:
0966:                //The starting block
0967:                ArrayList currBlock = new ArrayList(indices.length / 2);
0968:                al.add(currBlock);
0969:
0970:                //The value we'll check against the previous one to detect the
0971:                //end of contiguous segment
0972:                int lastVal = -1;
0973:
0974:                //Iterate the indices
0975:                for (int i = 0; i < indices.length; i++) {
0976:                    if (i != 0) {
0977:                        //See if we've hit a discontinuity
0978:                        boolean newBlock = reverseOrder ? indices[i] != lastVal - 1
0979:                                : indices[i] != lastVal + 1;
0980:
0981:                        if (newBlock) {
0982:                            currBlock = new ArrayList(indices.length - 1);
0983:                            al.add(currBlock);
0984:                        }
0985:                    }
0986:                    currBlock.add(new Integer(indices[i]));
0987:                    lastVal = indices[i];
0988:                }
0989:
0990:                for (int i = 0; i < al.size(); i++) {
0991:                    ArrayList curr = (ArrayList) al.get(i);
0992:                    Integer[] ints = (Integer[]) curr.toArray(new Integer[0]);
0993:
0994:                    al.set(i, toArrayOfInt(ints));
0995:                }
0996:
0997:                return al.toArray();
0998:            }
0999:
1000:            /** Get the children from a TreeModelEvent associated with the set of
1001:             * indices passed. */
1002:            private Object[] getChildrenForIndices(TreeModelEvent e,
1003:                    int[] indices) {
1004:                //XXX performance - better way to do this may be to have
1005:                //getContinguousIndexBlocks instead construct sub-treemodelevents - 
1006:                //that would save having to do these iterations later to extract the
1007:                //children.
1008:
1009:                //At the same time, discontiguous child removals are relatively rare
1010:                //events - optimizing them heavily may not be a good use of time.
1011:                Object[] children = e.getChildren();
1012:                int[] allIndices = e.getChildIndices();
1013:
1014:                ArrayList al = new ArrayList();
1015:
1016:                for (int i = 0; i < indices.length; i++) {
1017:                    int pos = Arrays.binarySearch(allIndices, indices[i]);
1018:                    if (pos > -1) {
1019:                        al.add(children[pos]);
1020:                    }
1021:                    if (al.size() == indices.length) {
1022:                        break;
1023:                    }
1024:                }
1025:                return al.toArray();
1026:            }
1027:
1028:            /** Converts an Integer[] to an int[] */
1029:            private static int[] toArrayOfInt(Integer[] ints) {
1030:                int[] result = new int[ints.length];
1031:                for (int i = 0; i < ints.length; i++) {
1032:                    result[i] = ints[i].intValue();
1033:                }
1034:                return result;
1035:            }
1036:
1037:            /** Converts an Integer[] to an int[] */
1038:            //XXX deleteme - used for debug logging only
1039:            private static Integer[] toArrayOfInteger(int[] ints) {
1040:                Integer[] result = new Integer[ints.length];
1041:                for (int i = 0; i < ints.length; i++) {
1042:                    result[i] = new Integer(ints[i]);
1043:                }
1044:                return result;
1045:            }
1046:
1047:            /** Sort an array of ints from highest to lowest */
1048:            private static void inverseSort(int[] array) {
1049:                //XXX replace with a proper sort algorithm at some point -
1050:                //this is brute force
1051:                for (int i = 0; i < array.length; i++) {
1052:                    array[i] *= -1;
1053:                }
1054:                Arrays.sort(array);
1055:                for (int i = 0; i < array.length; i++) {
1056:                    array[i] *= -1;
1057:                }
1058:            }
1059:
1060:            private static String tableModelEventToString(TableModelEvent e) {
1061:                StringBuffer sb = new StringBuffer();
1062:                sb.append("TableModelEvent ");
1063:                switch (e.getType()) {
1064:                case TableModelEvent.INSERT:
1065:                    sb.append("insert ");
1066:                    break;
1067:                case TableModelEvent.DELETE:
1068:                    sb.append("delete ");
1069:                    break;
1070:                case TableModelEvent.UPDATE:
1071:                    sb.append("update ");
1072:                    break;
1073:                default:
1074:                    sb.append("Unknown type " + e.getType());
1075:                }
1076:                sb.append("from ");
1077:                switch (e.getFirstRow()) {
1078:                case TableModelEvent.HEADER_ROW:
1079:                    sb.append("header row ");
1080:                    break;
1081:                default:
1082:                    sb.append(e.getFirstRow());
1083:                    sb.append(' ');
1084:                }
1085:                sb.append("to ");
1086:                sb.append(e.getLastRow());
1087:                sb.append(" column ");
1088:                switch (e.getColumn()) {
1089:                case TableModelEvent.ALL_COLUMNS:
1090:                    sb.append("ALL_COLUMNS");
1091:                    break;
1092:                default:
1093:                    sb.append(e.getColumn());
1094:                }
1095:                return sb.toString();
1096:            }
1097:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.