Source Code Cross Referenced for ExplorerManager.java in  » IDE-Netbeans » openide » org » openide » explorer » 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 » openide » org.openide.explorer 
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-2006 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:        package org.openide.explorer;
0042:
0043:        import org.openide.nodes.*;
0044:        import org.openide.util.*;
0045:        import org.openide.util.io.SafeException;
0046:
0047:        import java.awt.Component;
0048:
0049:        import java.beans.*;
0050:
0051:        import java.io.*;
0052:
0053:        import java.util.*;
0054:
0055:        /**
0056:         * Manages a selection and root context for a (set of) Explorer view(s).  The
0057:         * views should register their {@link java.beans.VetoableChangeListener}s and
0058:         * {@link java.beans.PropertyChangeListener}s at the
0059:         * <code>ExplorerManager</code> of the Explorer they belong to
0060:         * (usually found in AWT hierarchy using {@link ExplorerManager#find}. 
0061:         * The manager is then the mediator that keeps the shared state, 
0062:         * notifies {@link PropertyChangeListener}s and {@link VetoableChangeListener}s
0063:         * about changes and allows views to call its setter methods to incluence
0064:         * the root of the visible hierarchy using {@link #setRootContext}, the 
0065:         * set of selected nodes using {@link #setSelectedNodes} and also the 
0066:         * explored context (useful for {@link org.openide.explorer.view.ListView} for 
0067:         * example) using {@link #setExploredContext}.
0068:         * <p>
0069:         * This class interacts with Swing components in the
0070:         * <code>org.openide.explorer.view</code> package and as such it shall be
0071:         * used according to Swing threading model.
0072:         * <p>
0073:         * To provide an {@link ExplorerManager} from your component just let your
0074:         * component implement {@link Provider} as described at {@link ExplorerUtils}.
0075:         *
0076:         * <P>Deserialization may throw {@link SafeException} if the contexts cannot be
0077:         * restored correctly, but the stream is uncorrupted.
0078:         *
0079:         *
0080:         * @author Ian Formanek, Petr Hamernik, Jaroslav Tulach, Jan Jancura,
0081:         *         Jesse Glick
0082:         * @see ExplorerUtils
0083:         * @see org.openide.explorer.view.TreeView
0084:         * @see org.openide.explorer.view.ListView
0085:         */
0086:        public final class ExplorerManager extends Object implements 
0087:                Serializable, Cloneable {
0088:            /** generated Serialized Version UID */
0089:            static final long serialVersionUID = -4330330689803575792L;
0090:
0091:            /** Name of property for the root context. */
0092:            public static final String PROP_ROOT_CONTEXT = "rootContext"; // NOI18N
0093:
0094:            /** Name of property for the explored context. */
0095:            public static final String PROP_EXPLORED_CONTEXT = "exploredContext"; // NOI18N
0096:
0097:            /** Name of property for the node selection. */
0098:            public static final String PROP_SELECTED_NODES = "selectedNodes"; // NOI18N
0099:
0100:            /** Name of property for change in a node. */
0101:            public static final String PROP_NODE_CHANGE = "nodeChange"; // NOI18N
0102:
0103:            /** Request processor for managing selections.
0104:             */
0105:            static RequestProcessor selectionProcessor;
0106:
0107:            /** Delay for coalescing events before removing destroyed nodes from
0108:                the selection.
0109:             */
0110:            private static final int SELECTION_SYNC_DELAY = 200;
0111:
0112:            /** defines serialized fields for the manager.
0113:             */
0114:            private static final ObjectStreamField[] serialPersistentFields = {
0115:                    new ObjectStreamField("root", Node.Handle.class), // NOI18N
0116:                    new ObjectStreamField("rootName", String.class), // NOI18N
0117:                    new ObjectStreamField("explored", String[].class), // NOI18N
0118:                    new ObjectStreamField("selected", Object[].class) // NOI18N
0119:            };
0120:
0121:            /** The support for VetoableChangeEvent */
0122:            private transient VetoableChangeSupport vetoableSupport;
0123:
0124:            /** The support for PropertyChangeEvent */
0125:            private transient PropertyChangeSupport propertySupport;
0126:
0127:            /** The current root context */
0128:            private Node rootContext;
0129:
0130:            /** The current explored context */
0131:            private Node exploredContext;
0132:
0133:            /** The currently selected beans */
0134:            private Node[] selectedNodes;
0135:
0136:            /** listener to destroy of root node */
0137:            private transient Listener listener;
0138:
0139:            /** weak listener */
0140:            private transient NodeListener weakListener;
0141:
0142:            /** Task that removes manages node selection issues.
0143:             */
0144:            private RequestProcessor.Task selectionSyncTask;
0145:
0146:            /** Actions factory provided for this explorer manager */
0147:            private ExplorerActionsImpl actions;
0148:
0149:            /** Construct a new manager. */
0150:            public ExplorerManager() {
0151:                init();
0152:            }
0153:
0154:            /** Initializes the nodes.
0155:             */
0156:            private void init() {
0157:                exploredContext = rootContext = Node.EMPTY;
0158:                selectedNodes = new Node[0];
0159:                listener = new Listener();
0160:                weakListener = NodeOp.weakNodeListener(listener, null);
0161:            }
0162:
0163:            /** Clones the manager.
0164:             * @return manager with the same settings like this one
0165:             */
0166:            @Override
0167:            public ExplorerManager clone() {
0168:                ExplorerManager em = new ExplorerManager();
0169:                em.rootContext = rootContext;
0170:                em.exploredContext = exploredContext;
0171:                em.selectedNodes = selectedNodes;
0172:
0173:                return em;
0174:            }
0175:
0176:            /** Get the set of selected nodes.
0177:             * @return the selected nodes; empty (not <code>null</code>) if none are selected
0178:             */
0179:            public Node[] getSelectedNodes() {
0180:                return selectedNodes;
0181:            }
0182:
0183:            // compare two arrays of nodes in respect to a path to root
0184:            private boolean equalNodes(Node[] arr1, Node[] arr2) {
0185:                // generic tests
0186:                if (!Arrays.equals(arr1, arr2)) {
0187:                    return false;
0188:                }
0189:
0190:                if ((arr1 == null) || (arr1.length == 0)) {
0191:                    return true;
0192:                }
0193:
0194:                // compare paths from each node to root
0195:                int i = 0;
0196:
0197:                while ((i < arr1.length)
0198:                        && Arrays.equals(NodeOp.createPath(arr1[i], null),
0199:                                NodeOp.createPath(arr2[i], null))) {
0200:                    i++;
0201:                }
0202:
0203:                return i == arr1.length;
0204:            }
0205:
0206:            /** Set the set of selected nodes.
0207:             * @param value the nodes to select; empty (not <code>null</code>) if none are to be selected
0208:             * @exception PropertyVetoException when the given nodes cannot be selected
0209:             * @throws IllegalArgumentException if <code>null</code> is given, or if any elements
0210:             *                                  of the selection are not within the current root context
0211:             */
0212:            public final void setSelectedNodes(final Node[] value)
0213:                    throws PropertyVetoException {
0214:                class AtomicSetSelectedNodes implements  Runnable {
0215:                    public PropertyVetoException veto;
0216:                    private boolean doFire;
0217:                    private Node[] oldValue;
0218:
0219:                    /** @return false if no further processing is needed */
0220:                    private boolean checkArgumentIsValid() {
0221:                        if (value == null) {
0222:                            throw new IllegalArgumentException(
0223:                                    getString("EXC_NodeCannotBeNull"));
0224:                        }
0225:
0226:                        if (equalNodes(value, selectedNodes)) {
0227:                            return false;
0228:                        }
0229:
0230:                        for (int i = 0; i < value.length; i++) {
0231:                            if (value[i] == null) {
0232:                                throw new IllegalArgumentException(
0233:                                        getString("EXC_NoElementOfNodeSelectionMayBeNull"));
0234:                            }
0235:
0236:                            checkUnderRoot(value[i],
0237:                                    "EXC_NodeSelectionCannotContainNodes");
0238:                        }
0239:
0240:                        if ((value.length != 0) && (vetoableSupport != null)) {
0241:                            try {
0242:                                // we send the vetoable change event only for non-empty selections
0243:                                vetoableSupport.fireVetoableChange(
0244:                                        PROP_SELECTED_NODES, selectedNodes,
0245:                                        value);
0246:                            } catch (PropertyVetoException ex) {
0247:                                veto = ex;
0248:
0249:                                return false;
0250:                            }
0251:                        }
0252:
0253:                        return true;
0254:                    }
0255:
0256:                    private void updateSelection() {
0257:                        oldValue = selectedNodes;
0258:                        addRemoveListeners(false);
0259:                        selectedNodes = value;
0260:                        addRemoveListeners(true);
0261:
0262:                        doFire = true;
0263:                    }
0264:
0265:                    public void fire() {
0266:                        if (doFire) {
0267:                            fireInAWT(PROP_SELECTED_NODES, oldValue,
0268:                                    selectedNodes);
0269:                        }
0270:                    }
0271:
0272:                    public void run() {
0273:                        if (checkArgumentIsValid()) {
0274:                            updateSelection();
0275:                        }
0276:                    }
0277:                }
0278:
0279:                AtomicSetSelectedNodes setNodes = new AtomicSetSelectedNodes();
0280:                Children.MUTEX.readAccess(setNodes);
0281:                setNodes.fire();
0282:
0283:                if (setNodes.veto != null) {
0284:                    throw setNodes.veto;
0285:                }
0286:            }
0287:
0288:            /** Get the explored context.
0289:             * <p>The "explored context" is not as frequently used as the node selection;
0290:             * generally it refers to a parent node which contains all of the things
0291:             * being displayed at this moment. For <code>BeanTreeView</code> this is
0292:             * irrelevant, but <code>ContextTreeView</code> uses it (in lieu of the node
0293:             * selection) and for <code>IconView</code> it is important (the node
0294:             * whose children are visible, i.e. the "background" of the icon view).
0295:             * @return the node being explored, or <code>null</code>
0296:             */
0297:            public final Node getExploredContext() {
0298:                return exploredContext;
0299:            }
0300:
0301:            /** Set the explored context.
0302:             * The node selection will be cleared as well.
0303:             * @param value the new node to explore, or <code>null</code> if none should be explored.
0304:             * @throws IllegalArgumentException if the node is not within the current root context in the node hierarchy
0305:             */
0306:            public final void setExploredContext(Node value) {
0307:                setExploredContext(value, new Node[0]);
0308:            }
0309:
0310:            /** Set the explored context.
0311:             * The node selection will be changed as well. Note: node selection cannot be
0312:             * vetoed if calling this method. It is generally better to call setExploredContextAndSelection.
0313:             * @param value the new node to explore, or <code>null</code> if none should be explored.
0314:             * @throws IllegalArgumentException if the node is not within the current root context in the node hierarchy
0315:             */
0316:            public final void setExploredContext(final Node value,
0317:                    final Node[] selection) {
0318:                class SetExploredContext implements  Runnable {
0319:                    boolean doFire;
0320:                    Node oldValue;
0321:
0322:                    public void run() {
0323:                        // handles nulls correctly:
0324:                        if (Utilities.compareObjects(value, exploredContext)) {
0325:                            setSelectedNodes0(selection);
0326:
0327:                            return;
0328:                        }
0329:
0330:                        checkUnderRoot(value,
0331:                                "EXC_ContextMustBeWithinRootContext");
0332:                        setSelectedNodes0(selection);
0333:
0334:                        oldValue = exploredContext;
0335:                        addRemoveListeners(false);
0336:                        exploredContext = value;
0337:                        addRemoveListeners(true);
0338:
0339:                        doFire = true;
0340:                    }
0341:
0342:                    public void fire() {
0343:                        if (doFire) {
0344:                            fireInAWT(PROP_EXPLORED_CONTEXT, oldValue, value);
0345:                        }
0346:                    }
0347:                }
0348:
0349:                SetExploredContext set = new SetExploredContext();
0350:                Children.MUTEX.readAccess(set);
0351:                set.fire();
0352:            }
0353:
0354:            /** Set the explored context and selected nodes. If the change in selected nodes is vetoed,
0355:             * PropertyVetoException is rethrown from here.
0356:             * @param value the new node to explore, or <code>null</code> if none should be explored.
0357:             * @param selection the new nodes to be selected
0358:             * @throws IllegalArgumentException if the node is not within the current root context in the node hierarchy
0359:             * @throws PropertyVetoExcepion if listeners attached to this explorer manager do so
0360:             */
0361:            public final void setExploredContextAndSelection(final Node value,
0362:                    final Node[] selection) throws PropertyVetoException {
0363:                class SetExploredContextAndSelection implements  Runnable {
0364:                    public PropertyVetoException veto;
0365:                    private boolean doFire;
0366:                    private Object oldValue;
0367:
0368:                    public void run() {
0369:                        try {
0370:                            // handles nulls correctly:
0371:                            if (Utilities
0372:                                    .compareObjects(value, exploredContext)) {
0373:                                setSelectedNodes(selection);
0374:
0375:                                return;
0376:                            }
0377:
0378:                            checkUnderRoot(value,
0379:                                    "EXC_ContextMustBeWithinRootContext");
0380:                            setSelectedNodes(selection);
0381:
0382:                            oldValue = exploredContext;
0383:                            addRemoveListeners(false);
0384:                            exploredContext = value;
0385:                            addRemoveListeners(true);
0386:
0387:                            doFire = true;
0388:                        } catch (PropertyVetoException ex) {
0389:                            veto = ex;
0390:                        }
0391:                    }
0392:
0393:                    public void fire() {
0394:                        if (doFire) {
0395:                            fireInAWT(PROP_EXPLORED_CONTEXT, oldValue,
0396:                                    exploredContext);
0397:                        }
0398:                    }
0399:                }
0400:
0401:                SetExploredContextAndSelection set = new SetExploredContextAndSelection();
0402:                Children.MUTEX.readAccess(set);
0403:                set.fire();
0404:
0405:                if (set.veto != null) {
0406:                    throw set.veto;
0407:                }
0408:            }
0409:
0410:            final void addRemoveListeners(boolean add) {
0411:                Map<Node, Void> collect = new IdentityHashMap<Node, Void>(333);
0412:
0413:                collectNodes(exploredContext, collect);
0414:                for (Node n : selectedNodes) {
0415:                    collectNodes(n, collect);
0416:                }
0417:
0418:                for (Node n : collect.keySet()) {
0419:                    if (add) {
0420:                        n.addNodeListener(weakListener);
0421:                    } else {
0422:                        n.removeNodeListener(weakListener);
0423:                    }
0424:                }
0425:            }
0426:
0427:            private final void collectNodes(Node n, Map<Node, ?> collect) {
0428:                assert Children.MUTEX.isReadAccess();
0429:                while (n != null && n != rootContext) {
0430:                    collect.put(n, null);
0431:                    n = n.getParentNode();
0432:                }
0433:            }
0434:
0435:            /** Sets selected nodes and handles PropertyVetoException */
0436:            final void setSelectedNodes0(Node[] nodes) {
0437:                try {
0438:                    setSelectedNodes(nodes);
0439:                } catch (PropertyVetoException e) {
0440:                }
0441:            }
0442:
0443:            /** Get the root context.
0444:             * <p>The "root context" is simply the topmost node that this explorer can
0445:             * display or manipulate. For <code>BeanTreeView</code>, this would mean
0446:             * the root node of the tree. For e.g. <code>IconView</code>, this would
0447:             * mean the uppermost possible node that that icon view could display;
0448:             * while the explored context would change at user prompting via the
0449:             * up button and clicking on subfolders, the root context would be fixed
0450:             * by the code displaying the explorer.
0451:             * @return the root context node
0452:             */
0453:            public final Node getRootContext() {
0454:                return rootContext;
0455:            }
0456:
0457:            /** Set the root context.
0458:             * The explored context will be set to the new root context as well.
0459:             * If any of the selected nodes are not inside it, the selection will be cleared.
0460:             * @param value the new node to serve as a root
0461:             * @throws IllegalArgumentException if it is <code>null</code>
0462:             */
0463:            public final void setRootContext(final Node value) {
0464:                if (value == null) {
0465:                    throw new IllegalArgumentException(
0466:                            getString("EXC_CannotHaveNullRootContext"));
0467:                }
0468:
0469:                if (rootContext.equals(value)) {
0470:                    return;
0471:                }
0472:
0473:                class SetRootContext implements  Runnable {
0474:                    public void run() {
0475:                        addRemoveListeners(false);
0476:                        Node oldValue = rootContext;
0477:                        rootContext = value;
0478:
0479:                        oldValue.removeNodeListener(weakListener);
0480:                        rootContext.addNodeListener(weakListener);
0481:
0482:                        fireInAWT(PROP_ROOT_CONTEXT, oldValue, rootContext);
0483:
0484:                        Node[] newselection = getSelectedNodes();
0485:
0486:                        if (!areUnderTarget(newselection, rootContext)) {
0487:                            newselection = new Node[0];
0488:                        }
0489:                        setExploredContext(rootContext, newselection);
0490:                    }
0491:                }
0492:
0493:                SetRootContext run = new SetRootContext();
0494:                Children.MUTEX.readAccess(run);
0495:            }
0496:
0497:            /** @return true iff all nodes are under the target node */
0498:            private boolean areUnderTarget(Node[] nodes, Node target) {
0499:                bigloop: for (int i = 0; i < nodes.length; i++) {
0500:                    Node node = nodes[i];
0501:
0502:                    while (node != null) {
0503:                        if (node.equals(target)) {
0504:                            continue bigloop;
0505:                        }
0506:
0507:                        node = node.getParentNode();
0508:                    }
0509:
0510:                    return false;
0511:                }
0512:
0513:                return true;
0514:            }
0515:
0516:            /** Add a <code>PropertyChangeListener</code> to the listener list.
0517:             * @param l the listener to add
0518:             */
0519:            public synchronized void addPropertyChangeListener(
0520:                    PropertyChangeListener l) {
0521:                if (propertySupport == null) {
0522:                    propertySupport = new PropertyChangeSupport(this );
0523:                }
0524:
0525:                propertySupport.addPropertyChangeListener(l);
0526:            }
0527:
0528:            /** Remove a <code>PropertyChangeListener</code> from the listener list.
0529:             * @param l the listener to remove
0530:             */
0531:            public synchronized void removePropertyChangeListener(
0532:                    PropertyChangeListener l) {
0533:                if (propertySupport != null) {
0534:                    propertySupport.removePropertyChangeListener(l);
0535:                }
0536:            }
0537:
0538:            /** Add a <code>VetoableListener</code> to the listener list.
0539:             * @param l the listener to add
0540:             */
0541:            public synchronized void addVetoableChangeListener(
0542:                    VetoableChangeListener l) {
0543:                if (vetoableSupport == null) {
0544:                    vetoableSupport = new VetoableChangeSupport(this );
0545:                }
0546:
0547:                vetoableSupport.addVetoableChangeListener(l);
0548:            }
0549:
0550:            /** Remove a <code>VetoableChangeListener</code> from the listener list.
0551:             * @param l the listener to remove
0552:             */
0553:            public synchronized void removeVetoableChangeListener(
0554:                    VetoableChangeListener l) {
0555:                if (vetoableSupport != null) {
0556:                    vetoableSupport.removeVetoableChangeListener(l);
0557:                }
0558:            }
0559:
0560:            /** Checks whether given Node is a subnode of rootContext.
0561:             * @return true if specified Node is under current rootContext
0562:             */
0563:            private boolean isUnderRoot(Node node) {
0564:                while (node != null) {
0565:                    if (node.equals(rootContext)) {
0566:                        return true;
0567:                    }
0568:
0569:                    node = node.getParentNode();
0570:                }
0571:
0572:                return false;
0573:            }
0574:
0575:            /** Checks whether given Node is a subnode of rootContext.
0576:             * and throws IllegalArgumentException if not.
0577:             */
0578:            private void checkUnderRoot(Node value, String errorKey) {
0579:                if ((value != null) && !isUnderRoot(value)) {
0580:                    throw new IllegalArgumentException(NbBundle.getMessage(
0581:                            ExplorerManager.class, errorKey, value
0582:                                    .getDisplayName(), rootContext
0583:                                    .getDisplayName()));
0584:                }
0585:            }
0586:
0587:            /** Waits till all async processing is finished
0588:             */
0589:            final void waitFinished() {
0590:                if (selectionSyncTask != null) {
0591:                    selectionSyncTask.waitFinished();
0592:                }
0593:            }
0594:
0595:            /** serializes object
0596:             * @serialData the following objects are written in sequence:
0597:             * <ol>
0598:             * <li> a Node.Handle for the root context; may be null if root context
0599:             *      is not persistable
0600:             * <li> the display name of the root context (to give nicer error messages
0601:             *      later on)
0602:             * <li> the path from root context to explored context; null if no explored
0603:             *      context or no such path
0604:             * <li> for every element of node selection, path from root context to that node;
0605:             *      null if no such path
0606:             * <li> null to terminate
0607:             * </ol>
0608:             * Note that if the root context handle is null, the display name is still written
0609:             * but the paths to explored context and node selection are not written, the stream
0610:             * ends there.
0611:             */
0612:            private void writeObject(ObjectOutputStream os) throws IOException {
0613:                // indication that we gonna use put fields and not the old method.
0614:                os.writeObject(this );
0615:
0616:                ObjectOutputStream.PutField fields = os.putFields();
0617:
0618:                // [PENDING] is this method (and readObject) always called from within
0619:                // the Nodes mutex? It should be!
0620:                //System.err.println("rootContext: " + rootContext);
0621:                Node.Handle rCH = rootContext.getHandle();
0622:                fields.put("root", rCH); // NOI18N
0623:
0624:                //System.err.println("writing: " + rCH);
0625:                fields.put("rootName", rootContext.getDisplayName()); // NOI18N
0626:
0627:                if (rCH != null) {
0628:                    // Note that explored context may be null (this is valid).
0629:                    // Also, it may have happened that the hierarchy changed so that
0630:                    // the explored context is *no longer* under the root (though it was at
0631:                    // the time these things were set up). In this case, we cannot store the
0632:                    // path. Caution: NodeOp.createPath will create a path to a root (parentless)
0633:                    // node even if you specify a non-null root, if the first arg is not a child!
0634:                    String[] explored;
0635:
0636:                    if (exploredContext == null) {
0637:                        explored = null;
0638:                    } else if (isUnderRoot(exploredContext)) {
0639:                        explored = NodeOp.createPath(exploredContext,
0640:                                rootContext);
0641:                    } else {
0642:                        explored = null;
0643:                    }
0644:
0645:                    fields.put("explored", explored); // NOI18N
0646:
0647:                    List<String[]> selected = new LinkedList<String[]>();
0648:
0649:                    for (int i = 0; i < selectedNodes.length; i++) {
0650:                        if (isUnderRoot(selectedNodes[i])) {
0651:                            selected.add(NodeOp.createPath(selectedNodes[i],
0652:                                    rootContext));
0653:                        }
0654:                    }
0655:
0656:                    fields.put("selected", selected.toArray()); // NOI18N
0657:                }
0658:
0659:                os.writeFields();
0660:            }
0661:
0662:            /** Deserializes the view and initializes it
0663:             * @serialData see writeObject
0664:             */
0665:            private void readObject(ObjectInputStream ois) throws IOException,
0666:                    ClassNotFoundException {
0667:                // perform initialization
0668:                init();
0669:
0670:                // read the first object in the stream
0671:                Object firstObject = ois.readObject();
0672:
0673:                if (firstObject != this ) {
0674:                    // use old version of deserialization
0675:                    readObjectOld((Node.Handle) firstObject, ois);
0676:
0677:                    return;
0678:                }
0679:
0680:                // work with get fields
0681:                ObjectInputStream.GetField fields = ois.readFields();
0682:
0683:                // read root handle
0684:                Node.Handle h = (Node.Handle) fields.get("root", null); // NOI18N
0685:
0686:                //System.err.println("reading: " + h);
0687:                final String rootName = (String) fields.get("rootName", null); // NOI18N
0688:
0689:                //System.err.println("reading: " + rootName);
0690:                if (h == null) {
0691:                    // Cancel deserialization (e.g. of the ExplorerPanel window) in case the
0692:                    // root handle was not persistent:
0693:                    throw new SafeException(
0694:                            new IOException(
0695:                                    "Could not restore Explorer window; the root node \""
0696:                                            + rootName
0697:                                            + "\" is not persistent; override Node.getHandle to fix")); // NOI18N
0698:                } else {
0699:                    String[] exploredCtx = (String[]) fields.get("explored",
0700:                            null); // NOI18N
0701:                    Object[] selPaths = (Object[]) fields.get("selected", null); // NOI18N
0702:
0703:                    try {
0704:                        Node root = h.getNode();
0705:
0706:                        if (root == null) {
0707:                            throw new IOException("Node.Handle.getNode (for "
0708:                                    + rootName + ") should not return null"); // NOI18N
0709:                        }
0710:
0711:                        restoreSelection(root, exploredCtx, Arrays
0712:                                .asList(selPaths));
0713:                    } catch (IOException ioe) {
0714:                        SafeException safe = new SafeException(ioe);
0715:
0716:                        if (!Utilities.compareObjects(ioe.getMessage(), ioe
0717:                                .getLocalizedMessage())) {
0718:                            Exceptions.attachLocalizedMessage(safe, NbBundle
0719:                                    .getMessage(ExplorerManager.class,
0720:                                            "EXC_handle_failed", rootName));
0721:                        }
0722:
0723:                        throw safe;
0724:                    }
0725:                }
0726:            }
0727:
0728:            private void readObjectOld(Node.Handle h, ObjectInputStream ois)
0729:                    throws java.io.IOException, ClassNotFoundException {
0730:                if (h == null) {
0731:                    // do nothing => should not occur to often and moreover this is also
0732:                    // dead code replaced by new version
0733:                    return;
0734:                } else {
0735:                    String[] rootCtx = (String[]) ois.readObject();
0736:                    String[] exploredCtx = (String[]) ois.readObject();
0737:                    List<String[]> ll = new LinkedList<String[]>();
0738:
0739:                    for (;;) {
0740:                        String[] path = (String[]) ois.readObject();
0741:
0742:                        if (path == null) {
0743:                            break;
0744:                        }
0745:
0746:                        ll.add(path);
0747:                    }
0748:
0749:                    Node root = findPath(h.getNode(), rootCtx);
0750:                    restoreSelection(root, exploredCtx, ll);
0751:                }
0752:            }
0753:
0754:            private void restoreSelection(final Node root,
0755:                    final String[] exploredCtx,
0756:                    final List</*String[]*/?> selectedPaths) {
0757:                setRootContext(root);
0758:
0759:                // XXX(-ttran) findPath() can take a long time and employs DataSystems
0760:                // and others.  We cannot call it synchrorously, in the past deadlocks
0761:                // have happened because of this.  OTOH as we call setSelectedNodes
0762:                // asynchonously someone else can change the root context or the Node
0763:                // hierarchy in between, which causes setSelectedNodes to throw
0764:                // IllegalArgumentException.  There seems to be no simple good
0765:                // solution.  For now we just catch IllegalArgumentException and be
0766:                // decently silent about the fact.
0767:                //RequestProcessor.getDefault().post(new Runnable() {
0768:                // bugfix #38207, select nodes has to be called in AWT queue here
0769:                Mutex.EVENT.readAccess(new Runnable() {
0770:                    public void run() {
0771:                        // convert paths to Nodes
0772:                        List<Node> selNodes = new ArrayList<Node>(selectedPaths
0773:                                .size());
0774:
0775:                        for (Object path : selectedPaths) {
0776:                            selNodes.add(findPath(root, (String[]) path));
0777:                        }
0778:
0779:                        // set the selection
0780:                        Node[] newSelection = selNodes
0781:                                .toArray(new Node[selNodes.size()]);
0782:
0783:                        if (exploredCtx != null) {
0784:                            setExploredContext(findPath(root, exploredCtx),
0785:                                    newSelection);
0786:                        } else {
0787:                            setSelectedNodes0(newSelection);
0788:                        }
0789:                    }
0790:                });
0791:            }
0792:
0793:            /**
0794:             * Finds the proper Explorer manager for a given component.  This is done
0795:             * by traversing the component hierarchy and finding the first ancestor
0796:             * that implements {@link Provider}.  <P> This method should be used in
0797:             * {@link Component#addNotify} of each component that works with the
0798:             * Explorer manager, e.g.:
0799:             * <p><pre>
0800:             * private transient ExplorerManager explorer;
0801:             *
0802:             * public void addNotify () {
0803:             *   super.addNotify ();
0804:             *   explorer = ExplorerManager.find (this);
0805:             * }
0806:             * </pre>
0807:             *
0808:             * @param comp component to find the manager for
0809:             * @return the manager, or a new empty manager if no ancestor implements
0810:             * <code>Provider</code>
0811:             *
0812:             * @see Provider
0813:             */
0814:            public static ExplorerManager find(Component comp) {
0815:                // start looking for manager from parent, not the component itself
0816:                for (;;) {
0817:                    comp = comp.getParent();
0818:
0819:                    if (comp == null) {
0820:                        // create new explorer because nothing has been found
0821:                        return new ExplorerManager();
0822:                    }
0823:
0824:                    if (comp instanceof  Provider) {
0825:                        // ok, found a provider, return its manager
0826:                        return ((Provider) comp).getExplorerManager();
0827:                    }
0828:                }
0829:            }
0830:
0831:            /** Finds node by given path */
0832:            static Node findPath(Node r, String[] path) {
0833:                try {
0834:                    return NodeOp.findPath(r, path);
0835:                } catch (NodeNotFoundException ex) {
0836:                    return ex.getClosestNode();
0837:                }
0838:            }
0839:
0840:            /** Creates or retrieves RequestProcessor for selection updates. */
0841:            static synchronized RequestProcessor getSelectionProcessor() {
0842:                if (selectionProcessor == null) {
0843:                    selectionProcessor = new RequestProcessor(
0844:                            "ExplorerManager-selection"); //NOI18N
0845:                }
0846:
0847:                return selectionProcessor;
0848:            }
0849:
0850:            /** Finds ExplorerActionsImpl for a explorer manager.
0851:             * @param em the manager
0852:             * @return ExplorerActionsImpl
0853:             */
0854:            static synchronized ExplorerActionsImpl findExplorerActionsImpl(
0855:                    ExplorerManager em) {
0856:                if (em.actions == null) {
0857:                    em.actions = new ExplorerActionsImpl();
0858:                    em.actions.attach(em);
0859:                }
0860:
0861:                return em.actions;
0862:            }
0863:
0864:            final void fireInAWT(final String propName, final Object oldVal,
0865:                    final Object newVal) {
0866:                if (propertySupport != null) {
0867:                    Mutex.EVENT.readAccess(new Runnable() {
0868:                        public void run() {
0869:                            propertySupport.firePropertyChange(propName,
0870:                                    oldVal, newVal);
0871:                        }
0872:                    });
0873:                }
0874:            }
0875:
0876:            private static String getString(String key) {
0877:                return NbBundle.getMessage(ExplorerManager.class, key);
0878:            }
0879:
0880:            //
0881:            // inner classes
0882:            //
0883:
0884:            /** Interface for components wishing to provide their own <code>ExplorerManager</code>.
0885:             * @see ExplorerManager#find
0886:             * @see ExplorerUtils
0887:             */
0888:            public static interface Provider {
0889:                /** Get the explorer manager.
0890:                 * @return the manager
0891:                 */
0892:                public ExplorerManager getExplorerManager();
0893:            }
0894:
0895:            /** Listener to be notified when root node has been destroyed.
0896:             * Then the root node is changed to Node.EMPTY
0897:             */
0898:            private class Listener extends NodeAdapter implements  Runnable {
0899:                Collection<Node> removeList = new HashSet<Node>();
0900:
0901:                Listener() {
0902:                }
0903:
0904:                /** Fired when the node is deleted.
0905:                 * @param ev event describing the node
0906:                 */
0907:                @Override
0908:                public void nodeDestroyed(NodeEvent ev) {
0909:                    if (ev.getNode().equals(getRootContext())) {
0910:                        // node has been deleted
0911:                        // [PENDING] better to show a node with a label such as "<deleted>"
0912:                        // and a tool tip explaining the situation
0913:                        setRootContext(Node.EMPTY);
0914:                    } else {
0915:                        // assume that the node is among currently selected nodes
0916:                        scheduleRemove(ev.getNode());
0917:                    }
0918:                }
0919:
0920:                /* Change in a node.
0921:                 * @param ev the event
0922:                 */
0923:                @Override
0924:                public void propertyChange(java.beans.PropertyChangeEvent ev) {
0925:                    fireInAWT(PROP_NODE_CHANGE, null, null);
0926:                }
0927:
0928:                /** Schedules removal of a node
0929:                 */
0930:                private void scheduleRemove(Node n) {
0931:                    synchronized (ExplorerManager.this ) {
0932:                        if (selectionSyncTask == null) {
0933:                            selectionSyncTask = getSelectionProcessor().create(
0934:                                    this );
0935:                        } else {
0936:                            selectionSyncTask.cancel();
0937:                        }
0938:                    }
0939:
0940:                    synchronized (this ) {
0941:                        removeList.add(n);
0942:                    }
0943:
0944:                    // invariant: selectionSyncTask != null && is not running yet.
0945:                    selectionSyncTask.schedule(SELECTION_SYNC_DELAY);
0946:                }
0947:
0948:                public void run() {
0949:                    if (!Children.MUTEX.isReadAccess()) {
0950:                        Children.MUTEX.readAccess(this );
0951:
0952:                        return;
0953:                    }
0954:
0955:                    Collection<Node> remove;
0956:
0957:                    synchronized (this ) {
0958:                        // atomically clears the list while keeping a copy.
0959:                        // if another node is removed after this point, the selection
0960:                        // will be updated later.
0961:                        remove = removeList;
0962:                        removeList = new HashSet<Node>();
0963:                    }
0964:
0965:                    if (!isUnderRoot(exploredContext)) {
0966:                        setExploredContext(rootContext);
0967:                        return;
0968:                    }
0969:
0970:                    LinkedList<Node> newSel = new LinkedList<Node>(Arrays
0971:                            .asList(getSelectedNodes()));
0972:                    Iterator<Node> it = remove.iterator();
0973:
0974:                    while (it.hasNext()) {
0975:                        Node n_remove = it.next();
0976:
0977:                        if (newSel.contains(n_remove)) {
0978:                            // compare paths to root
0979:                            Node n_selection = newSel.get(newSel
0980:                                    .indexOf(n_remove));
0981:
0982:                            if (!Arrays.equals(NodeOp
0983:                                    .createPath(n_remove, null), NodeOp
0984:                                    .createPath(n_selection, null))) {
0985:                                it.remove();
0986:                            }
0987:                        }
0988:                    }
0989:
0990:                    newSel.removeAll(remove);
0991:                    for (Iterator<Node> i = newSel.iterator(); i.hasNext();) {
0992:                        Node n = i.next();
0993:                        if (!isUnderRoot(n))
0994:                            i.remove();
0995:                    }
0996:
0997:                    Node[] selNodes = newSel.toArray(new Node[newSel.size()]);
0998:                    setSelectedNodes0(selNodes);
0999:
1000:                }
1001:            }
1002:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.