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
.diagnostic
.Logger
;
7 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
8 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
9 import com
.intellij
.openapi
.progress
.ProgressManager
;
10 import com
.intellij
.openapi
.project
.IndexNotReadyException
;
11 import com
.intellij
.openapi
.util
.*;
12 import com
.intellij
.openapi
.util
.registry
.Registry
;
13 import com
.intellij
.openapi
.util
.registry
.RegistryValue
;
14 import com
.intellij
.ui
.LoadingNode
;
15 import com
.intellij
.ui
.treeStructure
.Tree
;
16 import com
.intellij
.util
.Alarm
;
17 import com
.intellij
.util
.ArrayUtil
;
18 import com
.intellij
.util
.ConcurrencyUtil
;
19 import com
.intellij
.util
.Time
;
20 import com
.intellij
.util
.concurrency
.WorkerThread
;
21 import com
.intellij
.util
.containers
.HashSet
;
22 import com
.intellij
.util
.enumeration
.EnumerationCopy
;
23 import com
.intellij
.util
.ui
.UIUtil
;
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
.awt
.event
.FocusAdapter
;
38 import java
.awt
.event
.FocusEvent
;
39 import java
.security
.AccessControlException
;
41 import java
.util
.List
;
42 import java
.util
.concurrent
.ScheduledExecutorService
;
43 import java
.util
.concurrent
.TimeUnit
;
45 public class AbstractTreeUi
{
46 private static final Logger LOG
= Logger
.getInstance("#com.intellij.ide.util.treeView.AbstractTreeBuilder");
47 protected JTree myTree
;// protected for TestNG
48 @SuppressWarnings({"WeakerAccess"}) protected DefaultTreeModel myTreeModel
;
49 private AbstractTreeStructure myTreeStructure
;
50 private AbstractTreeUpdater myUpdater
;
51 private Comparator
<NodeDescriptor
> myNodeDescriptorComparator
;
52 private final Comparator
<TreeNode
> myNodeComparator
= new Comparator
<TreeNode
>() {
53 public int compare(TreeNode n1
, TreeNode n2
) {
54 if (isLoadingNode(n1
) || isLoadingNode(n2
)) return 0;
55 NodeDescriptor nodeDescriptor1
= getDescriptorFrom(((DefaultMutableTreeNode
)n1
));
56 NodeDescriptor nodeDescriptor2
= getDescriptorFrom(((DefaultMutableTreeNode
)n2
));
57 return myNodeDescriptorComparator
!= null
58 ? myNodeDescriptorComparator
.compare(nodeDescriptor1
, nodeDescriptor2
)
59 : nodeDescriptor1
.getIndex() - nodeDescriptor2
.getIndex();
62 private DefaultMutableTreeNode myRootNode
;
63 private final HashMap
<Object
, Object
> myElementToNodeMap
= new HashMap
<Object
, Object
>();
64 private final HashSet
<DefaultMutableTreeNode
> myUnbuiltNodes
= new HashSet
<DefaultMutableTreeNode
>();
65 private TreeExpansionListener myExpansionListener
;
66 private MySelectionListener mySelectionListener
;
68 private WorkerThread myWorker
= null;
69 private final Set
<Runnable
> myActiveWorkerTasks
= new HashSet
<Runnable
>();
71 private ProgressIndicator myProgress
;
72 private static final int WAIT_CURSOR_DELAY
= 100;
73 private AbstractTreeNode
<Object
> TREE_NODE_WRAPPER
;
74 private boolean myRootNodeWasInitialized
= false;
75 private final Map
<Object
, List
<NodeAction
>> myNodeActions
= new HashMap
<Object
, List
<NodeAction
>>();
76 private boolean myUpdateFromRootRequested
;
77 private boolean myWasEverShown
;
78 private boolean myUpdateIfInactive
;
80 private final Set
<Object
> myLoadingParents
= new HashSet
<Object
>();
81 private final Map
<Object
, List
<NodeAction
>> myNodeChildrenActions
= new HashMap
<Object
, List
<NodeAction
>>();
83 private long myClearOnHideDelay
= -1;
84 private ScheduledExecutorService ourClearanceService
;
85 private final Map
<AbstractTreeUi
, Long
> ourUi2Countdown
= Collections
.synchronizedMap(new WeakHashMap
<AbstractTreeUi
, Long
>());
87 private final Set
<Runnable
> myDeferredSelections
= new HashSet
<Runnable
>();
88 private final Set
<Runnable
> myDeferredExpansions
= new HashSet
<Runnable
>();
90 private boolean myCanProcessDeferredSelections
;
92 private UpdaterTreeState myUpdaterState
;
93 private AbstractTreeBuilder myBuilder
;
95 private final Set
<DefaultMutableTreeNode
> myUpdatingChildren
= new HashSet
<DefaultMutableTreeNode
>();
96 private long myJanitorPollPeriod
= Time
.SECOND
* 10;
97 private boolean myCheckStructure
= false;
100 private boolean myCanYield
= false;
102 private final List
<TreeUpdatePass
> myYeildingPasses
= new ArrayList
<TreeUpdatePass
>();
104 private boolean myYeildingNow
;
106 private final Set
<DefaultMutableTreeNode
> myPendingNodeActions
= new HashSet
<DefaultMutableTreeNode
>();
107 private final Set
<Runnable
> myYeildingDoneRunnables
= new HashSet
<Runnable
>();
109 private final Alarm myBusyAlarm
= new Alarm();
110 private final Runnable myWaiterForReady
= new Runnable() {
112 maybeSetBusyAndScheduleWaiterForReady(false);
116 private final RegistryValue myYeildingUpdate
= Registry
.get("ide.tree.yeildingUiUpdate");
117 private final RegistryValue myShowBusyIndicator
= Registry
.get("ide.tree.showBusyIndicator");
118 private final RegistryValue myWaitForReadyTime
= Registry
.get("ide.tree.waitForReadyTimout");
120 private boolean myWasEverIndexNotReady
;
121 private boolean myShowing
;
122 private FocusAdapter myFocusListener
= new FocusAdapter() {
124 public void focusGained(FocusEvent e
) {
128 private Set
<DefaultMutableTreeNode
> myNotForSmartExpand
= new HashSet
<DefaultMutableTreeNode
>();
129 private TreePath myRequestedExpand
;
130 private ActionCallback myInitialized
= new ActionCallback();
131 private Map
<Object
, ActionCallback
> myReadyCallbacks
= new WeakHashMap
<Object
, ActionCallback
>();
133 protected final void init(AbstractTreeBuilder builder
,
135 DefaultTreeModel treeModel
,
136 AbstractTreeStructure treeStructure
,
137 @Nullable Comparator
<NodeDescriptor
> comparator
) {
139 init(builder
, tree
, treeModel
, treeStructure
, comparator
, true);
142 protected void init(AbstractTreeBuilder builder
,
144 DefaultTreeModel treeModel
,
145 AbstractTreeStructure treeStructure
,
146 @Nullable Comparator
<NodeDescriptor
> comparator
,
147 boolean updateIfInactive
) {
150 myTreeModel
= treeModel
;
151 TREE_NODE_WRAPPER
= getBuilder().createSearchingTreeNodeWrapper();
152 myTree
.setModel(myTreeModel
);
153 setRootNode((DefaultMutableTreeNode
)treeModel
.getRoot());
154 setTreeStructure(treeStructure
);
155 myNodeDescriptorComparator
= comparator
;
156 myUpdateIfInactive
= updateIfInactive
;
158 myExpansionListener
= new MyExpansionListener();
159 myTree
.addTreeExpansionListener(myExpansionListener
);
161 mySelectionListener
= new MySelectionListener();
162 myTree
.addTreeSelectionListener(mySelectionListener
);
164 setUpdater(getBuilder().createUpdater());
165 myProgress
= getBuilder().createProgressIndicator();
166 Disposer
.register(getBuilder(), getUpdater());
168 final UiNotifyConnector uiNotify
= new UiNotifyConnector(tree
, new Activatable() {
169 public void showNotify() {
171 myWasEverShown
= true;
177 public void hideNotify() {
184 Disposer
.register(getBuilder(), uiNotify
);
186 myTree
.addFocusListener(myFocusListener
);
190 private boolean isNodeActionsPending() {
191 return !myNodeActions
.isEmpty() || !myNodeChildrenActions
.isEmpty();
194 private void clearNodeActions() {
195 myNodeActions
.clear();
196 myNodeChildrenActions
.clear();
199 private void maybeSetBusyAndScheduleWaiterForReady(boolean forcedBusy
) {
200 if (!myShowBusyIndicator
.asBoolean() || !canYield()) return;
202 if (myTree
instanceof com
.intellij
.ui
.treeStructure
.Tree
) {
203 final com
.intellij
.ui
.treeStructure
.Tree tree
= (Tree
)myTree
;
204 final boolean isBusy
= !isReady() || forcedBusy
;
205 if (isBusy
&& tree
.isShowing()) {
206 tree
.setPaintBusy(true);
207 myBusyAlarm
.cancelAllRequests();
208 myBusyAlarm
.addRequest(myWaiterForReady
, myWaitForReadyTime
.asInteger());
211 tree
.setPaintBusy(false);
216 private void initClearanceServiceIfNeeded() {
217 if (ourClearanceService
!= null) return;
219 ourClearanceService
= ConcurrencyUtil
.newSingleScheduledThreadExecutor("AbstractTreeBuilder's janitor");
220 ourClearanceService
.scheduleWithFixedDelay(new Runnable() {
224 }, myJanitorPollPeriod
, myJanitorPollPeriod
, TimeUnit
.MILLISECONDS
);
227 private void cleanUpAll() {
228 final long now
= System
.currentTimeMillis();
229 final AbstractTreeUi
[] uis
= ourUi2Countdown
.keySet().toArray(new AbstractTreeUi
[ourUi2Countdown
.size()]);
230 for (AbstractTreeUi eachUi
: uis
) {
231 if (eachUi
== null) continue;
232 final Long timeToCleanup
= ourUi2Countdown
.get(eachUi
);
233 if (timeToCleanup
== null) continue;
234 if (now
>= timeToCleanup
.longValue()) {
235 ourUi2Countdown
.remove(eachUi
);
236 getBuilder().cleanUp();
241 protected void doCleanUp() {
242 final Application app
= ApplicationManager
.getApplication();
243 if (app
!= null && app
.isUnitTestMode()) {
248 //noinspection SSBasedInspection
249 SwingUtilities
.invokeLater(new Runnable() {
259 private void disposeClearanceService() {
261 if (ourClearanceService
!= null) {
262 ourClearanceService
.shutdown();
263 ourClearanceService
= null;
266 catch (AccessControlException e
) {
271 public void activate(boolean byShowing
) {
272 myCanProcessDeferredSelections
= true;
273 ourUi2Countdown
.remove(this);
275 if (!myWasEverShown
|| myUpdateFromRootRequested
|| myUpdateIfInactive
) {
276 getBuilder().updateFromRoot();
279 getUpdater().showNotify();
281 myWasEverShown
|= byShowing
;
284 public void deactivate() {
285 getUpdater().hideNotify();
286 myBusyAlarm
.cancelAllRequests();
288 if (!myWasEverShown
) return;
290 if (isNodeActionsPending()) {
291 cancelBackgroundLoading();
292 myUpdateFromRootRequested
= true;
295 if (getClearOnHideDelay() >= 0) {
296 ourUi2Countdown
.put(this, System
.currentTimeMillis() + getClearOnHideDelay());
297 initClearanceServiceIfNeeded();
302 public void release() {
303 if (isReleased()) return;
305 myTree
.removeTreeExpansionListener(myExpansionListener
);
306 myTree
.removeTreeSelectionListener(mySelectionListener
);
307 myTree
.removeFocusListener(myFocusListener
);
309 disposeNode(getRootNode());
310 myElementToNodeMap
.clear();
311 getUpdater().cancelAllRequests();
312 if (myWorker
!= null) {
313 myWorker
.dispose(true);
316 TREE_NODE_WRAPPER
.setValue(null);
317 if (myProgress
!= null) {
320 disposeClearanceService();
325 //todo [kirillk] afraid to do so just in release day, to uncomment
326 // myTreeStructure = null;
331 myDeferredSelections
.clear();
332 myDeferredExpansions
.clear();
333 myYeildingDoneRunnables
.clear();
336 public boolean isReleased() {
337 return myBuilder
== null;
340 protected void doExpandNodeChildren(final DefaultMutableTreeNode node
) {
341 getTreeStructure().commit();
342 getUpdater().addSubtreeToUpdate(node
);
343 getUpdater().performUpdate();
346 public final AbstractTreeStructure
getTreeStructure() {
347 return myTreeStructure
;
350 public final JTree
getTree() {
355 private NodeDescriptor
getDescriptorFrom(DefaultMutableTreeNode node
) {
356 return (NodeDescriptor
)node
.getUserObject();
360 public final DefaultMutableTreeNode
getNodeForElement(Object element
, final boolean validateAgainstStructure
) {
361 DefaultMutableTreeNode result
= null;
362 if (validateAgainstStructure
) {
365 final DefaultMutableTreeNode node
= findNode(element
, index
);
366 if (node
== null) break;
368 if (isNodeValidForElement(element
, node
)) {
377 result
= getFirstNode(element
);
381 if (result
!= null && !isNodeInStructure(result
)) {
389 private boolean isNodeInStructure(DefaultMutableTreeNode node
) {
390 return TreeUtil
.isAncestor(getRootNode(), node
) && getRootNode() == myTreeModel
.getRoot();
393 private boolean isNodeValidForElement(final Object element
, final DefaultMutableTreeNode node
) {
394 return isSameHierarchy(element
, node
) || isValidChildOfParent(element
, node
);
397 private boolean isValidChildOfParent(final Object element
, final DefaultMutableTreeNode node
) {
398 final DefaultMutableTreeNode parent
= (DefaultMutableTreeNode
)node
.getParent();
399 final Object parentElement
= getElementFor(parent
);
400 if (!isInStructure(parentElement
)) return false;
402 if (parent
instanceof ElementNode
) {
403 return ((ElementNode
)parent
).isValidChild(element
);
406 for (int i
= 0; i
< parent
.getChildCount(); i
++) {
407 final TreeNode child
= parent
.getChildAt(i
);
408 final Object eachElement
= getElementFor(child
);
409 if (element
.equals(eachElement
)) return true;
416 private boolean isSameHierarchy(Object eachParent
, DefaultMutableTreeNode eachParentNode
) {
417 boolean valid
= true;
419 if (eachParent
== null) {
420 valid
= eachParentNode
== null;
424 if (!eachParent
.equals(getElementFor(eachParentNode
))) {
429 eachParent
= getTreeStructure().getParentElement(eachParent
);
430 eachParentNode
= (DefaultMutableTreeNode
)eachParentNode
.getParent();
435 public final DefaultMutableTreeNode
getNodeForPath(Object
[] path
) {
436 DefaultMutableTreeNode node
= null;
437 for (final Object pathElement
: path
) {
438 node
= node
== null ?
getFirstNode(pathElement
) : findNodeForChildElement(node
, pathElement
);
446 public final void buildNodeForElement(Object element
) {
447 getUpdater().performUpdate();
448 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
450 final java
.util
.List
<Object
> elements
= new ArrayList
<Object
>();
452 element
= getTreeStructure().getParentElement(element
);
453 if (element
== null) {
456 elements
.add(0, element
);
459 for (final Object element1
: elements
) {
460 node
= getNodeForElement(element1
, false);
468 public final void buildNodeForPath(Object
[] path
) {
469 getUpdater().performUpdate();
470 DefaultMutableTreeNode node
= null;
471 for (final Object pathElement
: path
) {
472 node
= node
== null ?
getFirstNode(pathElement
) : findNodeForChildElement(node
, pathElement
);
473 if (node
!= null && node
!= path
[path
.length
- 1]) {
479 public final void setNodeDescriptorComparator(Comparator
<NodeDescriptor
> nodeDescriptorComparator
) {
480 myNodeDescriptorComparator
= nodeDescriptorComparator
;
481 List
<Object
> pathsToExpand
= new ArrayList
<Object
>();
482 List
<Object
> selectionPaths
= new ArrayList
<Object
>();
483 TreeBuilderUtil
.storePaths(getBuilder(), getRootNode(), pathsToExpand
, selectionPaths
, false);
484 resortChildren(getRootNode());
485 myTreeModel
.nodeStructureChanged(getRootNode());
486 TreeBuilderUtil
.restorePaths(getBuilder(), pathsToExpand
, selectionPaths
, false);
489 protected AbstractTreeBuilder
getBuilder() {
493 private void resortChildren(DefaultMutableTreeNode node
) {
494 ArrayList
<TreeNode
> childNodes
= TreeUtil
.childrenToArray(node
);
495 node
.removeAllChildren();
496 sortChildren(node
, childNodes
);
497 for (TreeNode childNode1
: childNodes
) {
498 DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)childNode1
;
500 resortChildren(childNode
);
504 protected final void initRootNode() {
505 if (myUpdateIfInactive
) {
508 myUpdateFromRootRequested
= true;
512 private void initRootNodeNowIfNeeded(TreeUpdatePass pass
) {
513 if (myRootNodeWasInitialized
) return;
515 myRootNodeWasInitialized
= true;
517 Object rootElement
= getTreeStructure().getRootElement();
518 addNodeAction(rootElement
, new NodeAction() {
519 public void onReady(final DefaultMutableTreeNode node
) {
520 processDeferredActions();
525 NodeDescriptor rootDescriptor
= getTreeStructure().createDescriptor(rootElement
, null);
526 getRootNode().setUserObject(rootDescriptor
);
527 update(rootDescriptor
, false);
528 if (getElementFromDescriptor(rootDescriptor
) != null) {
529 createMapping(getElementFromDescriptor(rootDescriptor
), getRootNode());
533 insertLoadingNode(getRootNode(), true);
535 boolean willUpdate
= false;
536 if (isAutoExpand(rootDescriptor
)) {
537 willUpdate
= myUnbuiltNodes
.contains(getRootNode());
538 expand(getRootNode(), true);
541 updateNodeChildren(getRootNode(), pass
, null, false, isAutoExpand(rootDescriptor
), false, true);
543 if (getRootNode().getChildCount() == 0) {
544 myTreeModel
.nodeChanged(getRootNode());
548 private boolean isAutoExpand(NodeDescriptor descriptor
) {
549 boolean autoExpand
= false;
551 if (descriptor
!= null) {
552 autoExpand
= getBuilder().isAutoExpandNode(descriptor
);
555 if (!autoExpand
&& !myTree
.isRootVisible()) {
556 Object element
= getElementFromDescriptor(descriptor
);
557 if (element
!= null && element
.equals(getTreeStructure().getRootElement())) return true;
563 private boolean isAutoExpand(DefaultMutableTreeNode node
) {
564 return isAutoExpand(getDescriptorFrom(node
));
567 private boolean update(final NodeDescriptor nodeDescriptor
, boolean canBeNonEdt
) {
568 if (!canBeNonEdt
&& myWasEverShown
) {
569 assertIsDispatchThread();
572 if (isEdt() || !myWasEverShown
|| (!isEdt() && canBeNonEdt
)) {
573 return getBuilder().updateNodeDescriptor(nodeDescriptor
);
576 UIUtil
.invokeLaterIfNeeded(new Runnable() {
579 getBuilder().updateNodeDescriptor(nodeDescriptor
);
587 private void assertIsDispatchThread() {
588 if (isTreeShowing() && !isEdt()) {
589 LOG
.error("Must be in event-dispatch thread");
593 private boolean isEdt() {
594 return SwingUtilities
.isEventDispatchThread();
597 private boolean isTreeShowing() {
601 private void assertNotDispatchThread() {
603 LOG
.error("Must not be in event-dispatch thread");
607 private void processDeferredActions() {
608 processDeferredActions(myDeferredSelections
);
609 processDeferredActions(myDeferredExpansions
);
612 private void processDeferredActions(Set
<Runnable
> actions
) {
613 final Runnable
[] runnables
= actions
.toArray(new Runnable
[actions
.size()]);
615 for (Runnable runnable
: runnables
) {
620 public void doUpdateFromRoot() {
621 updateSubtree(getRootNode(), false);
624 public ActionCallback
doUpdateFromRootCB() {
625 final ActionCallback cb
= new ActionCallback();
626 getUpdater().runAfterUpdate(new Runnable() {
631 updateSubtree(getRootNode(), false);
635 public final void updateSubtree(DefaultMutableTreeNode node
, boolean canSmartExpand
) {
636 updateSubtree(new TreeUpdatePass(node
), canSmartExpand
);
639 public final void updateSubtree(TreeUpdatePass pass
, boolean canSmartExpand
) {
640 if (getUpdater() != null) {
641 getUpdater().addSubtreeToUpdate(pass
);
644 updateSubtreeNow(pass
, canSmartExpand
);
648 final void updateSubtreeNow(TreeUpdatePass pass
, boolean canSmartExpand
) {
649 maybeSetBusyAndScheduleWaiterForReady(true);
651 initRootNodeNowIfNeeded(pass
);
653 final DefaultMutableTreeNode node
= pass
.getNode();
655 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
657 setUpdaterState(new UpdaterTreeState(this)).beforeSubtreeUpdate();
659 boolean forceUpdate
= true;
660 TreePath path
= getPathFor(node
);
661 boolean invisible
= !myTree
.isExpanded(path
) && (path
.getParentPath() == null || !myTree
.isExpanded(path
.getParentPath()));
663 if (invisible
&& myUnbuiltNodes
.contains(node
)) {
667 updateNodeChildren(node
, pass
, null, false, canSmartExpand
, forceUpdate
, false);
670 private boolean isToBuildInBackground(NodeDescriptor descriptor
) {
671 return getTreeStructure().isToBuildChildrenInBackground(getBuilder().getTreeStructureElement(descriptor
));
675 UpdaterTreeState
setUpdaterState(UpdaterTreeState state
) {
676 final UpdaterTreeState oldState
= myUpdaterState
;
677 if (oldState
== null) {
678 myUpdaterState
= state
;
682 oldState
.addAll(state
);
687 protected void doUpdateNode(DefaultMutableTreeNode node
) {
688 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
689 NodeDescriptor descriptor
= getDescriptorFrom(node
);
690 Object prevElement
= getElementFromDescriptor(descriptor
);
691 if (prevElement
== null) return;
692 boolean changes
= update(descriptor
, false);
693 if (!isValid(descriptor
)) {
694 if (isInStructure(prevElement
)) {
695 getUpdater().addSubtreeToUpdateByElement(getTreeStructure().getParentElement(prevElement
));
700 updateNodeImageAndPosition(node
, true);
704 public Object
getElementFromDescriptor(NodeDescriptor descriptor
) {
705 return getBuilder().getTreeStructureElement(descriptor
);
708 private void updateNodeChildren(final DefaultMutableTreeNode node
,
709 final TreeUpdatePass pass
,
710 @Nullable LoadedChildren loadedChildren
,
712 final boolean toSmartExpand
,
714 final boolean descriptorIsUpToDate
) {
715 getTreeStructure().commit();
716 final boolean wasExpanded
= myTree
.isExpanded(new TreePath(node
.getPath())) || isAutoExpand(node
);
717 final boolean wasLeaf
= node
.getChildCount() == 0;
720 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
721 if (descriptor
== null) {
722 removeLoading(node
, true);
726 boolean bgBuild
= isToBuildInBackground(descriptor
);
727 boolean notRequiredToUpdateChildren
= !forcedNow
&& !wasExpanded
&& !forceUpdate
;
728 LoadedChildren preloaded
= loadedChildren
;
729 boolean descriptorWasUpdated
= descriptorIsUpToDate
;
731 if (notRequiredToUpdateChildren
) {
732 if (myUnbuiltNodes
.contains(node
) && node
.getChildCount() == 0) {
733 insertLoadingNode(node
, true);
740 if (myUnbuiltNodes
.contains(node
)) {
741 if (!descriptorWasUpdated
) {
742 update(descriptor
, false);
743 descriptorWasUpdated
= true;
745 Pair
<Boolean
, LoadedChildren
> unbuilt
= processUnbuilt(node
, descriptor
, pass
, wasExpanded
, null);
746 if (unbuilt
.getFirst()) return;
747 preloaded
= unbuilt
.getSecond();
753 boolean childForceUpdate
= isChildNodeForceUpdate(node
, forceUpdate
, wasExpanded
);
755 if (!forcedNow
&& isToBuildInBackground(descriptor
)) {
756 queueBackgroundUpdate(node
, descriptor
, pass
, canSmartExpand(node
, toSmartExpand
), wasExpanded
, childForceUpdate
, descriptorWasUpdated
);
759 if (!descriptorWasUpdated
) {
760 update(descriptor
, false);
763 updateNodeChildrenNow(node
, pass
, preloaded
, toSmartExpand
, wasExpanded
, wasLeaf
, childForceUpdate
);
767 processNodeActionsIfReady(node
);
771 private boolean isChildNodeForceUpdate(DefaultMutableTreeNode node
, boolean parentForceUpdate
, boolean parentExpanded
) {
772 TreePath path
= getPathFor(node
);
773 return parentForceUpdate
&& (parentExpanded
|| myTree
.isExpanded(path
));
776 private void updateNodeChildrenNow(final DefaultMutableTreeNode node
, final TreeUpdatePass pass
,
777 final LoadedChildren preloadedChildren
,
778 final boolean toSmartExpand
,
779 final boolean wasExpanded
,
780 final boolean wasLeaf
,
781 final boolean forceUpdate
) {
782 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
784 final MutualMap
<Object
, Integer
> elementToIndexMap
= loadElementsFromStructure(descriptor
, preloadedChildren
);
785 final LoadedChildren loadedChildren
= preloadedChildren
!= null ? preloadedChildren
: new LoadedChildren(elementToIndexMap
.getKeys().toArray());
789 pass
.setCurrentNode(node
);
791 final boolean canSmartExpand
= canSmartExpand(node
, toSmartExpand
);
793 processExistingNodes(node
, elementToIndexMap
, pass
, canSmartExpand(node
, toSmartExpand
), forceUpdate
, wasExpanded
, preloadedChildren
).doWhenDone(new Runnable() {
795 if (isDisposed(node
)) {
799 removeLoading(node
, false);
801 final boolean expanded
= isExpanded(node
, wasExpanded
);
803 ArrayList
<TreeNode
> nodesToInsert
= collectNodesToInsert(descriptor
, elementToIndexMap
, node
, expanded
, loadedChildren
);
804 insertNodesInto(nodesToInsert
, node
);
805 updateNodesToInsert(nodesToInsert
, pass
, canSmartExpand
, isChildNodeForceUpdate(node
, forceUpdate
, expanded
));
806 removeLoading(node
, true);
808 if (node
.getChildCount() > 0) {
810 expand(node
, canSmartExpand
);
814 removeFromUpdating(node
);
816 final Object element
= getElementFor(node
);
817 addNodeAction(element
, new NodeAction() {
818 public void onReady(final DefaultMutableTreeNode node
) {
819 removeLoading(node
, false);
823 processNodeActionsIfReady(node
);
828 private boolean isDisposed(DefaultMutableTreeNode node
) {
829 return !node
.isNodeAncestor((DefaultMutableTreeNode
)myTree
.getModel().getRoot());
832 private void expand(DefaultMutableTreeNode node
, boolean canSmartExpand
) {
833 expand(new TreePath(node
.getPath()), canSmartExpand
);
836 private void expand(final TreePath path
, boolean canSmartExpand
) {
837 if (path
== null) return;
840 final Object last
= path
.getLastPathComponent();
841 boolean isLeaf
= myTree
.getModel().isLeaf(path
.getLastPathComponent());
842 final boolean isRoot
= last
== myTree
.getModel().getRoot();
843 final TreePath parent
= path
.getParentPath();
844 if (isRoot
&& !myTree
.isExpanded(path
)) {
845 if (myTree
.isRootVisible() || myUnbuiltNodes
.contains(last
)) {
846 insertLoadingNode((DefaultMutableTreeNode
)last
, false);
848 expandPath(path
, canSmartExpand
);
850 else if (myTree
.isExpanded(path
) || (isLeaf
&& parent
!= null && myTree
.isExpanded(parent
) && !myUnbuiltNodes
.contains(last
))) {
851 if (last
instanceof DefaultMutableTreeNode
) {
852 processNodeActionsIfReady((DefaultMutableTreeNode
)last
);
856 if (isLeaf
&& myUnbuiltNodes
.contains(last
)) {
857 insertLoadingNode((DefaultMutableTreeNode
)last
, true);
858 expandPath(path
, canSmartExpand
);
860 else if (isLeaf
&& parent
!= null) {
861 final DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)parent
.getLastPathComponent();
862 if (parentNode
!= null) {
863 addToUnbuilt(parentNode
);
865 expandPath(parent
, canSmartExpand
);
868 expandPath(path
, canSmartExpand
);
873 private void addToUnbuilt(DefaultMutableTreeNode node
) {
874 myUnbuiltNodes
.add(node
);
877 private void removeFromUnbuilt(DefaultMutableTreeNode node
) {
878 myUnbuiltNodes
.remove(node
);
881 private Pair
<Boolean
, LoadedChildren
> processUnbuilt(final DefaultMutableTreeNode node
,
882 final NodeDescriptor descriptor
,
883 final TreeUpdatePass pass
,
885 final LoadedChildren loadedChildren
) {
886 if (!isExpanded
&& getBuilder().isAlwaysShowPlus(descriptor
)) {
887 return new Pair
<Boolean
, LoadedChildren
>(true, null);
890 final Object element
= getElementFor(node
);
892 final LoadedChildren children
= loadedChildren
!= null ? loadedChildren
: new LoadedChildren(getChildrenFor(element
));
896 if (children
.getElements().size() == 0) {
897 removeLoading(node
, true);
901 if (isAutoExpand(node
)) {
902 addNodeAction(getElementFor(node
), new NodeAction() {
903 public void onReady(final DefaultMutableTreeNode node
) {
904 final TreePath path
= new TreePath(node
.getPath());
905 if (getTree().isExpanded(path
) || children
.getElements().size() == 0) {
906 removeLoading(node
, false);
909 maybeYeild(new ActiveRunnable() {
910 public ActionCallback
run() {
911 expand(element
, null);
912 return new ActionCallback
.Done();
922 processNodeActionsIfReady(node
);
924 return new Pair
<Boolean
, LoadedChildren
>(processed
, children
);
927 private boolean removeIfLoading(TreeNode node
) {
928 if (isLoadingNode(node
)) {
929 moveSelectionToParentIfNeeded(node
);
930 removeNodeFromParent((MutableTreeNode
)node
, false);
937 private void moveSelectionToParentIfNeeded(TreeNode node
) {
938 TreePath path
= getPathFor(node
);
939 if (myTree
.getSelectionModel().isPathSelected(path
)) {
940 TreePath parentPath
= path
.getParentPath();
941 myTree
.getSelectionModel().removeSelectionPath(path
);
942 if (parentPath
!= null) {
943 myTree
.getSelectionModel().addSelectionPath(parentPath
);
948 //todo [kirillk] temporary consistency check
949 private Object
[] getChildrenFor(final Object element
) {
950 final Object
[] passOne
;
952 passOne
= getTreeStructure().getChildElements(element
);
954 catch (IndexNotReadyException e
) {
955 if (!myWasEverIndexNotReady
) {
956 myWasEverIndexNotReady
= true;
957 LOG
.warn("Tree is not dumb-mode-aware; treeBuilder=" + getBuilder() + " treeStructure=" + getTreeStructure());
959 return ArrayUtil
.EMPTY_OBJECT_ARRAY
;
962 if (!myCheckStructure
) return passOne
;
964 final Object
[] passTwo
= getTreeStructure().getChildElements(element
);
966 final HashSet two
= new HashSet(Arrays
.asList(passTwo
));
968 if (passOne
.length
!= passTwo
.length
) {
970 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
974 for (Object eachInOne
: passOne
) {
975 if (!two
.contains(eachInOne
)) {
977 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
987 private void updateNodesToInsert(final ArrayList
<TreeNode
> nodesToInsert
, TreeUpdatePass pass
, boolean canSmartExpand
, boolean forceUpdate
) {
988 for (TreeNode aNodesToInsert
: nodesToInsert
) {
989 DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)aNodesToInsert
;
990 updateNodeChildren(childNode
, pass
, null, false, canSmartExpand
, forceUpdate
, true);
994 private ActionCallback
processExistingNodes(final DefaultMutableTreeNode node
,
995 final MutualMap
<Object
, Integer
> elementToIndexMap
,
996 final TreeUpdatePass pass
,
997 final boolean canSmartExpand
,
998 final boolean forceUpdate
,
999 final boolean wasExpaned
,
1000 final LoadedChildren preloaded
) {
1002 final ArrayList
<TreeNode
> childNodes
= TreeUtil
.childrenToArray(node
);
1003 return maybeYeild(new ActiveRunnable() {
1004 public ActionCallback
run() {
1005 if (pass
.isExpired()) return new ActionCallback
.Rejected();
1006 if (childNodes
.size() == 0) return new ActionCallback
.Done();
1009 final ActionCallback result
= new ActionCallback(childNodes
.size());
1011 for (TreeNode each
: childNodes
) {
1012 final DefaultMutableTreeNode eachChild
= (DefaultMutableTreeNode
)each
;
1013 if (isLoadingNode(eachChild
)) {
1018 final boolean childForceUpdate
= isChildNodeForceUpdate(eachChild
, forceUpdate
, wasExpaned
);
1020 maybeYeild(new ActiveRunnable() {
1022 public ActionCallback
run() {
1023 return processExistingNode(eachChild
, getDescriptorFrom(eachChild
), node
, elementToIndexMap
, pass
, canSmartExpand
, childForceUpdate
, preloaded
);
1025 }, pass
, node
).notify(result
);
1027 if (result
.isRejected()) {
1037 private boolean isRerunNeeded(TreeUpdatePass pass
) {
1038 if (pass
.isExpired()) return false;
1040 final boolean rerunBecauseTreeIsHidden
= !pass
.isExpired() && !isTreeShowing() && getUpdater().isInPostponeMode();
1042 return rerunBecauseTreeIsHidden
|| getUpdater().isRerunNeededFor(pass
);
1045 private ActionCallback
maybeYeild(final ActiveRunnable processRunnable
, final TreeUpdatePass pass
, final DefaultMutableTreeNode node
) {
1046 final ActionCallback result
= new ActionCallback();
1048 if (isRerunNeeded(pass
)) {
1049 getUpdater().addSubtreeToUpdate(pass
);
1050 result
.setRejected();
1053 if (isToYieldUpdateFor(node
)) {
1054 pass
.setCurrentNode(node
);
1055 yieldAndRun(new Runnable() {
1057 if (pass
.isExpired()) return;
1059 if (isRerunNeeded(pass
)) {
1060 runDone(new Runnable() {
1062 if (!pass
.isExpired()) {
1063 getUpdater().addSubtreeToUpdate(pass
);
1067 result
.setRejected();
1070 processRunnable
.run().notify(result
);
1076 processRunnable
.run().notify(result
);
1083 private void yieldAndRun(final Runnable runnable
, final TreeUpdatePass pass
) {
1084 myYeildingPasses
.add(pass
);
1085 myYeildingNow
= true;
1086 yield(new Runnable() {
1092 runOnYieldingDone(new Runnable() {
1097 executeYieldingRequest(runnable
, pass
);
1104 public boolean isYeildingNow() {
1105 return myYeildingNow
;
1108 private boolean hasSheduledUpdates() {
1109 return getUpdater().hasNodesToUpdate() || isLoadingInBackgroundNow();
1112 public boolean isReady() {
1113 return isIdle() && !hasPendingWork();
1116 public boolean hasPendingWork() {
1117 return hasNodesToUpdate() || (myUpdaterState
!= null && myUpdaterState
.isProcessingNow());
1120 public boolean isIdle() {
1121 return !isYeildingNow() && !isWorkerBusy() && (!hasSheduledUpdates() || getUpdater().isInPostponeMode());
1124 private void executeYieldingRequest(Runnable runnable
, TreeUpdatePass pass
) {
1126 myYeildingPasses
.remove(pass
);
1130 maybeYeildingFinished();
1134 private void maybeYeildingFinished() {
1135 if (myYeildingPasses
.size() == 0) {
1136 myYeildingNow
= false;
1137 flushPendingNodeActions();
1142 if (isReleased()) return;
1145 if (myTree
.isShowing() || myUpdateIfInactive
) {
1146 myInitialized
.setDone();
1149 if (myTree
.isShowing()) {
1150 if (getBuilder().isToEnsureSelectionOnFocusGained() && Registry
.is("ide.tree.ensureSelectionOnFocusGained")) {
1151 TreeUtil
.ensureSelection(myTree
);
1155 if (myInitialized
.isDone()) {
1156 for (ActionCallback each
: getReadyCallbacks(true)) {
1163 private void flushPendingNodeActions() {
1164 final DefaultMutableTreeNode
[] nodes
= myPendingNodeActions
.toArray(new DefaultMutableTreeNode
[myPendingNodeActions
.size()]);
1165 myPendingNodeActions
.clear();
1167 for (DefaultMutableTreeNode each
: nodes
) {
1168 processNodeActionsIfReady(each
);
1171 final Runnable
[] actions
= myYeildingDoneRunnables
.toArray(new Runnable
[myYeildingDoneRunnables
.size()]);
1172 for (Runnable each
: actions
) {
1173 if (!isYeildingNow()) {
1174 myYeildingDoneRunnables
.remove(each
);
1182 protected void runOnYieldingDone(Runnable onDone
) {
1183 getBuilder().runOnYeildingDone(onDone
);
1186 protected void yield(Runnable runnable
) {
1187 getBuilder().yield(runnable
);
1190 private boolean isToYieldUpdateFor(final DefaultMutableTreeNode node
) {
1191 if (!canYield()) return false;
1192 return getBuilder().isToYieldUpdateFor(node
);
1195 private MutualMap
<Object
, Integer
> loadElementsFromStructure(final NodeDescriptor descriptor
, @Nullable LoadedChildren preloadedChildren
) {
1196 MutualMap
<Object
, Integer
> elementToIndexMap
= new MutualMap
<Object
, Integer
>(true);
1197 List children
= preloadedChildren
!= null ? preloadedChildren
.getElements() : Arrays
.asList(getChildrenFor(getBuilder().getTreeStructureElement(descriptor
)));
1199 for (Object child
: children
) {
1200 if (!isValid(child
)) continue;
1201 elementToIndexMap
.put(child
, Integer
.valueOf(index
));
1204 return elementToIndexMap
;
1207 private void expand(final DefaultMutableTreeNode node
,
1208 final NodeDescriptor descriptor
,
1209 final boolean wasLeaf
,
1210 final boolean canSmartExpand
) {
1211 final Alarm alarm
= new Alarm(Alarm
.ThreadToUse
.SHARED_THREAD
);
1212 alarm
.addRequest(new Runnable() {
1214 myTree
.setCursor(Cursor
.getPredefinedCursor(Cursor
.WAIT_CURSOR
));
1216 }, WAIT_CURSOR_DELAY
);
1218 if (wasLeaf
&& isAutoExpand(descriptor
)) {
1219 expand(node
, canSmartExpand
);
1222 ArrayList
<TreeNode
> nodes
= TreeUtil
.childrenToArray(node
);
1223 for (TreeNode node1
: nodes
) {
1224 final DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)node1
;
1225 if (isLoadingNode(childNode
)) continue;
1226 NodeDescriptor childDescr
= getDescriptorFrom(childNode
);
1227 if (isAutoExpand(childDescr
)) {
1228 addNodeAction(getElementFor(childNode
), new NodeAction() {
1229 public void onReady(DefaultMutableTreeNode node
) {
1230 expand(childNode
, canSmartExpand
);
1233 addSubtreeToUpdate(childNode
);
1237 int n
= alarm
.cancelAllRequests();
1239 myTree
.setCursor(Cursor
.getDefaultCursor());
1243 public static boolean isLoadingNode(final Object node
) {
1244 return node
instanceof LoadingNode
;
1247 private ArrayList
<TreeNode
> collectNodesToInsert(final NodeDescriptor descriptor
, final MutualMap
<Object
, Integer
> elementToIndexMap
, DefaultMutableTreeNode parent
, boolean addLoadingNode
, @NotNull LoadedChildren loadedChildren
) {
1248 ArrayList
<TreeNode
> nodesToInsert
= new ArrayList
<TreeNode
>();
1249 final Collection
<Object
> allElements
= elementToIndexMap
.getKeys();
1251 for (Object child
: allElements
) {
1252 Integer index
= elementToIndexMap
.getValue(child
);
1253 NodeDescriptor childDescr
= loadedChildren
.getDescriptor(child
);
1254 boolean needToUpdate
= false;
1255 if (childDescr
== null) {
1256 childDescr
= getTreeStructure().createDescriptor(child
, descriptor
);
1257 needToUpdate
= true;
1260 //noinspection ConstantConditions
1261 if (childDescr
== null) {
1262 LOG
.error("childDescr == null, treeStructure = " + getTreeStructure() + ", child = " + child
);
1265 childDescr
.setIndex(index
.intValue());
1268 loadedChildren
.putDescriptor(child
, childDescr
, update(childDescr
, false));
1271 Object element
= getElementFromDescriptor(childDescr
);
1272 if (element
== null) {
1273 LOG
.error("childDescr.getElement() == null, child = " + child
+ ", builder = " + this);
1277 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
1278 if (node
== null || node
.getParent() != parent
) {
1279 final DefaultMutableTreeNode childNode
= createChildNode(childDescr
);
1280 if (addLoadingNode
|| getBuilder().isAlwaysShowPlus(childDescr
)) {
1281 insertLoadingNode(childNode
, true);
1283 addToUnbuilt(childNode
);
1285 nodesToInsert
.add(childNode
);
1286 createMapping(element
, childNode
);
1290 return nodesToInsert
;
1293 protected DefaultMutableTreeNode
createChildNode(final NodeDescriptor descriptor
) {
1294 return new ElementNode(this, descriptor
);
1297 protected boolean canYield() {
1298 return myCanYield
&& myYeildingUpdate
.asBoolean();
1301 public long getClearOnHideDelay() {
1302 return myClearOnHideDelay
> 0 ? myClearOnHideDelay
: Registry
.intValue("ide.tree.clearOnHideTime");
1305 public ActionCallback
getInitialized() {
1306 return myInitialized
;
1309 public ActionCallback
getReady(Object requestor
) {
1311 return new ActionCallback
.Done();
1313 return addReadyCallback(requestor
);
1317 private void addToUpdating(DefaultMutableTreeNode node
) {
1318 synchronized (myUpdatingChildren
) {
1319 myUpdatingChildren
.add(node
);
1323 private void removeFromUpdating(DefaultMutableTreeNode node
) {
1324 synchronized (myUpdatingChildren
) {
1325 myUpdatingChildren
.remove(node
);
1329 public boolean isUpdatingNow(DefaultMutableTreeNode node
) {
1330 synchronized (myUpdatingChildren
) {
1331 return myUpdatingChildren
.contains(node
);
1335 boolean hasUpdatingNow() {
1336 synchronized (myUpdatingChildren
) {
1337 return myUpdatingChildren
.size() > 0;
1341 public Map
getNodeActions() {
1342 return myNodeActions
;
1345 public List
<Object
> getLoadedChildrenFor(Object element
) {
1346 List
<Object
> result
= new ArrayList
<Object
>();
1348 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)findNodeByElement(element
);
1350 for (int i
= 0; i
< node
.getChildCount(); i
++) {
1351 TreeNode each
= node
.getChildAt(i
);
1352 if (isLoadingNode(each
)) continue;
1354 result
.add(getElementFor(each
));
1361 public boolean hasNodesToUpdate() {
1362 return getUpdater().hasNodesToUpdate() || hasUpdatingNow() || isLoadingInBackgroundNow();
1365 public List
<Object
> getExpandedElements() {
1366 List
<Object
> result
= new ArrayList
<Object
>();
1367 Enumeration
<TreePath
> enumeration
= myTree
.getExpandedDescendants(getPathFor(getRootNode()));
1368 while (enumeration
.hasMoreElements()) {
1369 TreePath each
= enumeration
.nextElement();
1370 Object eachElement
= getElementFor(each
.getLastPathComponent());
1371 if (eachElement
!= null) {
1372 result
.add(eachElement
);
1379 static class ElementNode
extends DefaultMutableTreeNode
{
1381 Set
<Object
> myElements
= new HashSet
<Object
>();
1382 AbstractTreeUi myUi
;
1384 ElementNode(AbstractTreeUi ui
, NodeDescriptor descriptor
) {
1390 public void insert(final MutableTreeNode newChild
, final int childIndex
) {
1391 super.insert(newChild
, childIndex
);
1392 final Object element
= myUi
.getElementFor(newChild
);
1393 if (element
!= null) {
1394 myElements
.add(element
);
1399 public void remove(final int childIndex
) {
1400 final TreeNode node
= getChildAt(childIndex
);
1401 super.remove(childIndex
);
1402 final Object element
= myUi
.getElementFor(node
);
1403 if (element
!= null) {
1404 myElements
.remove(element
);
1408 boolean isValidChild(Object childElement
) {
1409 return myElements
.contains(childElement
);
1413 public String
toString() {
1414 return String
.valueOf(getUserObject());
1418 private boolean isUpdatingParent(DefaultMutableTreeNode kid
) {
1419 DefaultMutableTreeNode eachParent
= kid
;
1420 while (eachParent
!= null) {
1421 if (isUpdatingNow(eachParent
)) return true;
1422 eachParent
= (DefaultMutableTreeNode
)eachParent
.getParent();
1428 private boolean isLoadedInBackground(Object element
) {
1429 synchronized (myLoadingParents
) {
1430 return myLoadingParents
.contains(element
);
1434 private void addToLoadedInBackground(Object element
) {
1435 synchronized (myLoadingParents
) {
1436 myLoadingParents
.add(element
);
1440 private void removeFromLoadedInBackground(final Object element
) {
1441 synchronized (myLoadingParents
) {
1442 myLoadingParents
.remove(element
);
1446 private boolean isLoadingInBackgroundNow() {
1447 synchronized (myLoadingParents
) {
1448 return myLoadingParents
.size() > 0;
1452 private boolean queueBackgroundUpdate(final DefaultMutableTreeNode node
,
1453 final NodeDescriptor descriptor
,
1454 final TreeUpdatePass pass
,
1455 final boolean canSmartExpand
,
1456 final boolean wasExpanded
,
1457 final boolean forceUpdate
,
1458 final boolean descriptorIsUpToDate
) {
1459 assertIsDispatchThread();
1461 final Object oldElementFromDescriptor
= getElementFromDescriptor(descriptor
);
1463 if (isLoadedInBackground(oldElementFromDescriptor
)) return false;
1465 addToLoadedInBackground(oldElementFromDescriptor
);
1467 if (!isNodeBeingBuilt(node
)) {
1468 LoadingNode loadingNode
= new LoadingNode(getLoadingNodeText());
1469 myTreeModel
.insertNodeInto(loadingNode
, node
, node
.getChildCount());
1472 final Ref
<LoadedChildren
> children
= new Ref
<LoadedChildren
>();
1473 final Ref
<Object
> elementFromDescriptor
= new Ref
<Object
>();
1474 Runnable buildRunnable
= new Runnable() {
1480 if (!descriptorIsUpToDate
) {
1481 update(descriptor
, true);
1484 Object element
= getElementFromDescriptor(descriptor
);
1485 if (element
== null) {
1486 removeFromLoadedInBackground(oldElementFromDescriptor
);
1490 elementFromDescriptor
.set(element
);
1492 Object
[] loadedElements
= getChildrenFor(getBuilder().getTreeStructureElement(descriptor
));
1493 LoadedChildren loaded
= new LoadedChildren(loadedElements
);
1494 for (Object each
: loadedElements
) {
1495 NodeDescriptor eachChildDescriptor
= getTreeStructure().createDescriptor(each
, descriptor
);
1496 loaded
.putDescriptor(each
, eachChildDescriptor
, eachChildDescriptor
.update());
1499 children
.set(loaded
);
1503 final DefaultMutableTreeNode
[] nodeToProcessActions
= new DefaultMutableTreeNode
[1];
1504 Runnable updateRunnable
= new Runnable() {
1506 if (isReleased()) return;
1507 if (children
.get() == null) return;
1509 if (isRerunNeeded(pass
)) {
1510 removeFromLoadedInBackground(elementFromDescriptor
.get());
1511 getUpdater().addSubtreeToUpdate(pass
);
1515 removeFromLoadedInBackground(elementFromDescriptor
.get());
1517 if (myUnbuiltNodes
.contains(node
)) {
1518 Pair
<Boolean
, LoadedChildren
> unbuilt
= processUnbuilt(node
, descriptor
, pass
, isExpanded(node
, wasExpanded
), children
.get());
1519 if (unbuilt
.getFirst()) {
1520 nodeToProcessActions
[0] = node
;
1525 updateNodeChildren(node
, pass
, children
.get(), true, canSmartExpand
, forceUpdate
, true);
1528 if (isRerunNeeded(pass
)) {
1529 getUpdater().addSubtreeToUpdate(pass
);
1533 Object element
= elementFromDescriptor
.get();
1535 if (element
!= null) {
1536 removeLoading(node
, true);
1537 nodeToProcessActions
[0] = node
;
1541 addTaskToWorker(buildRunnable
, true, updateRunnable
, new Runnable() {
1543 if (nodeToProcessActions
[0] != null) {
1544 processNodeActionsIfReady(nodeToProcessActions
[0]);
1551 private boolean isExpanded(DefaultMutableTreeNode node
, boolean isExpanded
) {
1552 return isExpanded
|| myTree
.isExpanded(getPathFor(node
));
1555 private void removeLoading(DefaultMutableTreeNode parent
, boolean removeFromUnbuilt
) {
1556 for (int i
= 0; i
< parent
.getChildCount(); i
++) {
1557 TreeNode child
= parent
.getChildAt(i
);
1558 if (removeIfLoading(child
)) {
1563 if (removeFromUnbuilt
) {
1564 removeFromUnbuilt(parent
);
1567 if (parent
== getRootNode() && !myTree
.isRootVisible() && parent
.getChildCount() == 0) {
1568 insertLoadingNode(parent
, false);
1574 private void processNodeActionsIfReady(final DefaultMutableTreeNode node
) {
1575 if (isNodeBeingBuilt(node
)) return;
1577 final Object o
= node
.getUserObject();
1578 if (!(o
instanceof NodeDescriptor
)) return;
1581 if (isYeildingNow()) {
1582 myPendingNodeActions
.add(node
);
1586 final Object element
= getBuilder().getTreeStructureElement((NodeDescriptor
)o
);
1588 processActions(node
, element
, myNodeActions
);
1590 boolean childrenReady
= !isLoadedInBackground(element
);
1591 if (childrenReady
) {
1592 processActions(node
, element
, myNodeChildrenActions
);
1595 if (!isUpdatingParent(node
) && !isWorkerBusy()) {
1596 final UpdaterTreeState state
= myUpdaterState
;
1597 if (myNodeActions
.size() == 0 && state
!= null && !state
.isProcessingNow()) {
1598 if (!state
.restore(childrenReady ? node
: null)) {
1599 setUpdaterState(state
);
1608 private void processActions(DefaultMutableTreeNode node
, Object element
, final Map
<Object
, List
<NodeAction
>> nodeActions
) {
1609 final List
<NodeAction
> actions
= nodeActions
.get(element
);
1610 if (actions
!= null) {
1611 nodeActions
.remove(element
);
1612 for (NodeAction each
: actions
) {
1619 private boolean canSmartExpand(DefaultMutableTreeNode node
, boolean canSmartExpand
) {
1620 return !myNotForSmartExpand
.contains(node
) && canSmartExpand
;
1623 private void processSmartExpand(final DefaultMutableTreeNode node
, final boolean canSmartExpand
) {
1624 if (!getBuilder().isSmartExpand() || !canSmartExpand(node
, canSmartExpand
)) return;
1626 if (isNodeBeingBuilt(node
)) {
1627 addNodeAction(getElementFor(node
), new NodeAction() {
1628 public void onReady(DefaultMutableTreeNode node
) {
1629 processSmartExpand(node
, canSmartExpand
);
1634 TreeNode child
= getChildForSmartExpand(node
);
1635 if (child
!= null) {
1636 final TreePath childPath
= new TreePath(node
.getPath()).pathByAddingChild(child
);
1637 myTree
.expandPath(childPath
);
1643 private TreeNode
getChildForSmartExpand(DefaultMutableTreeNode node
) {
1644 int realChildCount
= 0;
1645 TreeNode nodeToExpand
= null;
1647 for (int i
= 0; i
< node
.getChildCount(); i
++) {
1648 TreeNode eachChild
= node
.getChildAt(i
);
1650 if (!isLoadingNode(eachChild
)) {
1652 if (nodeToExpand
== null) {
1653 nodeToExpand
= eachChild
;
1657 if (realChildCount
> 1) {
1658 nodeToExpand
= null;
1663 return nodeToExpand
;
1666 public boolean isLoadingChildrenFor(final Object nodeObject
) {
1667 if (!(nodeObject
instanceof DefaultMutableTreeNode
)) return false;
1669 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)nodeObject
;
1671 int loadingNodes
= 0;
1672 for (int i
= 0; i
< Math
.min(node
.getChildCount(), 2); i
++) {
1673 TreeNode child
= node
.getChildAt(i
);
1674 if (isLoadingNode(child
)) {
1678 return loadingNodes
> 0 && loadingNodes
== node
.getChildCount();
1681 private boolean isParentLoading(Object nodeObject
) {
1682 if (!(nodeObject
instanceof DefaultMutableTreeNode
)) return false;
1684 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)nodeObject
;
1686 TreeNode eachParent
= node
.getParent();
1688 while (eachParent
!= null) {
1689 eachParent
= eachParent
.getParent();
1690 if (eachParent
instanceof DefaultMutableTreeNode
) {
1691 final Object eachElement
= getElementFor((DefaultMutableTreeNode
)eachParent
);
1692 if (isLoadedInBackground(eachElement
)) return true;
1699 protected String
getLoadingNodeText() {
1700 return IdeBundle
.message("progress.searching");
1703 private ActionCallback
processExistingNode(final DefaultMutableTreeNode childNode
,
1704 final NodeDescriptor childDescriptor
,
1705 final DefaultMutableTreeNode parentNode
,
1706 final MutualMap
<Object
, Integer
> elementToIndexMap
,
1707 TreeUpdatePass pass
,
1708 final boolean canSmartExpand
,
1709 boolean forceUpdate
,
1710 LoadedChildren parentPreloadedChildren
) {
1712 if (pass
.isExpired()) {
1713 return new ActionCallback
.Rejected();
1716 NodeDescriptor childDesc
= childDescriptor
;
1719 if (childDesc
== null) {
1721 return new ActionCallback
.Rejected();
1723 Object oldElement
= getElementFromDescriptor(childDesc
);
1724 if (oldElement
== null) {
1726 return new ActionCallback
.Rejected();
1730 if (parentPreloadedChildren
!= null && parentPreloadedChildren
.getDescriptor(oldElement
) != null) {
1731 changes
= parentPreloadedChildren
.isUpdated(oldElement
);
1733 changes
= update(childDesc
, false);
1736 boolean forceRemapping
= false;
1737 Object newElement
= getElementFromDescriptor(childDesc
);
1739 Integer index
= newElement
!= null ? elementToIndexMap
.getValue(getBuilder().getTreeStructureElement(childDesc
)) : null;
1740 if (index
!= null) {
1741 final Object elementFromMap
= elementToIndexMap
.getKey(index
);
1742 if (elementFromMap
!= newElement
&& elementFromMap
.equals(newElement
)) {
1743 if (isInStructure(elementFromMap
) && isInStructure(newElement
)) {
1744 if (parentNode
.getUserObject() instanceof NodeDescriptor
) {
1745 final NodeDescriptor parentDescriptor
= getDescriptorFrom(parentNode
);
1746 childDesc
= getTreeStructure().createDescriptor(elementFromMap
, parentDescriptor
);
1747 childNode
.setUserObject(childDesc
);
1748 newElement
= elementFromMap
;
1749 forceRemapping
= true;
1750 update(childDesc
, false);
1756 if (childDesc
.getIndex() != index
.intValue()) {
1759 childDesc
.setIndex(index
.intValue());
1762 if (index
!= null && changes
) {
1763 updateNodeImageAndPosition(childNode
, false);
1765 if (!oldElement
.equals(newElement
) | forceRemapping
) {
1766 removeMapping(oldElement
, childNode
, newElement
);
1767 if (newElement
!= null) {
1768 createMapping(newElement
, childNode
);
1772 if (index
== null) {
1773 int selectedIndex
= -1;
1774 if (TreeBuilderUtil
.isNodeOrChildSelected(myTree
, childNode
)) {
1775 selectedIndex
= parentNode
.getIndex(childNode
);
1778 if (childNode
.getParent() instanceof DefaultMutableTreeNode
) {
1779 final DefaultMutableTreeNode parent
= (DefaultMutableTreeNode
)childNode
.getParent();
1780 if (myTree
.isExpanded(new TreePath(parent
.getPath()))) {
1781 if (parent
.getChildCount() == 1 && parent
.getChildAt(0) == childNode
) {
1782 insertLoadingNode(parent
, false);
1787 Object disposedElement
= getElementFor(childNode
);
1789 removeNodeFromParent(childNode
, selectedIndex
>= 0);
1790 disposeNode(childNode
);
1792 adjustSelectionOnChildRemove(parentNode
, selectedIndex
, disposedElement
);
1795 elementToIndexMap
.remove(getBuilder().getTreeStructureElement(childDesc
));
1796 updateNodeChildren(childNode
, pass
, null, false, canSmartExpand
, forceUpdate
, true);
1799 if (parentNode
.equals(getRootNode())) {
1800 myTreeModel
.nodeChanged(getRootNode());
1803 return new ActionCallback
.Done();
1806 private void adjustSelectionOnChildRemove(DefaultMutableTreeNode parentNode
, int selectedIndex
, Object disposedElement
) {
1807 DefaultMutableTreeNode node
= getNodeForElement(disposedElement
, false);
1808 if (node
!= null && isValidForSelectionAdjusting(node
)) {
1809 Object newElement
= getElementFor(node
);
1810 addSelectionPath(getPathFor(node
), true, getExpiredElementCondition(newElement
));
1815 if (selectedIndex
>= 0) {
1816 if (parentNode
.getChildCount() > 0) {
1817 if (parentNode
.getChildCount() > selectedIndex
) {
1818 TreeNode newChildNode
= parentNode
.getChildAt(selectedIndex
);
1819 if (isValidForSelectionAdjusting(newChildNode
)) {
1820 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(newChildNode
)), true, getExpiredElementCondition(disposedElement
));
1824 TreeNode newChild
= parentNode
.getChildAt(parentNode
.getChildCount() - 1);
1825 if (isValidForSelectionAdjusting(newChild
)) {
1826 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(newChild
)), true, getExpiredElementCondition(disposedElement
));
1831 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(parentNode
)), true, getExpiredElementCondition(disposedElement
));
1836 private boolean isValidForSelectionAdjusting(TreeNode node
) {
1837 if (!myTree
.isRootVisible() && getRootNode() == node
) return false;
1839 if (isLoadingNode(node
)) return true;
1841 final Object elementInTree
= getElementFor(node
);
1842 if (elementInTree
== null) return false;
1844 final TreeNode parentNode
= node
.getParent();
1845 final Object parentElementInTree
= getElementFor(parentNode
);
1846 if (parentElementInTree
== null) return false;
1848 final Object parentElement
= getTreeStructure().getParentElement(elementInTree
);
1850 return parentElementInTree
.equals(parentElement
);
1853 public Condition
getExpiredElementCondition(final Object element
) {
1854 return new Condition() {
1855 public boolean value(final Object o
) {
1856 return isInStructure(element
);
1861 private void addSelectionPath(final TreePath path
, final boolean isAdjustedSelection
, final Condition isExpiredAdjustement
) {
1862 doWithUpdaterState(new Runnable() {
1864 TreePath toSelect
= null;
1866 if (isLoadingNode(path
.getLastPathComponent())) {
1867 final TreePath parentPath
= path
.getParentPath();
1868 if (parentPath
!= null) {
1869 toSelect
= parentPath
;
1876 if (toSelect
!= null) {
1877 myTree
.addSelectionPath(toSelect
);
1879 if (isAdjustedSelection
&& myUpdaterState
!= null) {
1880 final Object toSelectElement
= getElementFor(toSelect
.getLastPathComponent());
1881 myUpdaterState
.addAdjustedSelection(toSelectElement
, isExpiredAdjustement
);
1888 private static TreePath
getPathFor(TreeNode node
) {
1889 if (node
instanceof DefaultMutableTreeNode
) {
1890 return new TreePath(((DefaultMutableTreeNode
)node
).getPath());
1893 ArrayList nodes
= new ArrayList();
1894 TreeNode eachParent
= node
;
1895 while (eachParent
!= null) {
1896 nodes
.add(eachParent
);
1897 eachParent
= eachParent
.getParent();
1900 return new TreePath(ArrayUtil
.toObjectArray(nodes
));
1905 private void removeNodeFromParent(final MutableTreeNode node
, final boolean willAdjustSelection
) {
1906 doWithUpdaterState(new Runnable() {
1908 if (willAdjustSelection
) {
1909 final TreePath path
= getPathFor(node
);
1910 if (myTree
.isPathSelected(path
)) {
1911 myTree
.removeSelectionPath(path
);
1915 myTreeModel
.removeNodeFromParent(node
);
1920 private void expandPath(final TreePath path
, final boolean canSmartExpand
) {
1921 doWithUpdaterState(new Runnable() {
1923 if (path
.getLastPathComponent() instanceof DefaultMutableTreeNode
) {
1924 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
1925 if (node
.getChildCount() > 0 && !myTree
.isExpanded(path
)) {
1926 if (!canSmartExpand
) {
1927 myNotForSmartExpand
.add(node
);
1930 myRequestedExpand
= path
;
1931 myTree
.expandPath(path
);
1932 processSmartExpand(node
, canSmartExpand
);
1935 myNotForSmartExpand
.remove(node
);
1936 myRequestedExpand
= null;
1939 processNodeActionsIfReady(node
);
1946 private void doWithUpdaterState(Runnable runnable
) {
1947 if (myUpdaterState
!= null) {
1948 myUpdaterState
.process(runnable
);
1955 protected boolean doUpdateNodeDescriptor(final NodeDescriptor descriptor
) {
1956 return descriptor
.update();
1959 private void makeLoadingOrLeafIfNoChildren(final DefaultMutableTreeNode node
) {
1960 //Object[] children = null;
1961 //final boolean[] hasNoChildren = new boolean[1];
1962 //final NodeDescriptor descriptor = getDescriptorFrom(node);
1963 //if (!getBuilder().isAlwaysShowPlus(descriptor)) {
1964 // Runnable updateRunnable = new Runnable() {
1965 // public void run() {
1966 // if (isReleased()) return;
1968 // if (hasNoChildren[0]) {
1969 // //update(descriptor, false);
1970 // removeLoading(node, false);
1975 // if (isToBuildInBackground(descriptor)) {
1976 // //Runnable buildRunnable = new Runnable() {
1977 // // public void run() {
1978 // // if (isReleased()) return;
1980 // // update(descriptor, true);
1981 // // Object element = getBuilder().getTreeStructureElement(descriptor);
1982 // // if (element == null && !isValid(element)) return;
1984 // // Object[] children = getChildrenFor(element);
1985 // // hasNoChildren[0] = children.length == 0;
1988 // //addTaskToWorker(buildRunnable, false, updateRunnable, new Runnable() {
1989 // // public void run() {
1990 // // processNodeActionsIfReady(node);
1995 // children = getChildrenFor(getBuilder().getTreeStructureElement(descriptor));
1996 // if (children.length == 0) return children;
2000 //insertLoadingNode(node, true);
2002 TreePath path
= getPathFor(node
);
2003 if (path
== null) return;
2005 insertLoadingNode(node
, true);
2007 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
2008 if (getBuilder().isAlwaysShowPlus(descriptor
)) return;
2011 TreePath parentPath
= path
.getParentPath();
2012 if (myTree
.isVisible(path
) || (parentPath
!= null && myTree
.isExpanded(parentPath
))) {
2013 if (myTree
.isExpanded(path
)) {
2014 getUpdater().addSubtreeToUpdate(node
);
2016 insertLoadingNode(node
, false);
2022 private boolean isValid(DefaultMutableTreeNode node
) {
2023 if (node
== null) return false;
2024 final Object object
= node
.getUserObject();
2025 if (object
instanceof NodeDescriptor
) {
2026 return isValid((NodeDescriptor
)object
);
2032 private boolean isValid(NodeDescriptor descriptor
) {
2033 if (descriptor
== null) return false;
2034 return isValid(getElementFromDescriptor(descriptor
));
2037 private boolean isValid(Object element
) {
2038 if (element
instanceof ValidateableNode
) {
2039 if (!((ValidateableNode
)element
).isValid()) return false;
2041 return getBuilder().validateNode(element
);
2044 private void insertLoadingNode(final DefaultMutableTreeNode node
, boolean addToUnbuilt
) {
2045 if (!isLoadingChildrenFor(node
)) {
2046 myTreeModel
.insertNodeInto(new LoadingNode(), node
, 0);
2055 protected void addTaskToWorker(@NotNull final Runnable bgReadActionRunnable
,
2057 @Nullable final Runnable edtPostRunnable
,
2058 @Nullable final Runnable finalizeEdtRunnable
) {
2059 registerWorkerTask(bgReadActionRunnable
);
2061 final Runnable pooledThreadWithProgressRunnable
= new Runnable() {
2067 final AbstractTreeBuilder builder
= getBuilder();
2069 builder
.runBackgroundLoading(new Runnable() {
2071 assertNotDispatchThread();
2078 bgReadActionRunnable
.run();
2080 if (edtPostRunnable
!= null && !isReleased()) {
2081 builder
.updateAfterLoadedInBackground(new Runnable() {
2084 assertIsDispatchThread();
2090 edtPostRunnable
.run();
2093 unregisterWorkerTask(bgReadActionRunnable
, finalizeEdtRunnable
);
2099 unregisterWorkerTask(bgReadActionRunnable
, finalizeEdtRunnable
);
2102 catch (ProcessCanceledException e
) {
2103 unregisterWorkerTask(bgReadActionRunnable
, finalizeEdtRunnable
);
2105 catch (Throwable t
) {
2106 unregisterWorkerTask(bgReadActionRunnable
, finalizeEdtRunnable
);
2107 throw new RuntimeException(t
);
2114 Runnable pooledThreadRunnable
= new Runnable() {
2116 if (isReleased()) return;
2119 if (myProgress
!= null) {
2120 ProgressManager
.getInstance().runProcess(pooledThreadWithProgressRunnable
, myProgress
);
2123 pooledThreadWithProgressRunnable
.run();
2126 catch (ProcessCanceledException e
) {
2132 if (myWorker
== null || myWorker
.isDisposed()) {
2133 myWorker
= new WorkerThread("AbstractTreeBuilder.Worker", 1);
2136 myWorker
.addTaskFirst(pooledThreadRunnable
);
2139 myWorker
.addTask(pooledThreadRunnable
);
2141 myWorker
.dispose(false);
2145 myWorker
.addTaskFirst(pooledThreadRunnable
);
2148 myWorker
.addTask(pooledThreadRunnable
);
2153 private void registerWorkerTask(Runnable runnable
) {
2154 synchronized (myActiveWorkerTasks
) {
2155 myActiveWorkerTasks
.add(runnable
);
2159 private void unregisterWorkerTask(Runnable runnable
, @Nullable Runnable finalizeRunnable
) {
2161 synchronized (myActiveWorkerTasks
) {
2162 wasRemoved
= myActiveWorkerTasks
.remove(runnable
);
2165 if (wasRemoved
&& finalizeRunnable
!= null) {
2166 finalizeRunnable
.run();
2172 public boolean isWorkerBusy() {
2173 synchronized (myActiveWorkerTasks
) {
2174 return myActiveWorkerTasks
.size() > 0;
2178 private void clearWorkerTasks() {
2179 synchronized (myActiveWorkerTasks
) {
2180 myActiveWorkerTasks
.clear();
2184 private void updateNodeImageAndPosition(final DefaultMutableTreeNode node
, boolean updatePosition
) {
2185 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
2186 NodeDescriptor descriptor
= getDescriptorFrom(node
);
2187 if (getElementFromDescriptor(descriptor
) == null) return;
2189 boolean notified
= false;
2190 if (updatePosition
) {
2191 DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)node
.getParent();
2192 if (parentNode
!= null) {
2193 int oldIndex
= parentNode
.getIndex(node
);
2194 int newIndex
= oldIndex
;
2195 if (isLoadingChildrenFor(node
.getParent()) || getBuilder().isChildrenResortingNeeded(descriptor
)) {
2196 final ArrayList
<TreeNode
> children
= new ArrayList
<TreeNode
>(parentNode
.getChildCount());
2197 for (int i
= 0; i
< parentNode
.getChildCount(); i
++) {
2198 children
.add(parentNode
.getChildAt(i
));
2200 sortChildren(node
, children
);
2201 newIndex
= children
.indexOf(node
);
2204 if (oldIndex
!= newIndex
) {
2205 List
<Object
> pathsToExpand
= new ArrayList
<Object
>();
2206 List
<Object
> selectionPaths
= new ArrayList
<Object
>();
2207 TreeBuilderUtil
.storePaths(getBuilder(), node
, pathsToExpand
, selectionPaths
, false);
2208 removeNodeFromParent(node
, false);
2209 myTreeModel
.insertNodeInto(node
, parentNode
, newIndex
);
2210 TreeBuilderUtil
.restorePaths(getBuilder(), pathsToExpand
, selectionPaths
, false);
2214 myTreeModel
.nodeChanged(node
);
2219 myTreeModel
.nodeChanged(node
);
2225 myTreeModel
.nodeChanged(node
);
2230 public DefaultTreeModel
getTreeModel() {
2234 private void insertNodesInto(final ArrayList
<TreeNode
> toInsert
, DefaultMutableTreeNode parentNode
) {
2235 if (toInsert
.isEmpty()) return;
2237 sortChildren(parentNode
, toInsert
);
2239 ArrayList
<TreeNode
> all
= new ArrayList
<TreeNode
>(toInsert
.size() + parentNode
.getChildCount());
2240 all
.addAll(toInsert
);
2241 all
.addAll(TreeUtil
.childrenToArray(parentNode
));
2243 sortChildren(parentNode
, all
);
2245 int[] newNodeIndices
= new int[toInsert
.size()];
2246 int eachNewNodeIndex
= 0;
2247 for (int i
= 0; i
< toInsert
.size(); i
++) {
2248 TreeNode eachNewNode
= toInsert
.get(i
);
2249 while (all
.get(eachNewNodeIndex
) != eachNewNode
) eachNewNodeIndex
++;
2250 newNodeIndices
[i
] = eachNewNodeIndex
;
2251 parentNode
.insert((MutableTreeNode
)eachNewNode
, eachNewNodeIndex
);
2254 myTreeModel
.nodesWereInserted(parentNode
, newNodeIndices
);
2257 private void sortChildren(DefaultMutableTreeNode node
, ArrayList
<TreeNode
> children
) {
2258 getBuilder().sortChildren(myNodeComparator
, node
, children
);
2261 private void disposeNode(DefaultMutableTreeNode node
) {
2262 removeFromUpdating(node
);
2263 removeFromUnbuilt(node
);
2265 if (node
.getChildCount() > 0) {
2266 for (DefaultMutableTreeNode _node
= (DefaultMutableTreeNode
)node
.getFirstChild(); _node
!= null; _node
= _node
.getNextSibling()) {
2270 if (isLoadingNode(node
)) return;
2271 NodeDescriptor descriptor
= getDescriptorFrom(node
);
2272 if (descriptor
== null) return;
2273 final Object element
= getElementFromDescriptor(descriptor
);
2274 removeMapping(element
, node
, null);
2275 node
.setUserObject(null);
2276 node
.removeAllChildren();
2279 public void addSubtreeToUpdate(final DefaultMutableTreeNode root
) {
2280 addSubtreeToUpdate(root
, null);
2283 public void addSubtreeToUpdate(final DefaultMutableTreeNode root
, Runnable runAfterUpdate
) {
2284 getUpdater().runAfterUpdate(runAfterUpdate
);
2285 getUpdater().addSubtreeToUpdate(root
);
2288 public boolean wasRootNodeInitialized() {
2289 return myRootNodeWasInitialized
;
2292 private boolean isRootNodeBuilt() {
2293 return myRootNodeWasInitialized
&& isNodeBeingBuilt(myRootNode
);
2296 public void select(final Object
[] elements
, @Nullable final Runnable onDone
) {
2297 select(elements
, onDone
, false);
2300 public void select(final Object
[] elements
, @Nullable final Runnable onDone
, boolean addToSelection
) {
2301 select(elements
, onDone
, addToSelection
, false);
2304 public void select(final Object
[] elements
, @Nullable final Runnable onDone
, boolean addToSelection
, boolean deferred
) {
2305 _select(elements
, onDone
, addToSelection
, true, false, true, deferred
, false);
2308 void _select(final Object
[] elements
,
2309 final Runnable onDone
,
2310 final boolean addToSelection
,
2311 final boolean checkCurrentSelection
,
2312 final boolean checkIfInStructure
) {
2314 _select(elements
, onDone
, addToSelection
, checkCurrentSelection
, checkIfInStructure
, true, false, false);
2317 void _select(final Object
[] elements
,
2318 final Runnable onDone
,
2319 final boolean addToSelection
,
2320 final boolean checkCurrentSelection
,
2321 final boolean checkIfInStructure
,
2322 final boolean scrollToVisible
) {
2324 _select(elements
, onDone
, addToSelection
, checkCurrentSelection
, checkIfInStructure
, scrollToVisible
, false, false);
2327 void _select(final Object
[] elements
,
2328 final Runnable onDone
,
2329 final boolean addToSelection
,
2330 final boolean checkCurrentSelection
,
2331 final boolean checkIfInStructure
,
2332 final boolean scrollToVisible
,
2333 final boolean deferred
,
2334 final boolean canSmartExpand
) {
2336 boolean willAffectSelection
= elements
.length
> 0 || (elements
.length
== 0 && addToSelection
);
2337 if (!willAffectSelection
) {
2342 final boolean oldCanProcessDeferredSelection
= myCanProcessDeferredSelections
;
2344 if (!deferred
&& wasRootNodeInitialized() && willAffectSelection
) {
2345 myCanProcessDeferredSelections
= false;
2348 if (!checkDeferred(deferred
, onDone
)) return;
2350 if (!deferred
&& oldCanProcessDeferredSelection
&& !myCanProcessDeferredSelections
) {
2351 getTree().clearSelection();
2355 runDone(new Runnable() {
2357 if (!checkDeferred(deferred
, onDone
)) return;
2359 final Set
<Object
> currentElements
= getSelectedElements();
2361 if (checkCurrentSelection
&& currentElements
.size() > 0 && elements
.length
== currentElements
.size()) {
2362 boolean runSelection
= false;
2363 for (Object eachToSelect
: elements
) {
2364 if (!currentElements
.contains(eachToSelect
)) {
2365 runSelection
= true;
2370 if (!runSelection
) {
2371 if (elements
.length
> 0) {
2372 selectVisible(elements
[0], onDone
, true, true, scrollToVisible
);
2378 Set
<Object
> toSelect
= new HashSet
<Object
>();
2379 myTree
.clearSelection();
2380 toSelect
.addAll(Arrays
.asList(elements
));
2381 if (addToSelection
) {
2382 toSelect
.addAll(currentElements
);
2385 if (checkIfInStructure
) {
2386 final Iterator
<Object
> allToSelect
= toSelect
.iterator();
2387 while (allToSelect
.hasNext()) {
2388 Object each
= allToSelect
.next();
2389 if (!isInStructure(each
)) {
2390 allToSelect
.remove();
2395 final Object
[] elementsToSelect
= ArrayUtil
.toObjectArray(toSelect
);
2397 if (wasRootNodeInitialized()) {
2398 final int[] originalRows
= myTree
.getSelectionRows();
2399 if (!addToSelection
) {
2400 myTree
.clearSelection();
2402 addNext(elementsToSelect
, 0, new Runnable() {
2404 if (getTree().isSelectionEmpty()) {
2405 restoreSelection(currentElements
);
2409 }, originalRows
, deferred
, scrollToVisible
, canSmartExpand
);
2412 addToDeferred(elementsToSelect
, onDone
);
2418 private void restoreSelection(Set
<Object
> selection
) {
2419 for (Object each
: selection
) {
2420 DefaultMutableTreeNode node
= getNodeForElement(each
, false);
2421 if (node
!= null && isValidForSelectionAdjusting(node
)) {
2422 addSelectionPath(getPathFor(node
), false, null);
2428 private void addToDeferred(final Object
[] elementsToSelect
, final Runnable onDone
) {
2429 myDeferredSelections
.clear();
2430 myDeferredSelections
.add(new Runnable() {
2432 select(elementsToSelect
, onDone
, false, true);
2437 private boolean checkDeferred(boolean isDeferred
, @Nullable Runnable onDone
) {
2438 if (!isDeferred
|| myCanProcessDeferredSelections
|| !wasRootNodeInitialized()) {
2448 final Set
<Object
> getSelectedElements() {
2449 final TreePath
[] paths
= myTree
.getSelectionPaths();
2451 Set
<Object
> result
= new HashSet
<Object
>();
2452 if (paths
!= null) {
2453 for (TreePath eachPath
: paths
) {
2454 if (eachPath
.getLastPathComponent() instanceof DefaultMutableTreeNode
) {
2455 final DefaultMutableTreeNode eachNode
= (DefaultMutableTreeNode
)eachPath
.getLastPathComponent();
2456 final Object eachElement
= getElementFor(eachNode
);
2457 if (eachElement
!= null) {
2458 result
.add(eachElement
);
2467 private void addNext(final Object
[] elements
,
2469 @Nullable final Runnable onDone
,
2470 final int[] originalRows
,
2471 final boolean deferred
,
2472 final boolean scrollToVisible
,
2473 final boolean canSmartExpand
) {
2474 if (i
>= elements
.length
) {
2475 if (myTree
.isSelectionEmpty()) {
2476 myTree
.setSelectionRows(originalRows
);
2481 if (!checkDeferred(deferred
, onDone
)) {
2485 doSelect(elements
[i
], new Runnable() {
2487 if (!checkDeferred(deferred
, onDone
)) return;
2489 addNext(elements
, i
+ 1, onDone
, originalRows
, deferred
, scrollToVisible
, canSmartExpand
);
2491 }, true, deferred
, i
== 0, scrollToVisible
, canSmartExpand
);
2495 public void select(final Object element
, @Nullable final Runnable onDone
) {
2496 select(element
, onDone
, false);
2499 public void select(final Object element
, @Nullable final Runnable onDone
, boolean addToSelection
) {
2500 _select(new Object
[]{element
}, onDone
, addToSelection
, true, false);
2503 private void doSelect(final Object element
,
2504 final Runnable onDone
,
2505 final boolean addToSelection
,
2506 final boolean deferred
,
2507 final boolean canBeCentered
,
2508 final boolean scrollToVisible
,
2509 boolean canSmartExpand
) {
2510 final Runnable _onDone
= new Runnable() {
2512 if (!checkDeferred(deferred
, onDone
)) return;
2513 selectVisible(element
, onDone
, addToSelection
, canBeCentered
, scrollToVisible
);
2516 _expand(element
, _onDone
, true, false, canSmartExpand
);
2519 public void scrollSelectionToVisible(@Nullable Runnable onDone
, boolean shouldBeCentered
) {
2520 int[] rows
= myTree
.getSelectionRows();
2521 if (rows
== null || rows
.length
== 0) {
2527 Object toSelect
= null;
2528 for (int eachRow
: rows
) {
2529 TreePath path
= myTree
.getPathForRow(eachRow
);
2530 toSelect
= getElementFor(path
.getLastPathComponent());
2531 if (toSelect
!= null) break;
2534 if (toSelect
!= null) {
2535 selectVisible(toSelect
, onDone
, true, shouldBeCentered
, true);
2539 private void selectVisible(Object element
, final Runnable onDone
, boolean addToSelection
, boolean canBeCentered
, final boolean scroll
) {
2540 final DefaultMutableTreeNode toSelect
= getNodeForElement(element
, false);
2542 if (toSelect
== null) {
2547 if (getRootNode() == toSelect
&& !myTree
.isRootVisible()) {
2552 final int row
= myTree
.getRowForPath(new TreePath(toSelect
.getPath()));
2554 if (myUpdaterState
!= null) {
2555 myUpdaterState
.addSelection(element
);
2558 if (Registry
.is("ide.tree.autoscrollToVCenter") && canBeCentered
) {
2559 runDone(new Runnable() {
2561 TreeUtil
.showRowCentered(myTree
, row
, false, scroll
).doWhenDone(new Runnable() {
2570 TreeUtil
.showAndSelect(myTree
, row
- 2, row
+ 2, row
, -1, addToSelection
, scroll
).doWhenDone(new Runnable() {
2578 public void expand(final Object element
, @Nullable final Runnable onDone
) {
2579 expand(new Object
[] {element
}, onDone
);
2582 public void expand(final Object
[] element
, @Nullable final Runnable onDone
) {
2583 expand(element
, onDone
, false);
2587 void expand(final Object element
, @Nullable final Runnable onDone
, boolean checkIfInStructure
) {
2588 _expand(new Object
[]{element
}, onDone
== null ?
new EmptyRunnable() : onDone
, false, checkIfInStructure
, false);
2591 void expand(final Object
[] element
, @Nullable final Runnable onDone
, boolean checkIfInStructure
) {
2592 _expand(element
, onDone
== null ?
new EmptyRunnable() : onDone
, false, checkIfInStructure
, false);
2595 void _expand(final Object
[] element
,
2596 @NotNull final Runnable onDone
,
2597 final boolean parentsOnly
,
2598 final boolean checkIfInStructure
,
2599 final boolean canSmartExpand
) {
2601 runDone(new Runnable() {
2603 if (element
.length
== 0) {
2608 if (myUpdaterState
!= null) {
2609 myUpdaterState
.clearExpansion();
2613 final ActionCallback done
= new ActionCallback(element
.length
);
2614 done
.doWhenDone(new Runnable() {
2620 for (final Object toExpand
: element
) {
2621 _expand(toExpand
, new Runnable() {
2625 }, parentsOnly
, checkIfInStructure
, canSmartExpand
);
2631 public void collapseChildren(final Object element
, @Nullable final Runnable onDone
) {
2632 runDone(new Runnable() {
2634 final DefaultMutableTreeNode node
= getNodeForElement(element
, false);
2636 getTree().collapsePath(new TreePath(node
.getPath()));
2643 private void runDone(@Nullable Runnable done
) {
2644 if (isReleased()) return;
2645 if (done
== null) return;
2647 if (isYeildingNow()) {
2648 if (!myYeildingDoneRunnables
.contains(done
)) {
2649 myYeildingDoneRunnables
.add(done
);
2657 private void _expand(final Object element
,
2658 @NotNull final Runnable onDone
,
2659 final boolean parentsOnly
,
2660 boolean checkIfInStructure
,
2661 boolean canSmartExpand
) {
2663 if (checkIfInStructure
&& !isInStructure(element
)) {
2668 if (wasRootNodeInitialized()) {
2669 List
<Object
> kidsToExpand
= new ArrayList
<Object
>();
2670 Object eachElement
= element
;
2671 DefaultMutableTreeNode firstVisible
= null;
2673 if (!isValid(eachElement
)) break;
2675 firstVisible
= getNodeForElement(eachElement
, true);
2676 if (eachElement
!= element
|| !parentsOnly
) {
2677 assert !kidsToExpand
.contains(eachElement
) :
2678 "Not a valid tree structure, walking up the structure gives many entries for element=" +
2681 getTreeStructure().getRootElement();
2682 kidsToExpand
.add(eachElement
);
2684 if (firstVisible
!= null) break;
2685 eachElement
= getTreeStructure().getParentElement(eachElement
);
2686 if (eachElement
== null) {
2687 firstVisible
= null;
2693 if (firstVisible
== null) {
2696 else if (kidsToExpand
.size() == 0) {
2697 final DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)firstVisible
.getParent();
2698 if (parentNode
!= null) {
2699 final TreePath parentPath
= new TreePath(parentNode
.getPath());
2700 if (!myTree
.isExpanded(parentPath
)) {
2701 expand(parentPath
, canSmartExpand
);
2707 processExpand(firstVisible
, kidsToExpand
, kidsToExpand
.size() - 1, onDone
, canSmartExpand
);
2711 deferExpansion(element
, onDone
, parentsOnly
, canSmartExpand
);
2715 private void deferExpansion(final Object element
, final Runnable onDone
, final boolean parentsOnly
, final boolean canSmartExpand
) {
2716 myDeferredExpansions
.add(new Runnable() {
2718 _expand(element
, onDone
, parentsOnly
, false, canSmartExpand
);
2723 private void processExpand(final DefaultMutableTreeNode toExpand
,
2724 final List kidsToExpand
,
2725 final int expandIndex
,
2726 @NotNull final Runnable onDone
,
2727 final boolean canSmartExpand
) {
2729 final Object element
= getElementFor(toExpand
);
2730 if (element
== null) {
2735 addNodeAction(element
, new NodeAction() {
2736 public void onReady(final DefaultMutableTreeNode node
) {
2738 if (node
.getChildCount() > 0 && !myTree
.isExpanded(new TreePath(node
.getPath()))) {
2739 if (!isAutoExpand(node
)) {
2740 expand(node
, canSmartExpand
);
2744 if (expandIndex
<= 0) {
2749 final DefaultMutableTreeNode nextNode
= getNodeForElement(kidsToExpand
.get(expandIndex
- 1), false);
2750 if (nextNode
!= null) {
2751 processExpand(nextNode
, kidsToExpand
, expandIndex
- 1, onDone
, canSmartExpand
);
2760 if (myTree
.isExpanded(getPathFor(toExpand
)) && !myUnbuiltNodes
.contains(toExpand
)) {
2761 processNodeActionsIfReady(toExpand
);
2763 if (!myUnbuiltNodes
.contains(toExpand
)) {
2764 getUpdater().addSubtreeToUpdate(toExpand
);
2766 expand(toExpand
, canSmartExpand
);
2772 private String
asString(DefaultMutableTreeNode node
) {
2773 if (node
== null) return null;
2775 StringBuffer children
= new StringBuffer(node
.toString());
2776 children
.append(" [");
2777 for (int i
= 0; i
< node
.getChildCount(); i
++) {
2778 children
.append(node
.getChildAt(i
));
2779 if (i
< node
.getChildCount() - 1) {
2780 children
.append(",");
2783 children
.append("]");
2785 return children
.toString();
2789 public Object
getElementFor(Object node
) {
2790 if (!(node
instanceof DefaultMutableTreeNode
)) return null;
2791 return getElementFor((DefaultMutableTreeNode
)node
);
2795 Object
getElementFor(DefaultMutableTreeNode node
) {
2797 final Object o
= node
.getUserObject();
2798 if (o
instanceof NodeDescriptor
) {
2799 return getElementFromDescriptor(((NodeDescriptor
)o
));
2806 public final boolean isNodeBeingBuilt(final TreePath path
) {
2807 return isNodeBeingBuilt(path
.getLastPathComponent());
2810 public final boolean isNodeBeingBuilt(Object node
) {
2811 if (isParentLoading(node
) || isLoadingParent(node
)) return true;
2813 final boolean childrenAreNoLoadedYet
= myUnbuiltNodes
.contains(node
);
2814 if (childrenAreNoLoadedYet
) {
2815 if (node
instanceof DefaultMutableTreeNode
) {
2816 final TreePath nodePath
= new TreePath(((DefaultMutableTreeNode
)node
).getPath());
2817 if (!myTree
.isExpanded(nodePath
)) return false;
2827 private boolean isLoadingParent(Object node
) {
2828 if (!(node
instanceof DefaultMutableTreeNode
)) return false;
2829 return isLoadedInBackground(getElementFor((DefaultMutableTreeNode
)node
));
2832 public void setTreeStructure(final AbstractTreeStructure treeStructure
) {
2833 myTreeStructure
= treeStructure
;
2834 clearUpdaterState();
2837 public AbstractTreeUpdater
getUpdater() {
2841 public void setUpdater(final AbstractTreeUpdater updater
) {
2842 myUpdater
= updater
;
2843 if (updater
!= null && myUpdateIfInactive
) {
2844 updater
.showNotify();
2848 public DefaultMutableTreeNode
getRootNode() {
2852 public void setRootNode(@NotNull final DefaultMutableTreeNode rootNode
) {
2853 myRootNode
= rootNode
;
2856 private void dropUpdaterStateIfExternalChange() {
2857 if (myUpdaterState
!= null && !myUpdaterState
.isProcessingNow()) {
2858 clearUpdaterState();
2862 void clearUpdaterState() {
2863 myUpdaterState
= null;
2866 private void createMapping(Object element
, DefaultMutableTreeNode node
) {
2867 if (!myElementToNodeMap
.containsKey(element
)) {
2868 myElementToNodeMap
.put(element
, node
);
2871 final Object value
= myElementToNodeMap
.get(element
);
2872 final List
<DefaultMutableTreeNode
> nodes
;
2873 if (value
instanceof DefaultMutableTreeNode
) {
2874 nodes
= new ArrayList
<DefaultMutableTreeNode
>();
2875 nodes
.add((DefaultMutableTreeNode
)value
);
2876 myElementToNodeMap
.put(element
, nodes
);
2879 nodes
= (List
<DefaultMutableTreeNode
>)value
;
2885 private void removeMapping(Object element
, DefaultMutableTreeNode node
, @Nullable Object elementToPutNodeActionsFor
) {
2886 final Object value
= myElementToNodeMap
.get(element
);
2887 if (value
!= null) {
2888 if (value
instanceof DefaultMutableTreeNode
) {
2889 if (value
.equals(node
)) {
2890 myElementToNodeMap
.remove(element
);
2894 List
<DefaultMutableTreeNode
> nodes
= (List
<DefaultMutableTreeNode
>)value
;
2895 final boolean reallyRemoved
= nodes
.remove(node
);
2896 if (reallyRemoved
) {
2897 if (nodes
.isEmpty()) {
2898 myElementToNodeMap
.remove(element
);
2904 remapNodeActions(element
, elementToPutNodeActionsFor
);
2907 private void remapNodeActions(Object element
, Object elementToPutNodeActionsFor
) {
2908 _remapNodeActions(element
, elementToPutNodeActionsFor
, myNodeActions
);
2909 _remapNodeActions(element
, elementToPutNodeActionsFor
, myNodeChildrenActions
);
2912 private void _remapNodeActions(Object element
, Object elementToPutNodeActionsFor
, final Map
<Object
, List
<NodeAction
>> nodeActions
) {
2913 final List
<NodeAction
> actions
= nodeActions
.get(element
);
2914 nodeActions
.remove(element
);
2916 if (elementToPutNodeActionsFor
!= null && actions
!= null) {
2917 nodeActions
.put(elementToPutNodeActionsFor
, actions
);
2921 private DefaultMutableTreeNode
getFirstNode(Object element
) {
2922 return findNode(element
, 0);
2925 private DefaultMutableTreeNode
findNode(final Object element
, int startIndex
) {
2926 final Object value
= getBuilder().findNodeByElement(element
);
2927 if (value
== null) {
2930 if (value
instanceof DefaultMutableTreeNode
) {
2931 return startIndex
== 0 ?
(DefaultMutableTreeNode
)value
: null;
2933 final List
<DefaultMutableTreeNode
> nodes
= (List
<DefaultMutableTreeNode
>)value
;
2934 return startIndex
< nodes
.size() ? nodes
.get(startIndex
) : null;
2937 protected Object
findNodeByElement(Object element
) {
2938 if (myElementToNodeMap
.containsKey(element
)) {
2939 return myElementToNodeMap
.get(element
);
2943 TREE_NODE_WRAPPER
.setValue(element
);
2944 return myElementToNodeMap
.get(TREE_NODE_WRAPPER
);
2947 TREE_NODE_WRAPPER
.setValue(null);
2951 private DefaultMutableTreeNode
findNodeForChildElement(DefaultMutableTreeNode parentNode
, Object element
) {
2952 final Object value
= myElementToNodeMap
.get(element
);
2953 if (value
== null) {
2957 if (value
instanceof DefaultMutableTreeNode
) {
2958 final DefaultMutableTreeNode elementNode
= (DefaultMutableTreeNode
)value
;
2959 return parentNode
.equals(elementNode
.getParent()) ? elementNode
: null;
2962 final List
<DefaultMutableTreeNode
> allNodesForElement
= (List
<DefaultMutableTreeNode
>)value
;
2963 for (final DefaultMutableTreeNode elementNode
: allNodesForElement
) {
2964 if (parentNode
.equals(elementNode
.getParent())) {
2972 public void cancelBackgroundLoading() {
2973 if (myWorker
!= null) {
2974 myWorker
.cancelTasks();
2981 private void addNodeAction(Object element
, NodeAction action
, boolean shouldChildrenBeReady
) {
2982 _addNodeAction(element
, action
, myNodeActions
);
2983 if (shouldChildrenBeReady
) {
2984 _addNodeAction(element
, action
, myNodeChildrenActions
);
2989 private void _addNodeAction(Object element
, NodeAction action
, Map
<Object
, List
<NodeAction
>> map
) {
2990 maybeSetBusyAndScheduleWaiterForReady(true);
2991 List
<NodeAction
> list
= map
.get(element
);
2993 list
= new ArrayList
<NodeAction
>();
2994 map
.put(element
, list
);
3000 private void cleanUpNow() {
3001 if (isReleased()) return;
3003 final UpdaterTreeState state
= new UpdaterTreeState(this);
3005 myTree
.collapsePath(new TreePath(myTree
.getModel().getRoot()));
3006 myTree
.clearSelection();
3007 getRootNode().removeAllChildren();
3009 myRootNodeWasInitialized
= false;
3011 myElementToNodeMap
.clear();
3012 myDeferredSelections
.clear();
3013 myDeferredExpansions
.clear();
3014 myLoadingParents
.clear();
3015 myUnbuiltNodes
.clear();
3016 myUpdateFromRootRequested
= true;
3018 if (myWorker
!= null) {
3019 Disposer
.dispose(myWorker
);
3023 myTree
.invalidate();
3025 state
.restore(null);
3028 public AbstractTreeUi
setClearOnHideDelay(final long clearOnHideDelay
) {
3029 myClearOnHideDelay
= clearOnHideDelay
;
3033 public void setJantorPollPeriod(final long time
) {
3034 myJanitorPollPeriod
= time
;
3037 public void setCheckStructure(final boolean checkStructure
) {
3038 myCheckStructure
= checkStructure
;
3041 private class MySelectionListener
implements TreeSelectionListener
{
3042 public void valueChanged(final TreeSelectionEvent e
) {
3043 dropUpdaterStateIfExternalChange();
3048 private class MyExpansionListener
implements TreeExpansionListener
{
3049 public void treeExpanded(TreeExpansionEvent event
) {
3050 dropUpdaterStateIfExternalChange();
3052 TreePath path
= event
.getPath();
3054 if (myRequestedExpand
!= null && !myRequestedExpand
.equals(path
)) return;
3056 final DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
3058 if (!myUnbuiltNodes
.contains(node
)) {
3059 removeLoading(node
, false);
3061 boolean hasUnbuiltChildren
= false;
3062 for (int i
= 0; i
< node
.getChildCount(); i
++) {
3063 DefaultMutableTreeNode each
= (DefaultMutableTreeNode
)node
.getChildAt(i
);
3064 if (myUnbuiltNodes
.contains(each
)) {
3065 makeLoadingOrLeafIfNoChildren(each
);
3066 hasUnbuiltChildren
= true;
3070 if (hasUnbuiltChildren
) {
3071 getUpdater().addSubtreeToUpdate(node
);
3074 getBuilder().expandNodeChildren(node
);
3077 processSmartExpand(node
, canSmartExpand(node
, true));
3078 processNodeActionsIfReady(node
);
3081 public void treeCollapsed(TreeExpansionEvent e
) {
3082 dropUpdaterStateIfExternalChange();
3084 final TreePath path
= e
.getPath();
3085 final DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
3086 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
3089 TreePath pathToSelect
= null;
3090 if (isSelectionInside(node
)) {
3091 pathToSelect
= new TreePath(node
.getPath());
3095 NodeDescriptor descriptor
= getDescriptorFrom(node
);
3096 if (getBuilder().isDisposeOnCollapsing(descriptor
)) {
3097 runDone(new Runnable() {
3099 if (isDisposed(node
)) return;
3101 TreePath nodePath
= new TreePath(node
.getPath());
3102 if (myTree
.isExpanded(nodePath
)) return;
3104 removeChildren(node
);
3105 makeLoadingOrLeafIfNoChildren(node
);
3108 if (node
.equals(getRootNode())) {
3109 if (myTree
.isRootVisible()) {
3110 //todo kirillk to investigate -- should be done by standard selction move
3111 //addSelectionPath(new TreePath(getRootNode().getPath()), true, Condition.FALSE);
3115 myTreeModel
.reload(node
);
3119 if (pathToSelect
!= null && myTree
.isSelectionEmpty()) {
3120 addSelectionPath(pathToSelect
, true, Condition
.FALSE
);
3124 private void removeChildren(DefaultMutableTreeNode node
) {
3125 EnumerationCopy copy
= new EnumerationCopy(node
.children());
3126 while (copy
.hasMoreElements()) {
3127 disposeNode((DefaultMutableTreeNode
)copy
.nextElement());
3129 node
.removeAllChildren();
3130 myTreeModel
.nodeStructureChanged(node
);
3133 private boolean isSelectionInside(DefaultMutableTreeNode parent
) {
3134 TreePath path
= new TreePath(myTreeModel
.getPathToRoot(parent
));
3135 TreePath
[] paths
= myTree
.getSelectionPaths();
3136 if (paths
== null) return false;
3137 for (TreePath path1
: paths
) {
3138 if (path
.isDescendant(path1
)) return true;
3144 public boolean isInStructure(@Nullable Object element
) {
3145 Object eachParent
= element
;
3146 while (eachParent
!= null) {
3147 if (getTreeStructure().getRootElement().equals(eachParent
)) return true;
3148 eachParent
= getTreeStructure().getParentElement(eachParent
);
3154 interface NodeAction
{
3155 void onReady(DefaultMutableTreeNode node
);
3158 public void setCanYield(final boolean canYield
) {
3159 myCanYield
= canYield
;
3162 public Collection
<TreeUpdatePass
> getYeildingPasses() {
3163 return myYeildingPasses
;
3166 public boolean isBuilt(Object element
) {
3167 if (!myElementToNodeMap
.containsKey(element
)) return false;
3168 final Object node
= myElementToNodeMap
.get(element
);
3169 return !myUnbuiltNodes
.contains(node
);
3172 static class LoadedChildren
{
3174 private List myElements
;
3175 private Map
<Object
, NodeDescriptor
> myDescriptors
= new HashMap
<Object
, NodeDescriptor
>();
3176 private Map
<NodeDescriptor
, Boolean
> myChanges
= new HashMap
<NodeDescriptor
, Boolean
>();
3178 LoadedChildren(Object
[] elements
) {
3179 myElements
= Arrays
.asList(elements
!= null ? elements
: new Object
[0]);
3182 void putDescriptor(Object element
, NodeDescriptor descriptor
, boolean isChanged
) {
3183 assert myElements
.contains(element
);
3184 myDescriptors
.put(element
, descriptor
);
3185 myChanges
.put(descriptor
, isChanged
);
3188 List
getElements() {
3192 NodeDescriptor
getDescriptor(Object element
) {
3193 return myDescriptors
.get(element
);
3197 public String
toString() {
3198 return Arrays
.asList(myElements
) + "->" + myChanges
;
3201 public boolean isUpdated(Object element
) {
3202 NodeDescriptor desc
= getDescriptor(element
);
3203 return myChanges
.get(desc
);
3207 UpdaterTreeState
getUpdaterState() {
3208 return myUpdaterState
;
3211 private ActionCallback
addReadyCallback(Object requestor
) {
3212 synchronized (myReadyCallbacks
) {
3213 ActionCallback cb
= myReadyCallbacks
.get(requestor
);
3215 cb
= new ActionCallback();
3216 myReadyCallbacks
.put(requestor
, cb
);
3223 private ActionCallback
[] getReadyCallbacks(boolean clear
) {
3224 synchronized (myReadyCallbacks
) {
3225 ActionCallback
[] result
= myReadyCallbacks
.values().toArray(new ActionCallback
[myReadyCallbacks
.size()]);
3227 myReadyCallbacks
.clear();