1 package com
.intellij
.ide
.util
.treeView
;
3 import com
.intellij
.ide
.IdeBundle
;
4 import com
.intellij
.openapi
.application
.Application
;
5 import com
.intellij
.openapi
.application
.ApplicationManager
;
6 import com
.intellij
.openapi
.application
.ModalityState
;
7 import com
.intellij
.openapi
.diagnostic
.Logger
;
8 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
9 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
10 import com
.intellij
.openapi
.progress
.ProgressManager
;
11 import com
.intellij
.openapi
.project
.IndexNotReadyException
;
12 import com
.intellij
.openapi
.util
.*;
13 import com
.intellij
.openapi
.util
.registry
.Registry
;
14 import com
.intellij
.openapi
.util
.registry
.RegistryValue
;
15 import com
.intellij
.ui
.LoadingNode
;
16 import com
.intellij
.ui
.treeStructure
.Tree
;
17 import com
.intellij
.util
.Alarm
;
18 import com
.intellij
.util
.ArrayUtil
;
19 import com
.intellij
.util
.ConcurrencyUtil
;
20 import com
.intellij
.util
.Time
;
21 import com
.intellij
.util
.concurrency
.WorkerThread
;
22 import com
.intellij
.util
.containers
.HashSet
;
23 import com
.intellij
.util
.enumeration
.EnumerationCopy
;
24 import com
.intellij
.util
.ui
.tree
.TreeUtil
;
25 import com
.intellij
.util
.ui
.update
.Activatable
;
26 import com
.intellij
.util
.ui
.update
.UiNotifyConnector
;
27 import org
.jetbrains
.annotations
.NotNull
;
28 import org
.jetbrains
.annotations
.Nullable
;
31 import javax
.swing
.event
.TreeExpansionEvent
;
32 import javax
.swing
.event
.TreeExpansionListener
;
33 import javax
.swing
.event
.TreeSelectionEvent
;
34 import javax
.swing
.event
.TreeSelectionListener
;
35 import javax
.swing
.tree
.*;
37 import java
.security
.AccessControlException
;
39 import java
.util
.List
;
40 import java
.util
.concurrent
.ScheduledExecutorService
;
41 import java
.util
.concurrent
.TimeUnit
;
43 class AbstractTreeUi
{
44 private static final Logger LOG
= Logger
.getInstance("#com.intellij.ide.util.treeView.AbstractTreeBuilder");
45 protected JTree myTree
;// protected for TestNG
46 @SuppressWarnings({"WeakerAccess"}) protected DefaultTreeModel myTreeModel
;
47 private AbstractTreeStructure myTreeStructure
;
48 private AbstractTreeUpdater myUpdater
;
49 private Comparator
<NodeDescriptor
> myNodeDescriptorComparator
;
50 private final Comparator
<TreeNode
> myNodeComparator
= new Comparator
<TreeNode
>() {
51 public int compare(TreeNode n1
, TreeNode n2
) {
52 if (isLoadingNode(n1
) || isLoadingNode(n2
)) return 0;
53 NodeDescriptor nodeDescriptor1
= (NodeDescriptor
)((DefaultMutableTreeNode
)n1
).getUserObject();
54 NodeDescriptor nodeDescriptor2
= (NodeDescriptor
)((DefaultMutableTreeNode
)n2
).getUserObject();
55 return myNodeDescriptorComparator
!= null
56 ? myNodeDescriptorComparator
.compare(nodeDescriptor1
, nodeDescriptor2
)
57 : nodeDescriptor1
.getIndex() - nodeDescriptor2
.getIndex();
60 private DefaultMutableTreeNode myRootNode
;
61 private final HashMap
<Object
, Object
> myElementToNodeMap
= new HashMap
<Object
, Object
>();
62 private final HashSet
<DefaultMutableTreeNode
> myUnbuiltNodes
= new HashSet
<DefaultMutableTreeNode
>();
63 private TreeExpansionListener myExpansionListener
;
64 private MySelectionListener mySelectionListener
;
66 private WorkerThread myWorker
= null;
67 private final ArrayList
<Runnable
> myActiveWorkerTasks
= new ArrayList
<Runnable
>();
69 private ProgressIndicator myProgress
;
70 private static final int WAIT_CURSOR_DELAY
= 100;
71 private AbstractTreeNode
<Object
> TREE_NODE_WRAPPER
;
72 private boolean myRootNodeWasInitialized
= false;
73 private final Map
<Object
, List
<NodeAction
>> myNodeActions
= new HashMap
<Object
, List
<NodeAction
>>();
74 private boolean myUpdateFromRootRequested
;
75 private boolean myWasEverShown
;
76 private boolean myUpdateIfInactive
;
77 private final List
<Object
> myLoadingParents
= new ArrayList
<Object
>();
78 private long myClearOnHideDelay
= -1;
79 private ScheduledExecutorService ourClearanceService
;
80 private final Map
<AbstractTreeUi
, Long
> ourUi2Countdown
= Collections
.synchronizedMap(new WeakHashMap
<AbstractTreeUi
, Long
>());
82 private final List
<Runnable
> myDeferredSelections
= new ArrayList
<Runnable
>();
83 private final List
<Runnable
> myDeferredExpansions
= new ArrayList
<Runnable
>();
85 private boolean myCanProcessDeferredSelections
;
87 private UpdaterTreeState myUpdaterState
;
88 private AbstractTreeBuilder myBuilder
;
90 private final Set
<DefaultMutableTreeNode
> myUpdatingChildren
= new HashSet
<DefaultMutableTreeNode
>();
91 private long myJanitorPollPeriod
= Time
.SECOND
* 10;
92 private boolean myCheckStructure
= false;
95 private boolean myCanYield
= false;
97 private final List
<TreeUpdatePass
> myYeildingPasses
= new ArrayList
<TreeUpdatePass
>();
99 private boolean myYeildingNow
;
101 private List
<DefaultMutableTreeNode
> myPendingNodeActions
= new ArrayList
<DefaultMutableTreeNode
>();
102 private List
<Runnable
> myYeildingDoneRunnables
= new ArrayList
<Runnable
>();
104 private Alarm myBusyAlarm
= new Alarm();
105 private Runnable myWaiterForReady
= new Runnable() {
107 maybeSetBusyAndScheduleWaiterForReady(false);
111 private RegistryValue myYeildingUpdate
= Registry
.get("ide.tree.yeildingUiUpdate");
112 private RegistryValue myShowBusyIndicator
= Registry
.get("ide.tree.showBusyIndicator");
113 private RegistryValue myWaitForReadyTime
= Registry
.get("ide.tree.waitForReadyTimout");
115 private boolean myWasEverIndexNotReady
;
117 protected final void init(AbstractTreeBuilder builder
,
119 DefaultTreeModel treeModel
,
120 AbstractTreeStructure treeStructure
,
121 Comparator
<NodeDescriptor
> comparator
) {
123 init(builder
, tree
, treeModel
, treeStructure
, comparator
, true);
126 protected void init(AbstractTreeBuilder builder
,
128 DefaultTreeModel treeModel
,
129 AbstractTreeStructure treeStructure
,
130 Comparator
<NodeDescriptor
> comparator
,
131 boolean updateIfInactive
) {
134 myTreeModel
= treeModel
;
135 TREE_NODE_WRAPPER
= getBuilder().createSearchingTreeNodeWrapper();
136 myTree
.setModel(myTreeModel
);
137 setRootNode((DefaultMutableTreeNode
)treeModel
.getRoot());
138 setTreeStructure(treeStructure
);
139 myNodeDescriptorComparator
= comparator
;
140 myUpdateIfInactive
= updateIfInactive
;
142 myExpansionListener
= new MyExpansionListener();
143 myTree
.addTreeExpansionListener(myExpansionListener
);
145 mySelectionListener
= new MySelectionListener();
146 myTree
.addTreeSelectionListener(mySelectionListener
);
148 setUpdater(getBuilder().createUpdater());
149 myProgress
= getBuilder().createProgressIndicator();
150 Disposer
.register(getBuilder(), getUpdater());
152 final UiNotifyConnector uiNotify
= new UiNotifyConnector(tree
, new Activatable() {
153 public void showNotify() {
155 AbstractTreeUi
.this.showNotify();
159 public void hideNotify() {
161 AbstractTreeUi
.this.hideNotify();
165 Disposer
.register(getBuilder(), uiNotify
);
168 protected void hideNotify() {
169 myBusyAlarm
.cancelAllRequests();
171 if (!myWasEverShown
) return;
173 if (!myNodeActions
.isEmpty()) {
174 cancelBackgroundLoading();
175 myUpdateFromRootRequested
= true;
178 if (getClearOnHideDelay() >= 0) {
179 ourUi2Countdown
.put(this, System
.currentTimeMillis() + getClearOnHideDelay());
180 initClearanceServiceIfNeeded();
184 private void maybeSetBusyAndScheduleWaiterForReady(boolean forcedBusy
) {
185 if (!myShowBusyIndicator
.asBoolean() || !canYield()) return;
187 if (myTree
instanceof com
.intellij
.ui
.treeStructure
.Tree
) {
188 final com
.intellij
.ui
.treeStructure
.Tree tree
= (Tree
)myTree
;
189 final boolean isBusy
= !isReady() || forcedBusy
;
190 if (isBusy
&& tree
.isShowing()) {
191 tree
.setPaintBusy(true);
192 myBusyAlarm
.cancelAllRequests();
193 myBusyAlarm
.addRequest(myWaiterForReady
, myWaitForReadyTime
.asInteger());
196 tree
.setPaintBusy(false);
201 private void initClearanceServiceIfNeeded() {
202 if (ourClearanceService
!= null) return;
204 ourClearanceService
= ConcurrencyUtil
.newSingleScheduledThreadExecutor("AbstractTreeBuilder's janitor");
205 ourClearanceService
.scheduleWithFixedDelay(new Runnable() {
209 }, myJanitorPollPeriod
, myJanitorPollPeriod
, TimeUnit
.MILLISECONDS
);
212 private void cleanUpAll() {
213 final long now
= System
.currentTimeMillis();
214 final AbstractTreeUi
[] uis
= ourUi2Countdown
.keySet().toArray(new AbstractTreeUi
[ourUi2Countdown
.size()]);
215 for (AbstractTreeUi eachUi
: uis
) {
216 if (eachUi
== null) continue;
217 final Long timeToCleanup
= ourUi2Countdown
.get(eachUi
);
218 if (timeToCleanup
== null) continue;
219 if (now
>= timeToCleanup
.longValue()) {
220 ourUi2Countdown
.remove(eachUi
);
221 getBuilder().cleanUp();
226 protected void doCleanUp() {
227 final Application app
= ApplicationManager
.getApplication();
228 if (app
!= null && app
.isUnitTestMode()) {
233 //noinspection SSBasedInspection
234 SwingUtilities
.invokeLater(new Runnable() {
244 private void disposeClearanceService() {
246 if (ourClearanceService
!= null) {
247 ourClearanceService
.shutdown();
248 ourClearanceService
= null;
251 catch (AccessControlException e
) {
257 myCanProcessDeferredSelections
= true;
259 ourUi2Countdown
.remove(this);
261 if (!myWasEverShown
|| myUpdateFromRootRequested
) {
262 if (wasRootNodeInitialized()) {
263 getBuilder().updateFromRoot();
266 initRootNodeNowIfNeeded(new TreeUpdatePass(getRootNode()));
267 getBuilder().updateFromRoot();
270 myWasEverShown
= true;
273 public void release() {
274 if (isReleased()) return;
276 myTree
.removeTreeExpansionListener(myExpansionListener
);
277 myTree
.removeTreeSelectionListener(mySelectionListener
);
278 disposeNode(getRootNode());
279 myElementToNodeMap
.clear();
280 getUpdater().cancelAllRequests();
281 if (myWorker
!= null) {
282 myWorker
.dispose(true);
285 TREE_NODE_WRAPPER
.setValue(null);
286 if (myProgress
!= null) {
289 disposeClearanceService();
294 //todo [kirillk] afraid to do so just in release day, to uncomment
295 // myTreeStructure = null;
299 public boolean isReleased() {
300 return myBuilder
== null;
303 protected void doExpandNodeChildren(final DefaultMutableTreeNode node
) {
304 getTreeStructure().commit();
305 addNodeAction(getElementFor(node
), new NodeAction() {
306 public void onReady(final DefaultMutableTreeNode node
) {
307 processSmartExpand(node
);
310 getUpdater().addSubtreeToUpdate(node
);
311 getUpdater().performUpdate();
314 public final AbstractTreeStructure
getTreeStructure() {
315 return myTreeStructure
;
318 public final JTree
getTree() {
323 public final DefaultMutableTreeNode
getNodeForElement(Object element
, final boolean validateAgainstStructure
) {
324 DefaultMutableTreeNode result
= null;
325 if (validateAgainstStructure
) {
328 final DefaultMutableTreeNode node
= findNode(element
, index
);
329 if (node
== null) break;
331 if (isNodeValidForElement(element
, node
)) {
340 result
= getFirstNode(element
);
344 if (result
!= null && !isNodeInStructure(result
)) {
352 private boolean isNodeInStructure(DefaultMutableTreeNode node
) {
353 return TreeUtil
.isAncestor(getRootNode(), node
) && getRootNode() == myTreeModel
.getRoot();
356 private boolean isNodeValidForElement(final Object element
, final DefaultMutableTreeNode node
) {
357 return isSameHierarchy(element
, node
) || isValidChildOfParent(element
, node
);
360 private boolean isValidChildOfParent(final Object element
, final DefaultMutableTreeNode node
) {
361 final DefaultMutableTreeNode parent
= (DefaultMutableTreeNode
)node
.getParent();
362 final Object parentElement
= getElementFor(parent
);
363 if (!isInStructure(parentElement
)) return false;
365 if (parent
instanceof ElementNode
) {
366 return ((ElementNode
)parent
).isValidChild(element
);
369 for (int i
= 0; i
< parent
.getChildCount(); i
++) {
370 final TreeNode child
= parent
.getChildAt(i
);
371 final Object eachElement
= getElementFor(child
);
372 if (element
.equals(eachElement
)) return true;
379 private boolean isSameHierarchy(Object eachParent
, DefaultMutableTreeNode eachParentNode
) {
380 boolean valid
= true;
382 if (eachParent
== null) {
383 valid
= eachParentNode
== null;
387 if (!eachParent
.equals(getElementFor(eachParentNode
))) {
392 eachParent
= getTreeStructure().getParentElement(eachParent
);
393 eachParentNode
= (DefaultMutableTreeNode
)eachParentNode
.getParent();
398 public final DefaultMutableTreeNode
getNodeForPath(Object
[] path
) {
399 DefaultMutableTreeNode node
= null;
400 for (final Object pathElement
: path
) {
401 node
= node
== null ?
getFirstNode(pathElement
) : findNodeForChildElement(node
, pathElement
);
409 public final void buildNodeForElement(Object element
) {
410 getUpdater().performUpdate();
411 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
413 final java
.util
.List
<Object
> elements
= new ArrayList
<Object
>();
415 element
= getTreeStructure().getParentElement(element
);
416 if (element
== null) {
419 elements
.add(0, element
);
422 for (final Object element1
: elements
) {
423 node
= getNodeForElement(element1
, false);
431 public final void buildNodeForPath(Object
[] path
) {
432 getUpdater().performUpdate();
433 DefaultMutableTreeNode node
= null;
434 for (final Object pathElement
: path
) {
435 node
= node
== null ?
getFirstNode(pathElement
) : findNodeForChildElement(node
, pathElement
);
436 if (node
!= null && node
!= path
[path
.length
- 1]) {
442 public final void setNodeDescriptorComparator(Comparator
<NodeDescriptor
> nodeDescriptorComparator
) {
443 myNodeDescriptorComparator
= nodeDescriptorComparator
;
444 List
<Object
> pathsToExpand
= new ArrayList
<Object
>();
445 List
<Object
> selectionPaths
= new ArrayList
<Object
>();
446 TreeBuilderUtil
.storePaths(getBuilder(), getRootNode(), pathsToExpand
, selectionPaths
, false);
447 resortChildren(getRootNode());
448 myTreeModel
.nodeStructureChanged(getRootNode());
449 TreeBuilderUtil
.restorePaths(getBuilder(), pathsToExpand
, selectionPaths
, false);
452 protected AbstractTreeBuilder
getBuilder() {
456 private void resortChildren(DefaultMutableTreeNode node
) {
457 ArrayList
<TreeNode
> childNodes
= TreeUtil
.childrenToArray(node
);
458 node
.removeAllChildren();
459 Collections
.sort(childNodes
, myNodeComparator
);
460 for (TreeNode childNode1
: childNodes
) {
461 DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)childNode1
;
463 resortChildren(childNode
);
467 protected final void initRootNode() {
468 final Activatable activatable
= new Activatable() {
469 public void showNotify() {
470 if (!myRootNodeWasInitialized
) {
471 initRootNodeNowIfNeeded(new TreeUpdatePass(getRootNode()));
475 public void hideNotify() {
479 if (myUpdateIfInactive
|| ApplicationManager
.getApplication().isUnitTestMode()) {
480 activatable
.showNotify();
483 new UiNotifyConnector
.Once(myTree
, activatable
);
487 private void initRootNodeNowIfNeeded(TreeUpdatePass pass
) {
488 if (myRootNodeWasInitialized
) return;
490 myRootNodeWasInitialized
= true;
491 Object rootElement
= getTreeStructure().getRootElement();
492 addNodeAction(rootElement
, new NodeAction() {
493 public void onReady(final DefaultMutableTreeNode node
) {
494 processDeferredActions();
497 NodeDescriptor nodeDescriptor
= getTreeStructure().createDescriptor(rootElement
, null);
498 getRootNode().setUserObject(nodeDescriptor
);
499 update(nodeDescriptor
, false);
500 if (getElementFromDescriptor(nodeDescriptor
) != null) {
501 createMapping(getElementFromDescriptor(nodeDescriptor
), getRootNode());
503 addLoadingNode(getRootNode());
504 boolean willUpdate
= false;
505 if (getBuilder().isAutoExpandNode(nodeDescriptor
)) {
506 willUpdate
= myUnbuiltNodes
.contains(getRootNode());
507 expand(getRootNode());
510 updateNodeChildren(getRootNode(), pass
, null, false);
512 if (getRootNode().getChildCount() == 0) {
513 myTreeModel
.nodeChanged(getRootNode());
516 if (!myLoadingParents
.contains(getTreeStructure().getRootElement())) {
517 processDeferredActions();
521 private boolean update(final NodeDescriptor nodeDescriptor
, boolean canBeNonEdt
) {
522 if (!canBeNonEdt
&& myWasEverShown
) {
523 assertIsDispatchThread();
526 final Application app
= ApplicationManager
.getApplication();
527 if (app
.isDispatchThread() || !myWasEverShown
) {
528 return getBuilder().updateNodeDescriptor(nodeDescriptor
);
531 app
.invokeLater(new Runnable() {
534 getBuilder().updateNodeDescriptor(nodeDescriptor
);
537 }, ModalityState
.stateForComponent(myTree
));
542 private void assertIsDispatchThread() {
543 if (myTree
.isShowing()) {
544 ApplicationManager
.getApplication().assertIsDispatchThread();
548 private void processDeferredActions() {
549 processDeferredActions(myDeferredSelections
);
550 processDeferredActions(myDeferredExpansions
);
553 private void processDeferredActions(List
<Runnable
> actions
) {
554 final Runnable
[] runnables
= actions
.toArray(new Runnable
[actions
.size()]);
556 for (Runnable runnable
: runnables
) {
561 public void doUpdateFromRoot() {
562 updateSubtree(getRootNode());
565 public ActionCallback
doUpdateFromRootCB() {
566 final ActionCallback cb
= new ActionCallback();
567 getUpdater().runAfterUpdate(new Runnable() {
572 updateSubtree(getRootNode());
576 public final void updateSubtree(DefaultMutableTreeNode node
) {
577 updateSubtree(new TreeUpdatePass(node
));
580 public final void updateSubtree(TreeUpdatePass pass
) {
581 maybeSetBusyAndScheduleWaiterForReady(true);
583 initRootNodeNowIfNeeded(pass
);
585 final DefaultMutableTreeNode node
= pass
.getNode();
587 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
589 setUpdaterState(new UpdaterTreeState(this)).beforeSubtreeUpdate();
591 getBuilder().updateNode(node
);
592 updateNodeChildren(node
, pass
, null, false);
596 UpdaterTreeState
setUpdaterState(UpdaterTreeState state
) {
597 final UpdaterTreeState oldState
= myUpdaterState
;
598 if (oldState
== null) {
599 myUpdaterState
= state
;
603 oldState
.addAll(state
);
608 protected void doUpdateNode(DefaultMutableTreeNode node
) {
609 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
610 NodeDescriptor descriptor
= (NodeDescriptor
)node
.getUserObject();
611 Object prevElement
= getElementFromDescriptor(descriptor
);
612 if (prevElement
== null) return;
613 boolean changes
= update(descriptor
, false);
614 if (getElementFromDescriptor(descriptor
) == null) {
615 LOG
.assertTrue(false, "element == null, updateSubtree should be invoked for parent! builder=" +
621 "; parentDescriptor=" +
622 descriptor
.getParentDescriptor());
625 updateNodeImageAndPosition(node
);
629 public Object
getElementFromDescriptor(NodeDescriptor descriptor
) {
630 return getBuilder().getTreeStructureElement(descriptor
);
633 private void updateNodeChildren(final DefaultMutableTreeNode node
,
634 final TreeUpdatePass pass
,
635 @Nullable Object
[] preloadedChildren
,
637 getTreeStructure().commit();
638 final boolean wasExpanded
= myTree
.isExpanded(new TreePath(node
.getPath()));
639 final boolean wasLeaf
= node
.getChildCount() == 0;
641 final NodeDescriptor descriptor
= (NodeDescriptor
)node
.getUserObject();
643 if (descriptor
== null) return;
645 if (myUnbuiltNodes
.contains(node
)) {
646 processUnbuilt(node
, descriptor
, pass
);
647 processNodeActionsIfReady(node
);
651 if (!forcedNow
&& getTreeStructure().isToBuildChildrenInBackground(getBuilder().getTreeStructureElement(descriptor
))) {
652 queueBackgroundUpdate(node
, descriptor
, pass
);
656 final MutualMap
<Object
, Integer
> elementToIndexMap
= collectElementToIndexMap(descriptor
, preloadedChildren
);
658 myUpdatingChildren
.add(node
);
659 processAllChildren(node
, elementToIndexMap
, pass
).doWhenDone(new Runnable() {
661 if (isDisposed(node
)) {
666 removeLoadingNode(node
);
669 ArrayList
<TreeNode
> nodesToInsert
= collectNodesToInsert(descriptor
, elementToIndexMap
);
671 insertNodesInto(nodesToInsert
, node
);
673 updateNodesToInsert(nodesToInsert
, pass
);
679 if (wasExpanded
|| wasLeaf
) {
680 expand(node
, descriptor
, wasLeaf
);
683 myUpdatingChildren
.remove(node
);
685 final Object element
= getElementFor(node
);
686 addNodeAction(element
, new NodeAction() {
687 public void onReady(final DefaultMutableTreeNode node
) {
688 removeLoadingNode(node
);
692 processNodeActionsIfReady(node
);
697 private boolean isDisposed(DefaultMutableTreeNode node
) {
698 return !node
.isNodeAncestor((DefaultMutableTreeNode
)myTree
.getModel().getRoot());
701 private void expand(DefaultMutableTreeNode node
) {
702 expand(new TreePath(node
.getPath()));
705 private void expand(final TreePath path
) {
706 if (path
== null) return;
707 final Object last
= path
.getLastPathComponent();
708 boolean isLeaf
= myTree
.getModel().isLeaf(path
.getLastPathComponent());
709 final boolean isRoot
= last
== myTree
.getModel().getRoot();
710 final TreePath parent
= path
.getParentPath();
712 processNodeActionsIfReady((DefaultMutableTreeNode
)last
);
714 else if (myTree
.isExpanded(path
) || (isLeaf
&& parent
!= null && myTree
.isExpanded(parent
))) {
715 if (last
instanceof DefaultMutableTreeNode
) {
716 processNodeActionsIfReady((DefaultMutableTreeNode
)last
);
720 if (isLeaf
&& parent
!= null) {
721 final DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)parent
.getLastPathComponent();
722 if (parentNode
!= null) {
723 myUnbuiltNodes
.add(parentNode
);
733 private void processUnbuilt(final DefaultMutableTreeNode node
, final NodeDescriptor descriptor
, final TreeUpdatePass pass
) {
734 if (getBuilder().isAlwaysShowPlus(descriptor
)) return; // check for isAlwaysShowPlus is important for e.g. changing Show Members state!
736 final Object element
= getBuilder().getTreeStructureElement(descriptor
);
738 if (getTreeStructure().isToBuildChildrenInBackground(element
)) return; //?
740 final Object
[] children
= getChildrenFor(element
);
741 if (children
.length
== 0) {
742 removeLoadingNode(node
);
744 else if (getBuilder().isAutoExpandNode((NodeDescriptor
)node
.getUserObject())) {
745 addNodeAction(getElementFor(node
), new NodeAction() {
746 public void onReady(final DefaultMutableTreeNode node
) {
747 final TreePath path
= new TreePath(node
.getPath());
748 if (getTree().isExpanded(path
) || children
.length
== 0) {
749 removeLoadingNode(node
);
752 maybeYeild(new ActiveRunnable() {
753 public ActionCallback
run() {
754 expand(element
, null);
755 return new ActionCallback
.Done();
764 private void removeLoadingNode(final DefaultMutableTreeNode parent
) {
765 for (int i
= 0; i
< parent
.getChildCount(); i
++) {
766 if (removeIfLoading(parent
.getChildAt(i
))) break;
768 myUnbuiltNodes
.remove(parent
);
771 private boolean removeIfLoading(TreeNode node
) {
772 if (isLoadingNode(node
)) {
773 removeNodeFromParent((MutableTreeNode
)node
, false);
780 //todo [kirillk] temporary consistency check
781 private Object
[] getChildrenFor(final Object element
) {
782 final Object
[] passOne
;
784 passOne
= getTreeStructure().getChildElements(element
);
786 catch (IndexNotReadyException e
) {
787 if (!myWasEverIndexNotReady
) {
788 myWasEverIndexNotReady
= true;
789 LOG
.warn("Tree is not dumb-mode-aware; treeBuilder=" + getBuilder() + " treeStructure=" + getTreeStructure());
791 return ArrayUtil
.EMPTY_OBJECT_ARRAY
;
794 if (!myCheckStructure
) return passOne
;
796 final Object
[] passTwo
= getTreeStructure().getChildElements(element
);
798 final HashSet two
= new HashSet(Arrays
.asList(passTwo
));
800 if (passOne
.length
!= passTwo
.length
) {
802 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
806 for (Object eachInOne
: passOne
) {
807 if (!two
.contains(eachInOne
)) {
809 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
819 private void updateNodesToInsert(final ArrayList
<TreeNode
> nodesToInsert
, TreeUpdatePass pass
) {
820 for (TreeNode aNodesToInsert
: nodesToInsert
) {
821 DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)aNodesToInsert
;
822 addLoadingNode(childNode
);
823 updateNodeChildren(childNode
, pass
, null, false);
827 private ActionCallback
processAllChildren(final DefaultMutableTreeNode node
,
828 final MutualMap
<Object
, Integer
> elementToIndexMap
,
829 final TreeUpdatePass pass
) {
831 final ArrayList
<TreeNode
> childNodes
= TreeUtil
.childrenToArray(node
);
832 return maybeYeild(new ActiveRunnable() {
833 public ActionCallback
run() {
834 return processAllChildren(node
, elementToIndexMap
, pass
, childNodes
);
839 private ActionCallback
maybeYeild(final ActiveRunnable processRunnable
, final TreeUpdatePass pass
, final DefaultMutableTreeNode node
) {
840 final ActionCallback result
= new ActionCallback();
842 if (isToYieldUpdateFor(node
)) {
843 pass
.setCurrentNode(node
);
844 yieldAndRun(new Runnable() {
846 if (pass
.isExpired()) return;
848 if (getUpdater().isRerunNeededFor(pass
)) {
849 getUpdater().addSubtreeToUpdate(pass
);
850 result
.setRejected();
853 processRunnable
.run().notify(result
);
859 processRunnable
.run().notify(result
);
865 private void yieldAndRun(final Runnable runnable
, final TreeUpdatePass pass
) {
866 myYeildingPasses
.add(pass
);
867 myYeildingNow
= true;
868 yield(new Runnable() {
874 runOnYeildingDone(new Runnable() {
879 executeYeildingRequest(runnable
, pass
);
886 public boolean isYeildingNow() {
887 return myYeildingNow
;
890 private boolean hasSheduledUpdates() {
891 return getUpdater().hasNodesToUpdate() || myLoadingParents
.size() > 0;
894 private boolean hasExpandedUnbuiltNodes() {
895 for (DefaultMutableTreeNode each
: myUnbuiltNodes
) {
896 if (myTree
.isExpanded(new TreePath(each
.getPath()))) return true;
902 public boolean isReady() {
903 return !isYeildingNow() && !hasSheduledUpdates() && !hasExpandedUnbuiltNodes() && !isWorkerBusy();
906 private void executeYeildingRequest(Runnable runnable
, TreeUpdatePass pass
) {
908 myYeildingPasses
.remove(pass
);
912 maybeYeildingFinished();
916 private void maybeYeildingFinished() {
917 if (myYeildingPasses
.size() == 0) {
918 myYeildingNow
= false;
919 flushPendingNodeActions();
923 private void flushPendingNodeActions() {
924 final DefaultMutableTreeNode
[] nodes
= myPendingNodeActions
.toArray(new DefaultMutableTreeNode
[myPendingNodeActions
.size()]);
925 myPendingNodeActions
.clear();
927 for (DefaultMutableTreeNode each
: nodes
) {
928 processNodeActionsIfReady(each
);
931 final Runnable
[] actions
= myYeildingDoneRunnables
.toArray(new Runnable
[myYeildingDoneRunnables
.size()]);
932 for (Runnable each
: actions
) {
933 if (!isYeildingNow()) {
934 myYeildingDoneRunnables
.remove(each
);
940 protected void runOnYeildingDone(Runnable onDone
) {
941 getBuilder().runOnYeildingDone(onDone
);
944 protected void yield(Runnable runnable
) {
945 getBuilder().yield(runnable
);
948 private ActionCallback
processAllChildren(final DefaultMutableTreeNode node
,
949 final MutualMap
<Object
, Integer
> elementToIndexMap
,
950 final TreeUpdatePass pass
,
951 final ArrayList
<TreeNode
> childNodes
) {
954 if (pass
.isExpired()) return new ActionCallback
.Rejected();
956 if (childNodes
.size() == 0) return new ActionCallback
.Done();
959 final ActionCallback result
= new ActionCallback(childNodes
.size());
961 for (TreeNode childNode1
: childNodes
) {
962 final DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)childNode1
;
963 if (isLoadingNode(childNode
)) {
968 maybeYeild(new ActiveRunnable() {
970 public ActionCallback
run() {
971 return processChildNode(childNode
, (NodeDescriptor
)childNode
.getUserObject(), node
, elementToIndexMap
, pass
);
973 }, pass
, node
).notify(result
);
979 private boolean isToYieldUpdateFor(final DefaultMutableTreeNode node
) {
980 if (!canYield()) return false;
981 return getBuilder().isToYieldUpdateFor(node
);
984 private MutualMap
<Object
, Integer
> collectElementToIndexMap(final NodeDescriptor descriptor
, @Nullable Object
[] preloadedChildren
) {
985 MutualMap
<Object
, Integer
> elementToIndexMap
= new MutualMap
<Object
, Integer
>(true);
986 Object
[] children
= preloadedChildren
!= null ? preloadedChildren
: getChildrenFor(getBuilder().getTreeStructureElement(descriptor
));
988 for (Object child
: children
) {
989 if (!isValid(child
)) continue;
990 elementToIndexMap
.put(child
, Integer
.valueOf(index
));
993 return elementToIndexMap
;
996 private void expand(final DefaultMutableTreeNode node
, final NodeDescriptor descriptor
, final boolean wasLeaf
) {
997 final Alarm alarm
= new Alarm(Alarm
.ThreadToUse
.SHARED_THREAD
);
998 alarm
.addRequest(new Runnable() {
1000 myTree
.setCursor(Cursor
.getPredefinedCursor(Cursor
.WAIT_CURSOR
));
1002 }, WAIT_CURSOR_DELAY
);
1004 if (wasLeaf
&& getBuilder().isAutoExpandNode(descriptor
)) {
1008 ArrayList
<TreeNode
> nodes
= TreeUtil
.childrenToArray(node
);
1009 for (TreeNode node1
: nodes
) {
1010 final DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)node1
;
1011 if (isLoadingNode(childNode
)) continue;
1012 NodeDescriptor childDescr
= (NodeDescriptor
)childNode
.getUserObject();
1013 if (getBuilder().isAutoExpandNode(childDescr
)) {
1014 addNodeAction(getElementFor(childNode
), new NodeAction() {
1015 public void onReady(DefaultMutableTreeNode node
) {
1019 addSubtreeToUpdate(childNode
);
1023 int n
= alarm
.cancelAllRequests();
1025 myTree
.setCursor(Cursor
.getDefaultCursor());
1029 public static boolean isLoadingNode(final Object node
) {
1030 return node
instanceof LoadingNode
;
1033 private ArrayList
<TreeNode
> collectNodesToInsert(final NodeDescriptor descriptor
, final MutualMap
<Object
, Integer
> elementToIndexMap
) {
1034 ArrayList
<TreeNode
> nodesToInsert
= new ArrayList
<TreeNode
>();
1035 final Collection
<Object
> allElements
= elementToIndexMap
.getKeys();
1036 for (Object child
: allElements
) {
1037 Integer index
= elementToIndexMap
.getValue(child
);
1038 final NodeDescriptor childDescr
= getTreeStructure().createDescriptor(child
, descriptor
);
1039 //noinspection ConstantConditions
1040 if (childDescr
== null) {
1041 LOG
.error("childDescr == null, treeStructure = " + getTreeStructure() + ", child = " + child
);
1044 childDescr
.setIndex(index
.intValue());
1045 update(childDescr
, false);
1046 if (getElementFromDescriptor(childDescr
) == null) {
1047 LOG
.error("childDescr.getElement() == null, child = " + child
+ ", builder = " + this);
1050 final DefaultMutableTreeNode childNode
= createChildNode(childDescr
);
1051 nodesToInsert
.add(childNode
);
1052 createMapping(getElementFromDescriptor(childDescr
), childNode
);
1055 return nodesToInsert
;
1058 protected DefaultMutableTreeNode
createChildNode(final NodeDescriptor descriptor
) {
1059 return new ElementNode(this, descriptor
);
1062 protected boolean canYield() {
1063 return myCanYield
&& myYeildingUpdate
.asBoolean();
1066 public long getClearOnHideDelay() {
1067 return myClearOnHideDelay
> 0 ? myClearOnHideDelay
: Registry
.intValue("ide.tree.clearOnHideTime");
1070 static class ElementNode
extends DefaultMutableTreeNode
{
1072 Set
<Object
> myElements
= new HashSet
<Object
>();
1073 AbstractTreeUi myUi
;
1076 ElementNode(AbstractTreeUi ui
, NodeDescriptor descriptor
) {
1082 public void insert(final MutableTreeNode newChild
, final int childIndex
) {
1083 super.insert(newChild
, childIndex
);
1084 final Object element
= myUi
.getElementFor(newChild
);
1085 if (element
!= null) {
1086 myElements
.add(element
);
1091 public void remove(final int childIndex
) {
1092 final TreeNode node
= getChildAt(childIndex
);
1093 super.remove(childIndex
);
1094 final Object element
= myUi
.getElementFor(node
);
1095 if (element
!= null) {
1096 myElements
.remove(element
);
1100 boolean isValidChild(Object childElement
) {
1101 return myElements
.contains(childElement
);
1105 public String
toString() {
1106 return String
.valueOf(getUserObject());
1110 private boolean isUpdatingParent(DefaultMutableTreeNode kid
) {
1111 DefaultMutableTreeNode eachParent
= kid
;
1112 while (eachParent
!= null) {
1113 if (myUpdatingChildren
.contains(eachParent
)) return true;
1114 eachParent
= (DefaultMutableTreeNode
)eachParent
.getParent();
1120 private boolean queueBackgroundUpdate(final DefaultMutableTreeNode node
, final NodeDescriptor descriptor
, final TreeUpdatePass pass
) {
1121 if (myLoadingParents
.contains(getElementFromDescriptor(descriptor
))) return false;
1123 myLoadingParents
.add(getElementFromDescriptor(descriptor
));
1125 if (!isNodeBeingBuilt(node
)) {
1126 LoadingNode loadingNode
= new LoadingNode(getLoadingNodeText());
1127 myTreeModel
.insertNodeInto(loadingNode
, node
, node
.getChildCount());
1130 final Ref
<Object
[]> children
= new Ref
<Object
[]>();
1131 Runnable buildRunnable
= new Runnable() {
1133 if (isReleased()) return;
1135 update(descriptor
, true);
1136 Object element
= getElementFromDescriptor(descriptor
);
1137 if (element
== null) return;
1139 children
.set(getChildrenFor(getBuilder().getTreeStructureElement(descriptor
))); // load children
1143 final DefaultMutableTreeNode
[] nodeToProcessActions
= new DefaultMutableTreeNode
[1];
1144 Runnable updateRunnable
= new Runnable() {
1146 if (isReleased()) return;
1147 if (children
.get() == null) return;
1149 updateNodeChildren(node
, pass
, children
.get(), true);
1151 myLoadingParents
.remove(getElementFromDescriptor(descriptor
));
1153 update(descriptor
, false);
1154 Object element
= getElementFromDescriptor(descriptor
);
1156 if (element
!= null) {
1157 myUnbuiltNodes
.remove(node
);
1159 for (int i
= 0; i
< node
.getChildCount(); i
++) {
1160 TreeNode child
= node
.getChildAt(i
);
1161 if (isLoadingNode(child
)) {
1162 //if (TreeBuilderUtil.isNodeSelected(myTree, node)) {
1163 // addSelectionPath(new TreePath(myTreeModel.getPathToRoot(node)), true, Condition.FALSE);
1165 removeIfLoading(child
);
1170 nodeToProcessActions
[0] = node
;
1174 addTaskToWorker(buildRunnable
, true, updateRunnable
, new Runnable() {
1176 if (nodeToProcessActions
[0] != null) {
1177 processNodeActionsIfReady(nodeToProcessActions
[0]);
1184 private void processNodeActionsIfReady(final DefaultMutableTreeNode node
) {
1185 if (isNodeBeingBuilt(node
)) return;
1187 final Object o
= node
.getUserObject();
1188 if (!(o
instanceof NodeDescriptor
)) return;
1191 if (isYeildingNow()) {
1192 myPendingNodeActions
.add(node
);
1196 final Object element
= getBuilder().getTreeStructureElement((NodeDescriptor
)o
);
1198 final List
<NodeAction
> actions
= myNodeActions
.get(element
);
1199 if (actions
!= null) {
1200 myNodeActions
.remove(element
);
1201 for (NodeAction each
: actions
) {
1206 if (!isUpdatingParent(node
) && !isWorkerBusy()) {
1207 final UpdaterTreeState state
= myUpdaterState
;
1208 if (myNodeActions
.size() == 0 && state
!= null && !state
.isProcessingNow()) {
1209 if (!state
.restore()) {
1210 setUpdaterState(state
);
1217 private void processSmartExpand(final DefaultMutableTreeNode node
) {
1218 if (getBuilder().isSmartExpand() && node
.getChildCount() == 1) { // "smart" expand
1219 TreeNode childNode
= node
.getChildAt(0);
1220 if (isLoadingNode(childNode
)) return;
1221 final TreePath childPath
= new TreePath(node
.getPath()).pathByAddingChild(childNode
);
1226 public boolean isLoadingChildrenFor(final Object nodeObject
) {
1227 if (!(nodeObject
instanceof DefaultMutableTreeNode
)) return false;
1229 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)nodeObject
;
1231 int loadingNodes
= 0;
1232 for (int i
= 0; i
< Math
.min(node
.getChildCount(), 2); i
++) {
1233 TreeNode child
= node
.getChildAt(i
);
1234 if (isLoadingNode(child
)) {
1238 return loadingNodes
> 0 && loadingNodes
== node
.getChildCount();
1241 private boolean isParentLoading(Object nodeObject
) {
1242 if (!(nodeObject
instanceof DefaultMutableTreeNode
)) return false;
1244 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)nodeObject
;
1246 TreeNode eachParent
= node
.getParent();
1248 while (eachParent
!= null) {
1249 eachParent
= eachParent
.getParent();
1250 if (eachParent
instanceof DefaultMutableTreeNode
) {
1251 final Object eachElement
= getElementFor((DefaultMutableTreeNode
)eachParent
);
1252 if (myLoadingParents
.contains(eachElement
)) return true;
1259 protected String
getLoadingNodeText() {
1260 return IdeBundle
.message("progress.searching");
1263 private ActionCallback
processChildNode(final DefaultMutableTreeNode childNode
,
1264 final NodeDescriptor childDescriptor
,
1265 final DefaultMutableTreeNode parentNode
,
1266 final MutualMap
<Object
, Integer
> elementToIndexMap
,
1267 TreeUpdatePass pass
) {
1269 if (pass
.isExpired()) {
1270 return new ActionCallback
.Rejected();
1273 NodeDescriptor childDesc
= childDescriptor
;
1276 if (childDesc
== null) {
1278 return new ActionCallback
.Rejected();
1280 Object oldElement
= getElementFromDescriptor(childDesc
);
1281 if (oldElement
== null) {
1283 return new ActionCallback
.Rejected();
1285 boolean changes
= update(childDesc
, false);
1286 boolean forceRemapping
= false;
1287 Object newElement
= getElementFromDescriptor(childDesc
);
1289 Integer index
= newElement
!= null ? elementToIndexMap
.getValue(getBuilder().getTreeStructureElement(childDesc
)) : null;
1290 if (index
!= null) {
1291 final Object elementFromMap
= elementToIndexMap
.getKey(index
);
1292 if (elementFromMap
!= newElement
&& elementFromMap
.equals(newElement
)) {
1293 if (isInStructure(elementFromMap
) && isInStructure(newElement
)) {
1294 if (parentNode
.getUserObject() instanceof NodeDescriptor
) {
1295 final NodeDescriptor parentDescriptor
= (NodeDescriptor
)parentNode
.getUserObject();
1296 childDesc
= getTreeStructure().createDescriptor(elementFromMap
, parentDescriptor
);
1297 childNode
.setUserObject(childDesc
);
1298 newElement
= elementFromMap
;
1299 forceRemapping
= true;
1300 update(childDesc
, false);
1306 if (childDesc
.getIndex() != index
.intValue()) {
1309 childDesc
.setIndex(index
.intValue());
1312 if (index
!= null && changes
) {
1313 updateNodeImageAndPosition(childNode
);
1315 if (!oldElement
.equals(newElement
) | forceRemapping
) {
1316 removeMapping(oldElement
, childNode
, newElement
);
1317 if (newElement
!= null) {
1318 createMapping(newElement
, childNode
);
1322 if (index
== null) {
1323 int selectedIndex
= -1;
1324 if (TreeBuilderUtil
.isNodeOrChildSelected(myTree
, childNode
)) {
1325 selectedIndex
= parentNode
.getIndex(childNode
);
1328 if (childNode
.getParent() instanceof DefaultMutableTreeNode
) {
1329 final DefaultMutableTreeNode parent
= (DefaultMutableTreeNode
)childNode
.getParent();
1330 if (myTree
.isExpanded(new TreePath(parent
.getPath()))) {
1331 if (parent
.getChildCount() == 1 && parent
.getChildAt(0) == childNode
) {
1332 insertLoadingNode(parent
, false);
1337 Object disposedElement
= getElementFor(childNode
);
1339 removeNodeFromParent(childNode
, selectedIndex
>= 0);
1340 disposeNode(childNode
);
1342 if (selectedIndex
>= 0) {
1343 if (parentNode
.getChildCount() > 0) {
1344 if (parentNode
.getChildCount() > selectedIndex
) {
1345 TreeNode newChildNode
= parentNode
.getChildAt(selectedIndex
);
1346 if (isValidForSelectionAdjusting(newChildNode
)) {
1347 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(newChildNode
)), true, getExpiredElementCondition(disposedElement
));
1351 TreeNode newChild
= parentNode
.getChildAt(parentNode
.getChildCount() - 1);
1352 if (isValidForSelectionAdjusting(newChild
)) {
1353 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(newChild
)), true, getExpiredElementCondition(disposedElement
));
1358 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(parentNode
)), true, getExpiredElementCondition(disposedElement
));
1363 elementToIndexMap
.remove(getBuilder().getTreeStructureElement(childDesc
));
1364 updateNodeChildren(childNode
, pass
, null, false);
1367 if (parentNode
.equals(getRootNode())) {
1368 myTreeModel
.nodeChanged(getRootNode());
1371 return new ActionCallback
.Done();
1374 private boolean isValidForSelectionAdjusting(TreeNode node
) {
1375 if (isLoadingNode(node
)) return true;
1377 final Object elementInTree
= getElementFor(node
);
1378 if (elementInTree
== null) return false;
1380 final TreeNode parentNode
= node
.getParent();
1381 final Object parentElementInTree
= getElementFor(parentNode
);
1382 if (parentElementInTree
== null) return false;
1384 final Object parentElement
= getTreeStructure().getParentElement(elementInTree
);
1386 return parentElementInTree
.equals(parentElement
);
1389 private Condition
getExpiredElementCondition(final Object element
) {
1390 return new Condition() {
1391 public boolean value(final Object o
) {
1392 return isInStructure(element
);
1397 private void addSelectionPath(final TreePath path
, final boolean isAdjustedSelection
, final Condition isExpiredAdjustement
) {
1398 doWithUpdaterState(new Runnable() {
1400 TreePath toSelect
= null;
1402 if (isLoadingNode(path
.getLastPathComponent())) {
1403 final TreePath parentPath
= path
.getParentPath();
1404 if (parentPath
!= null) {
1405 toSelect
= parentPath
;
1412 if (toSelect
!= null) {
1413 myTree
.addSelectionPath(toSelect
);
1415 if (isAdjustedSelection
&& myUpdaterState
!= null) {
1416 final Object toSelectElement
= getElementFor(toSelect
.getLastPathComponent());
1417 myUpdaterState
.addAdjustedSelection(toSelectElement
, isExpiredAdjustement
);
1424 private static TreePath
getPathFor(TreeNode node
) {
1425 if (node
instanceof DefaultMutableTreeNode
) {
1426 return new TreePath(((DefaultMutableTreeNode
)node
).getPath());
1429 ArrayList nodes
= new ArrayList();
1430 TreeNode eachParent
= node
;
1431 while (eachParent
!= null) {
1432 nodes
.add(eachParent
);
1433 eachParent
= eachParent
.getParent();
1436 return new TreePath(ArrayUtil
.toObjectArray(nodes
));
1441 private void removeNodeFromParent(final MutableTreeNode node
, final boolean willAdjustSelection
) {
1442 doWithUpdaterState(new Runnable() {
1444 if (willAdjustSelection
) {
1445 final TreePath path
= getPathFor(node
);
1446 if (myTree
.isPathSelected(path
)) {
1447 myTree
.removeSelectionPath(path
);
1451 myTreeModel
.removeNodeFromParent(node
);
1456 private void expandPath(final TreePath path
) {
1457 doWithUpdaterState(new Runnable() {
1459 myTree
.expandPath(path
);
1464 private void doWithUpdaterState(Runnable runnable
) {
1465 if (myUpdaterState
!= null) {
1466 myUpdaterState
.process(runnable
);
1473 protected boolean doUpdateNodeDescriptor(final NodeDescriptor descriptor
) {
1474 return descriptor
.update();
1477 private void addLoadingNode(final DefaultMutableTreeNode node
) {
1478 final boolean[] hasNoChildren
= new boolean[1];
1479 final NodeDescriptor descriptor
= (NodeDescriptor
)node
.getUserObject();
1480 if (!getBuilder().isAlwaysShowPlus(descriptor
)) {
1481 Runnable updateRunnable
= new Runnable() {
1483 if (isReleased()) return;
1485 if (hasNoChildren
[0]) {
1486 update(descriptor
, false);
1487 removeLoadingNode(node
);
1492 if (getTreeStructure().isToBuildChildrenInBackground(getBuilder().getTreeStructureElement(descriptor
))) {
1493 Runnable buildRunnable
= new Runnable() {
1495 if (isReleased()) return;
1497 update(descriptor
, true);
1498 Object element
= getBuilder().getTreeStructureElement(descriptor
);
1499 if (element
== null && !isValid(element
)) return;
1501 Object
[] children
= getChildrenFor(element
);
1502 hasNoChildren
[0] = children
.length
== 0;
1505 addTaskToWorker(buildRunnable
, false, updateRunnable
, new Runnable() {
1507 processNodeActionsIfReady(node
);
1512 Object
[] children
= getChildrenFor(getBuilder().getTreeStructureElement(descriptor
));
1513 if (children
.length
== 0) return;
1517 insertLoadingNode(node
, true);
1520 private boolean isValid(Object element
) {
1521 if (element
instanceof ValidateableNode
) {
1522 if (!((ValidateableNode
)element
).isValid()) return false;
1524 return getBuilder().validateNode(element
);
1527 private void insertLoadingNode(final DefaultMutableTreeNode node
, boolean addToUnbuilt
) {
1528 myTreeModel
.insertNodeInto(new LoadingNode(), node
, 0);
1530 myUnbuiltNodes
.add(node
);
1535 protected void addTaskToWorker(@NotNull final Runnable bgReadActionRunnable
,
1537 @Nullable final Runnable edtPostRunnable
,
1538 @Nullable final Runnable finilizeEdtRunnable
) {
1539 registerWorkerTask(bgReadActionRunnable
);
1541 final Runnable pooledThreadWithProgressRunnable
= new Runnable() {
1547 getBuilder().runBackgroundLoading(new Runnable() {
1554 bgReadActionRunnable
.run();
1556 if (edtPostRunnable
!= null) {
1557 getBuilder().updateAfterLoadedInBackground(new Runnable() {
1560 edtPostRunnable
.run();
1563 unregisterWorkerTask(bgReadActionRunnable
, finilizeEdtRunnable
);
1569 unregisterWorkerTask(bgReadActionRunnable
, finilizeEdtRunnable
);
1572 catch (ProcessCanceledException e
) {
1573 unregisterWorkerTask(bgReadActionRunnable
, finilizeEdtRunnable
);
1575 catch (Throwable t
) {
1576 unregisterWorkerTask(bgReadActionRunnable
, finilizeEdtRunnable
);
1577 throw new RuntimeException(t
);
1584 Runnable pooledThreadRunnable
= new Runnable() {
1586 if (isReleased()) return;
1589 if (myProgress
!= null) {
1590 ProgressManager
.getInstance().runProcess(pooledThreadWithProgressRunnable
, myProgress
);
1593 pooledThreadWithProgressRunnable
.run();
1596 catch (ProcessCanceledException e
) {
1602 if (myWorker
== null || myWorker
.isDisposed()) {
1603 myWorker
= new WorkerThread("AbstractTreeBuilder.Worker", 1);
1606 myWorker
.addTaskFirst(pooledThreadRunnable
);
1609 myWorker
.addTask(pooledThreadRunnable
);
1611 myWorker
.dispose(false);
1615 myWorker
.addTaskFirst(pooledThreadRunnable
);
1618 myWorker
.addTask(pooledThreadRunnable
);
1623 private void registerWorkerTask(Runnable runnable
) {
1624 synchronized (myActiveWorkerTasks
) {
1625 myActiveWorkerTasks
.add(runnable
);
1629 private void unregisterWorkerTask(Runnable runnable
, @Nullable Runnable finalizeRunnable
) {
1631 synchronized (myActiveWorkerTasks
) {
1632 wasRemoved
= myActiveWorkerTasks
.remove(runnable
);
1635 if (wasRemoved
&& finalizeRunnable
!= null) {
1636 finalizeRunnable
.run();
1640 public boolean isWorkerBusy() {
1641 synchronized (myActiveWorkerTasks
) {
1642 return myActiveWorkerTasks
.size() > 0;
1646 private void clearWorkerTasks() {
1647 synchronized (myActiveWorkerTasks
) {
1648 myActiveWorkerTasks
.clear();
1652 private void updateNodeImageAndPosition(final DefaultMutableTreeNode node
) {
1653 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
1654 NodeDescriptor descriptor
= (NodeDescriptor
)node
.getUserObject();
1655 if (getElementFromDescriptor(descriptor
) == null) return;
1656 DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)node
.getParent();
1657 if (parentNode
!= null) {
1658 int oldIndex
= parentNode
.getIndex(node
);
1659 int newIndex
= oldIndex
;
1660 if (isLoadingChildrenFor(node
.getParent()) || getBuilder().isChildrenResortingNeeded(descriptor
)) {
1661 final ArrayList
<TreeNode
> children
= new ArrayList
<TreeNode
>(parentNode
.getChildCount());
1662 for (int i
= 0; i
< parentNode
.getChildCount(); i
++) {
1663 children
.add(parentNode
.getChildAt(i
));
1665 Collections
.sort(children
, myNodeComparator
);
1666 newIndex
= children
.indexOf(node
);
1669 if (oldIndex
!= newIndex
) {
1670 List
<Object
> pathsToExpand
= new ArrayList
<Object
>();
1671 List
<Object
> selectionPaths
= new ArrayList
<Object
>();
1672 TreeBuilderUtil
.storePaths(getBuilder(), node
, pathsToExpand
, selectionPaths
, false);
1673 removeNodeFromParent(node
, false);
1674 myTreeModel
.insertNodeInto(node
, parentNode
, newIndex
);
1675 TreeBuilderUtil
.restorePaths(getBuilder(), pathsToExpand
, selectionPaths
, false);
1678 myTreeModel
.nodeChanged(node
);
1682 myTreeModel
.nodeChanged(node
);
1686 public DefaultTreeModel
getTreeModel() {
1690 private void insertNodesInto(ArrayList
<TreeNode
> nodes
, DefaultMutableTreeNode parentNode
) {
1691 if (nodes
.isEmpty()) return;
1693 nodes
= new ArrayList
<TreeNode
>(nodes
);
1694 Collections
.sort(nodes
, myNodeComparator
);
1696 ArrayList
<TreeNode
> all
= TreeUtil
.childrenToArray(parentNode
);
1698 Collections
.sort(all
, myNodeComparator
);
1700 int[] indices
= new int[nodes
.size()];
1702 for (int i
= 0; i
< nodes
.size(); i
++) {
1703 TreeNode node
= nodes
.get(i
);
1704 while (all
.get(idx
) != node
) idx
++;
1706 parentNode
.insert((MutableTreeNode
)node
, idx
);
1709 myTreeModel
.nodesWereInserted(parentNode
, indices
);
1712 private void disposeNode(DefaultMutableTreeNode node
) {
1713 myUpdatingChildren
.remove(node
);
1714 myUnbuiltNodes
.remove(node
);
1716 if (node
.getChildCount() > 0) {
1717 for (DefaultMutableTreeNode _node
= (DefaultMutableTreeNode
)node
.getFirstChild(); _node
!= null; _node
= _node
.getNextSibling()) {
1721 if (isLoadingNode(node
)) return;
1722 NodeDescriptor descriptor
= (NodeDescriptor
)node
.getUserObject();
1723 if (descriptor
== null) return;
1724 final Object element
= getElementFromDescriptor(descriptor
);
1725 removeMapping(element
, node
, null);
1726 node
.setUserObject(null);
1727 node
.removeAllChildren();
1730 public void addSubtreeToUpdate(final DefaultMutableTreeNode root
) {
1731 addSubtreeToUpdate(root
, null);
1734 public void addSubtreeToUpdate(final DefaultMutableTreeNode root
, Runnable runAfterUpdate
) {
1735 getUpdater().runAfterUpdate(runAfterUpdate
);
1736 getUpdater().addSubtreeToUpdate(root
);
1739 public boolean wasRootNodeInitialized() {
1740 return myRootNodeWasInitialized
;
1743 public void select(final Object
[] elements
, @Nullable final Runnable onDone
) {
1744 select(elements
, onDone
, false);
1747 public void select(final Object
[] elements
, @Nullable final Runnable onDone
, boolean addToSelection
) {
1748 select(elements
, onDone
, addToSelection
, false);
1751 public void select(final Object
[] elements
, @Nullable final Runnable onDone
, boolean addToSelection
, boolean deferred
) {
1752 _select(elements
, onDone
, addToSelection
, true, false, deferred
);
1755 void _select(final Object
[] elements
,
1756 final Runnable onDone
,
1757 final boolean addToSelection
,
1758 final boolean checkCurrentSelection
,
1759 final boolean checkIfInStructure
) {
1761 _select(elements
, onDone
, addToSelection
, checkCurrentSelection
, checkIfInStructure
, false);
1764 void _select(final Object
[] elements
,
1765 final Runnable onDone
,
1766 final boolean addToSelection
,
1767 final boolean checkCurrentSelection
,
1768 final boolean checkIfInStructure
,
1769 final boolean deferred
) {
1771 boolean willAffectSelection
= elements
.length
> 0 || (elements
.length
== 0 && addToSelection
);
1772 if (!willAffectSelection
) {
1777 final boolean oldCanProcessDeferredSelection
= myCanProcessDeferredSelections
;
1779 if (!deferred
&& wasRootNodeInitialized() && willAffectSelection
) {
1780 myCanProcessDeferredSelections
= false;
1783 if (!checkDeferred(deferred
, onDone
)) return;
1785 if (!deferred
&& oldCanProcessDeferredSelection
&& !myCanProcessDeferredSelections
) {
1786 getTree().clearSelection();
1790 runDone(new Runnable() {
1792 if (!checkDeferred(deferred
, onDone
)) return;
1794 final Set
<Object
> currentElements
= getSelectedElements();
1796 if (checkCurrentSelection
&& currentElements
.size() > 0 && elements
.length
== currentElements
.size()) {
1797 boolean runSelection
= false;
1798 for (Object eachToSelect
: elements
) {
1799 if (!currentElements
.contains(eachToSelect
)) {
1800 runSelection
= true;
1805 if (!runSelection
) {
1806 if (elements
.length
> 0) {
1807 selectVisible(elements
[0], onDone
, true);
1813 Set
<Object
> toSelect
= new HashSet
<Object
>();
1814 myTree
.clearSelection();
1815 toSelect
.addAll(Arrays
.asList(elements
));
1816 if (addToSelection
) {
1817 toSelect
.addAll(currentElements
);
1820 if (checkIfInStructure
) {
1821 final Iterator
<Object
> allToSelect
= toSelect
.iterator();
1822 while (allToSelect
.hasNext()) {
1823 Object each
= allToSelect
.next();
1824 if (!isInStructure(each
)) {
1825 allToSelect
.remove();
1830 final Object
[] elementsToSelect
= ArrayUtil
.toObjectArray(toSelect
);
1832 if (wasRootNodeInitialized()) {
1833 final int[] originalRows
= myTree
.getSelectionRows();
1834 if (!addToSelection
) {
1835 myTree
.clearSelection();
1837 addNext(elementsToSelect
, 0, onDone
, originalRows
, deferred
);
1840 myDeferredSelections
.clear();
1841 myDeferredSelections
.add(new Runnable() {
1843 select(elementsToSelect
, onDone
, false, true);
1851 private boolean checkDeferred(boolean isDeferred
, @Nullable Runnable onDone
) {
1852 if (!isDeferred
|| myCanProcessDeferredSelections
|| !wasRootNodeInitialized()) {
1861 final Set
<Object
> getSelectedElements() {
1862 final TreePath
[] paths
= myTree
.getSelectionPaths();
1864 Set
<Object
> result
= new HashSet
<Object
>();
1865 if (paths
!= null) {
1866 for (TreePath eachPath
: paths
) {
1867 if (eachPath
.getLastPathComponent() instanceof DefaultMutableTreeNode
) {
1868 final DefaultMutableTreeNode eachNode
= (DefaultMutableTreeNode
)eachPath
.getLastPathComponent();
1869 final Object eachElement
= getElementFor(eachNode
);
1870 if (eachElement
!= null) {
1871 result
.add(eachElement
);
1880 private void addNext(final Object
[] elements
, final int i
, @Nullable final Runnable onDone
, final int[] originalRows
, final boolean deferred
) {
1881 if (i
>= elements
.length
) {
1882 if (myTree
.isSelectionEmpty()) {
1883 myTree
.setSelectionRows(originalRows
);
1888 if (!checkDeferred(deferred
, onDone
)) {
1892 doSelect(elements
[i
], new Runnable() {
1894 if (!checkDeferred(deferred
, onDone
)) return;
1896 addNext(elements
, i
+ 1, onDone
, originalRows
, deferred
);
1902 public void select(final Object element
, @Nullable final Runnable onDone
) {
1903 select(element
, onDone
, false);
1906 public void select(final Object element
, @Nullable final Runnable onDone
, boolean addToSelection
) {
1907 _select(new Object
[] {element
}, onDone
, addToSelection
, true, false);
1910 private void doSelect(final Object element
, final Runnable onDone
, final boolean addToSelection
, final boolean deferred
) {
1911 final Runnable _onDone
= new Runnable() {
1913 if (!checkDeferred(deferred
, onDone
)) return;
1914 selectVisible(element
, onDone
, addToSelection
);
1917 _expand(element
, _onDone
, true, false);
1920 private void selectVisible(Object element
, final Runnable onDone
, boolean addToSelection
) {
1921 final DefaultMutableTreeNode toSelect
= getNodeForElement(element
, false);
1922 if (toSelect
== null) {
1926 final int row
= myTree
.getRowForPath(new TreePath(toSelect
.getPath()));
1928 if (myUpdaterState
!= null) {
1929 myUpdaterState
.addSelection(element
);
1931 TreeUtil
.showAndSelect(myTree
, row
- 2, row
+ 2, row
, -1, addToSelection
).doWhenDone(new Runnable() {
1938 public void expand(final Object element
, @Nullable final Runnable onDone
) {
1939 expand(element
, onDone
, false);
1943 void expand(final Object element
, @Nullable final Runnable onDone
, boolean checkIfInStructure
) {
1944 _expand(new Object
[]{element
}, onDone
== null ?
new EmptyRunnable() : onDone
, false, checkIfInStructure
);
1947 void expand(final Object
[] element
, @Nullable final Runnable onDone
, boolean checkIfInStructure
) {
1948 _expand(element
, onDone
== null ?
new EmptyRunnable() : onDone
, false, checkIfInStructure
);
1951 void _expand(final Object
[] element
, @NotNull final Runnable onDone
, final boolean parentsOnly
, final boolean checkIfInStructure
) {
1952 runDone(new Runnable() {
1954 if (element
.length
== 0) {
1959 if (myUpdaterState
!= null) {
1960 myUpdaterState
.clearExpansion();
1964 final ActionCallback done
= new ActionCallback(element
.length
);
1965 done
.doWhenDone(new Runnable() {
1971 for (final Object toExpand
: element
) {
1972 _expand(toExpand
, new Runnable() {
1976 }, parentsOnly
, checkIfInStructure
);
1982 public void collapseChildren(final Object element
, @Nullable final Runnable onDone
) {
1983 runDone(new Runnable() {
1985 final DefaultMutableTreeNode node
= getNodeForElement(element
, false);
1987 getTree().collapsePath(new TreePath(node
.getPath()));
1994 private void runDone(@Nullable Runnable done
) {
1995 if (done
== null) return;
1997 if (isYeildingNow()) {
1998 if (!myYeildingDoneRunnables
.contains(done
)) {
1999 myYeildingDoneRunnables
.add(done
);
2007 private void _expand(final Object element
, @NotNull final Runnable onDone
, final boolean parentsOnly
, boolean checkIfInStructure
) {
2008 if (checkIfInStructure
&& !isInStructure(element
)) {
2013 if (wasRootNodeInitialized()) {
2014 List
<Object
> kidsToExpand
= new ArrayList
<Object
>();
2015 Object eachElement
= element
;
2016 DefaultMutableTreeNode firstVisible
= null;
2018 if (!isValid(eachElement
)) break;
2020 firstVisible
= getNodeForElement(eachElement
, true);
2021 if (eachElement
!= element
|| !parentsOnly
) {
2022 assert !kidsToExpand
.contains(eachElement
) :
2023 "Not a valid tree structure, walking up the structure gives many entries for element=" +
2026 getTreeStructure().getRootElement();
2027 kidsToExpand
.add(eachElement
);
2029 if (firstVisible
!= null) break;
2030 eachElement
= getTreeStructure().getParentElement(eachElement
);
2031 if (eachElement
== null) {
2032 firstVisible
= null;
2037 if (firstVisible
== null) {
2040 else if (kidsToExpand
.size() == 0) {
2041 final DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)firstVisible
.getParent();
2042 if (parentNode
!= null) {
2043 final TreePath parentPath
= new TreePath(parentNode
.getPath());
2044 if (!myTree
.isExpanded(parentPath
)) {
2051 processExpand(firstVisible
, kidsToExpand
, kidsToExpand
.size() - 1, onDone
);
2055 myDeferredExpansions
.add(new Runnable() {
2057 _expand(element
, onDone
, parentsOnly
, false);
2063 private void processExpand(final DefaultMutableTreeNode toExpand
,
2064 final List kidsToExpand
,
2065 final int expandIndex
,
2066 @NotNull final Runnable onDone
) {
2067 final Object element
= getElementFor(toExpand
);
2068 if (element
== null) {
2073 addNodeAction(element
, new NodeAction() {
2074 public void onReady(final DefaultMutableTreeNode node
) {
2075 if (node
.getChildCount() >= 0 && !myTree
.isExpanded(new TreePath(node
.getPath()))) {
2076 final ArrayList list
= new ArrayList();
2077 for (int i
= 0; i
< node
.getChildCount(); i
++) {
2078 list
.add(node
.getChildAt(i
));
2083 if (expandIndex
< 0) {
2088 final DefaultMutableTreeNode nextNode
= getNodeForElement(kidsToExpand
.get(expandIndex
), false);
2089 if (nextNode
!= null) {
2090 processExpand(nextNode
, kidsToExpand
, expandIndex
- 1, onDone
);
2103 private Object
getElementFor(Object node
) {
2104 if (!(node
instanceof DefaultMutableTreeNode
)) return null;
2105 return getElementFor((DefaultMutableTreeNode
)node
);
2109 private Object
getElementFor(DefaultMutableTreeNode node
) {
2111 final Object o
= node
.getUserObject();
2112 if (o
instanceof NodeDescriptor
) {
2113 return getElementFromDescriptor(((NodeDescriptor
)o
));
2120 public final boolean isNodeBeingBuilt(final TreePath path
) {
2121 return isNodeBeingBuilt(path
.getLastPathComponent());
2124 public final boolean isNodeBeingBuilt(Object node
) {
2125 if (isParentLoading(node
) || isLoadingParent(node
)) return true;
2127 final boolean childrenAreNoLoadedYet
= isLoadingChildrenFor(node
) && myUnbuiltNodes
.contains(node
);
2128 if (childrenAreNoLoadedYet
) {
2129 if (node
instanceof DefaultMutableTreeNode
) {
2130 final TreePath nodePath
= new TreePath(((DefaultMutableTreeNode
)node
).getPath());
2131 if (!myTree
.isExpanded(nodePath
)) return false;
2141 private boolean isLoadingParent(Object node
) {
2142 if (!(node
instanceof DefaultMutableTreeNode
)) return false;
2143 return myLoadingParents
.contains(getElementFor((DefaultMutableTreeNode
)node
));
2146 public void setTreeStructure(final AbstractTreeStructure treeStructure
) {
2147 myTreeStructure
= treeStructure
;
2148 clearUpdaterState();
2151 public AbstractTreeUpdater
getUpdater() {
2155 public void setUpdater(final AbstractTreeUpdater updater
) {
2156 myUpdater
= updater
;
2159 public DefaultMutableTreeNode
getRootNode() {
2163 public void setRootNode(@NotNull final DefaultMutableTreeNode rootNode
) {
2164 myRootNode
= rootNode
;
2167 private void dropUpdaterStateIfExternalChange() {
2168 if (myUpdaterState
!= null && !myUpdaterState
.isProcessingNow()) {
2169 clearUpdaterState();
2173 private void clearUpdaterState() {
2174 myUpdaterState
= null;
2177 private void createMapping(Object element
, DefaultMutableTreeNode node
) {
2178 if (!myElementToNodeMap
.containsKey(element
)) {
2179 myElementToNodeMap
.put(element
, node
);
2182 final Object value
= myElementToNodeMap
.get(element
);
2183 final List
<DefaultMutableTreeNode
> nodes
;
2184 if (value
instanceof DefaultMutableTreeNode
) {
2185 nodes
= new ArrayList
<DefaultMutableTreeNode
>();
2186 nodes
.add((DefaultMutableTreeNode
)value
);
2187 myElementToNodeMap
.put(element
, nodes
);
2190 nodes
= (List
<DefaultMutableTreeNode
>)value
;
2196 private void removeMapping(Object element
, DefaultMutableTreeNode node
, @Nullable Object elementToPutNodeActionsFor
) {
2197 final Object value
= myElementToNodeMap
.get(element
);
2198 if (value
!= null) {
2199 if (value
instanceof DefaultMutableTreeNode
) {
2200 if (value
.equals(node
)) {
2201 myElementToNodeMap
.remove(element
);
2205 List
<DefaultMutableTreeNode
> nodes
= (List
<DefaultMutableTreeNode
>)value
;
2206 final boolean reallyRemoved
= nodes
.remove(node
);
2207 if (reallyRemoved
) {
2208 if (nodes
.isEmpty()) {
2209 myElementToNodeMap
.remove(element
);
2215 final List
<NodeAction
> actions
= myNodeActions
.get(element
);
2216 myNodeActions
.remove(element
);
2218 if (elementToPutNodeActionsFor
!= null) {
2219 myNodeActions
.put(elementToPutNodeActionsFor
, actions
);
2223 private DefaultMutableTreeNode
getFirstNode(Object element
) {
2224 return findNode(element
, 0);
2227 private DefaultMutableTreeNode
findNode(final Object element
, int startIndex
) {
2228 final Object value
= getBuilder().findNodeByElement(element
);
2229 if (value
== null) {
2232 if (value
instanceof DefaultMutableTreeNode
) {
2233 return startIndex
== 0 ?
(DefaultMutableTreeNode
)value
: null;
2235 final List
<DefaultMutableTreeNode
> nodes
= (List
<DefaultMutableTreeNode
>)value
;
2236 return startIndex
< nodes
.size() ? nodes
.get(startIndex
) : null;
2239 protected Object
findNodeByElement(Object element
) {
2240 if (myElementToNodeMap
.containsKey(element
)) {
2241 return myElementToNodeMap
.get(element
);
2245 TREE_NODE_WRAPPER
.setValue(element
);
2246 return myElementToNodeMap
.get(TREE_NODE_WRAPPER
);
2249 TREE_NODE_WRAPPER
.setValue(null);
2253 private DefaultMutableTreeNode
findNodeForChildElement(DefaultMutableTreeNode parentNode
, Object element
) {
2254 final Object value
= myElementToNodeMap
.get(element
);
2255 if (value
== null) {
2259 if (value
instanceof DefaultMutableTreeNode
) {
2260 final DefaultMutableTreeNode elementNode
= (DefaultMutableTreeNode
)value
;
2261 return parentNode
.equals(elementNode
.getParent()) ? elementNode
: null;
2264 final List
<DefaultMutableTreeNode
> allNodesForElement
= (List
<DefaultMutableTreeNode
>)value
;
2265 for (final DefaultMutableTreeNode elementNode
: allNodesForElement
) {
2266 if (parentNode
.equals(elementNode
.getParent())) {
2274 public void cancelBackgroundLoading() {
2275 if (myWorker
!= null) {
2276 myWorker
.cancelTasks();
2279 myNodeActions
.clear();
2282 private void addNodeAction(Object element
, NodeAction action
) {
2283 maybeSetBusyAndScheduleWaiterForReady(true);
2285 List
<NodeAction
> list
= myNodeActions
.get(element
);
2287 list
= new ArrayList
<NodeAction
>();
2288 myNodeActions
.put(element
, list
);
2293 private void cleanUpNow() {
2294 if (isReleased()) return;
2296 final UpdaterTreeState state
= new UpdaterTreeState(this);
2298 myTree
.collapsePath(new TreePath(myTree
.getModel().getRoot()));
2299 myTree
.clearSelection();
2300 getRootNode().removeAllChildren();
2302 myRootNodeWasInitialized
= false;
2303 myNodeActions
.clear();
2304 myElementToNodeMap
.clear();
2305 myDeferredSelections
.clear();
2306 myDeferredExpansions
.clear();
2307 myLoadingParents
.clear();
2308 myUnbuiltNodes
.clear();
2309 myUpdateFromRootRequested
= true;
2311 if (myWorker
!= null) {
2312 Disposer
.dispose(myWorker
);
2316 myTree
.invalidate();
2321 public AbstractTreeUi
setClearOnHideDelay(final long clearOnHideDelay
) {
2322 myClearOnHideDelay
= clearOnHideDelay
;
2326 public void setJantorPollPeriod(final long time
) {
2327 myJanitorPollPeriod
= time
;
2330 public void setCheckStructure(final boolean checkStructure
) {
2331 myCheckStructure
= checkStructure
;
2334 private class MySelectionListener
implements TreeSelectionListener
{
2335 public void valueChanged(final TreeSelectionEvent e
) {
2336 dropUpdaterStateIfExternalChange();
2340 private class MyExpansionListener
implements TreeExpansionListener
{
2341 public void treeExpanded(TreeExpansionEvent event
) {
2342 dropUpdaterStateIfExternalChange();
2344 TreePath path
= event
.getPath();
2345 final DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
2347 if (!myUnbuiltNodes
.contains(node
)) {
2350 myUnbuiltNodes
.remove(node
);
2353 getBuilder().expandNodeChildren(node
);
2355 runDone(new Runnable() {
2357 final Object element
= getElementFor(node
);
2359 for (int i
= 0; i
< node
.getChildCount(); i
++) {
2360 removeIfLoading(node
.getChildAt(i
));
2363 if (node
.getChildCount() == 0) {
2364 addNodeAction(element
, new NodeAction() {
2365 public void onReady(final DefaultMutableTreeNode node
) {
2366 expand(element
, null);
2371 processSmartExpand(node
);
2376 public void treeCollapsed(TreeExpansionEvent e
) {
2377 final TreePath path
= e
.getPath();
2378 final DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
2379 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
2381 TreePath pathToSelect
= null;
2382 if (isSelectionInside(node
)) {
2383 pathToSelect
= new TreePath(node
.getPath());
2387 NodeDescriptor descriptor
= (NodeDescriptor
)node
.getUserObject();
2388 if (getBuilder().isDisposeOnCollapsing(descriptor
)) {
2389 runDone(new Runnable() {
2391 if (isDisposed(node
)) return;
2393 TreePath nodePath
= new TreePath(node
.getPath());
2394 if (myTree
.isExpanded(nodePath
)) return;
2396 removeChildren(node
);
2397 addLoadingNode(node
);
2400 if (node
.equals(getRootNode())) {
2401 addSelectionPath(new TreePath(getRootNode().getPath()), true, Condition
.FALSE
);
2404 myTreeModel
.reload(node
);
2408 if (pathToSelect
!= null && myTree
.isSelectionEmpty()) {
2409 addSelectionPath(pathToSelect
, true, Condition
.FALSE
);
2413 private void removeChildren(DefaultMutableTreeNode node
) {
2414 EnumerationCopy copy
= new EnumerationCopy(node
.children());
2415 while (copy
.hasMoreElements()) {
2416 disposeNode((DefaultMutableTreeNode
)copy
.nextElement());
2418 node
.removeAllChildren();
2419 myTreeModel
.nodeStructureChanged(node
);
2422 private boolean isSelectionInside(DefaultMutableTreeNode parent
) {
2423 TreePath path
= new TreePath(myTreeModel
.getPathToRoot(parent
));
2424 TreePath
[] paths
= myTree
.getSelectionPaths();
2425 if (paths
== null) return false;
2426 for (TreePath path1
: paths
) {
2427 if (path
.isDescendant(path1
)) return true;
2433 public boolean isInStructure(@Nullable Object element
) {
2434 Object eachParent
= element
;
2435 while (eachParent
!= null) {
2436 if (getTreeStructure().getRootElement().equals(eachParent
)) return true;
2437 eachParent
= getTreeStructure().getParentElement(eachParent
);
2443 interface NodeAction
{
2444 void onReady(DefaultMutableTreeNode node
);
2447 public void setCanYield(final boolean canYield
) {
2448 myCanYield
= canYield
;
2451 public Collection
<TreeUpdatePass
> getYeildingPasses() {
2452 return myYeildingPasses
;
2455 public boolean isBuilt(Object element
) {
2456 if (!myElementToNodeMap
.containsKey(element
)) return false;
2457 final Object node
= myElementToNodeMap
.get(element
);
2458 return !myUnbuiltNodes
.contains(node
);