2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.ide
.util
.treeView
;
18 import com
.intellij
.ide
.IdeBundle
;
19 import com
.intellij
.openapi
.application
.Application
;
20 import com
.intellij
.openapi
.application
.ApplicationManager
;
21 import com
.intellij
.openapi
.diagnostic
.Logger
;
22 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
23 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
24 import com
.intellij
.openapi
.progress
.ProgressManager
;
25 import com
.intellij
.openapi
.project
.IndexNotReadyException
;
26 import com
.intellij
.openapi
.util
.*;
27 import com
.intellij
.openapi
.util
.registry
.Registry
;
28 import com
.intellij
.openapi
.util
.registry
.RegistryValue
;
29 import com
.intellij
.ui
.LoadingNode
;
30 import com
.intellij
.ui
.treeStructure
.Tree
;
31 import com
.intellij
.util
.Alarm
;
32 import com
.intellij
.util
.ArrayUtil
;
33 import com
.intellij
.util
.ConcurrencyUtil
;
34 import com
.intellij
.util
.Time
;
35 import com
.intellij
.util
.concurrency
.WorkerThread
;
36 import com
.intellij
.util
.containers
.HashSet
;
37 import com
.intellij
.util
.enumeration
.EnumerationCopy
;
38 import com
.intellij
.util
.ui
.UIUtil
;
39 import com
.intellij
.util
.ui
.tree
.TreeUtil
;
40 import com
.intellij
.util
.ui
.update
.Activatable
;
41 import com
.intellij
.util
.ui
.update
.UiNotifyConnector
;
42 import org
.jetbrains
.annotations
.NotNull
;
43 import org
.jetbrains
.annotations
.Nullable
;
46 import javax
.swing
.event
.TreeExpansionEvent
;
47 import javax
.swing
.event
.TreeExpansionListener
;
48 import javax
.swing
.event
.TreeSelectionEvent
;
49 import javax
.swing
.event
.TreeSelectionListener
;
50 import javax
.swing
.tree
.*;
52 import java
.awt
.event
.FocusAdapter
;
53 import java
.awt
.event
.FocusEvent
;
54 import java
.security
.AccessControlException
;
56 import java
.util
.List
;
57 import java
.util
.concurrent
.ScheduledExecutorService
;
58 import java
.util
.concurrent
.TimeUnit
;
60 public class AbstractTreeUi
{
61 private static final Logger LOG
= Logger
.getInstance("#com.intellij.ide.util.treeView.AbstractTreeBuilder");
62 protected JTree myTree
;// protected for TestNG
63 @SuppressWarnings({"WeakerAccess"}) protected DefaultTreeModel myTreeModel
;
64 private AbstractTreeStructure myTreeStructure
;
65 private AbstractTreeUpdater myUpdater
;
67 private Comparator
<NodeDescriptor
> myNodeDescriptorComparator
;
68 private final Comparator
<TreeNode
> myNodeComparator
= new Comparator
<TreeNode
>() {
69 public int compare(TreeNode n1
, TreeNode n2
) {
70 if (isLoadingNode(n1
) || isLoadingNode(n2
)) return 0;
71 NodeDescriptor nodeDescriptor1
= getDescriptorFrom(((DefaultMutableTreeNode
)n1
));
72 NodeDescriptor nodeDescriptor2
= getDescriptorFrom(((DefaultMutableTreeNode
)n2
));
73 return myNodeDescriptorComparator
!= null
74 ? myNodeDescriptorComparator
.compare(nodeDescriptor1
, nodeDescriptor2
)
75 : nodeDescriptor1
.getIndex() - nodeDescriptor2
.getIndex();
78 long myOwnComparatorStamp
;
79 long myLastComparatorStamp
;
81 private DefaultMutableTreeNode myRootNode
;
82 private final HashMap
<Object
, Object
> myElementToNodeMap
= new HashMap
<Object
, Object
>();
83 private final HashSet
<DefaultMutableTreeNode
> myUnbuiltNodes
= new HashSet
<DefaultMutableTreeNode
>();
84 private TreeExpansionListener myExpansionListener
;
85 private MySelectionListener mySelectionListener
;
87 private WorkerThread myWorker
= null;
88 private final Set
<Runnable
> myActiveWorkerTasks
= new HashSet
<Runnable
>();
90 private ProgressIndicator myProgress
;
91 private static final int WAIT_CURSOR_DELAY
= 100;
92 private AbstractTreeNode
<Object
> TREE_NODE_WRAPPER
;
93 private boolean myRootNodeWasInitialized
= false;
94 private final Map
<Object
, List
<NodeAction
>> myNodeActions
= new HashMap
<Object
, List
<NodeAction
>>();
95 private boolean myUpdateFromRootRequested
;
96 private boolean myWasEverShown
;
97 private boolean myUpdateIfInactive
;
99 private final Map
<Object
, UpdateInfo
> myLoadedInBackground
= new HashMap
<Object
, UpdateInfo
>();
100 private final Map
<Object
, List
<NodeAction
>> myNodeChildrenActions
= new HashMap
<Object
, List
<NodeAction
>>();
102 private long myClearOnHideDelay
= -1;
103 private ScheduledExecutorService ourClearanceService
;
104 private final Map
<AbstractTreeUi
, Long
> ourUi2Countdown
= Collections
.synchronizedMap(new WeakHashMap
<AbstractTreeUi
, Long
>());
106 private final Set
<Runnable
> myDeferredSelections
= new HashSet
<Runnable
>();
107 private final Set
<Runnable
> myDeferredExpansions
= new HashSet
<Runnable
>();
109 private boolean myCanProcessDeferredSelections
;
111 private UpdaterTreeState myUpdaterState
;
112 private AbstractTreeBuilder myBuilder
;
113 private boolean myReleaseRequested
;
115 private final Set
<DefaultMutableTreeNode
> myUpdatingChildren
= new HashSet
<DefaultMutableTreeNode
>();
116 private long myJanitorPollPeriod
= Time
.SECOND
* 10;
117 private boolean myCheckStructure
= false;
120 private boolean myCanYield
= false;
122 private final List
<TreeUpdatePass
> myYeildingPasses
= new ArrayList
<TreeUpdatePass
>();
124 private boolean myYeildingNow
;
126 private final Set
<DefaultMutableTreeNode
> myPendingNodeActions
= new HashSet
<DefaultMutableTreeNode
>();
127 private final Set
<Runnable
> myYeildingDoneRunnables
= new HashSet
<Runnable
>();
129 private final Alarm myBusyAlarm
= new Alarm();
130 private final Runnable myWaiterForReady
= new Runnable() {
132 maybeSetBusyAndScheduleWaiterForReady(false);
136 private final RegistryValue myYeildingUpdate
= Registry
.get("ide.tree.yeildingUiUpdate");
137 private final RegistryValue myShowBusyIndicator
= Registry
.get("ide.tree.showBusyIndicator");
138 private final RegistryValue myWaitForReadyTime
= Registry
.get("ide.tree.waitForReadyTimout");
140 private boolean myWasEverIndexNotReady
;
141 private boolean myShowing
;
142 private FocusAdapter myFocusListener
= new FocusAdapter() {
144 public void focusGained(FocusEvent e
) {
148 private Set
<DefaultMutableTreeNode
> myNotForSmartExpand
= new HashSet
<DefaultMutableTreeNode
>();
149 private TreePath myRequestedExpand
;
150 private ActionCallback myInitialized
= new ActionCallback();
151 private Map
<Object
, ActionCallback
> myReadyCallbacks
= new WeakHashMap
<Object
, ActionCallback
>();
153 private boolean myPassthroughMode
= false;
156 private Set
<Object
> myAutoExpandRoots
= new HashSet
<Object
>();
157 private final RegistryValue myAutoExpandDepth
= Registry
.get("ide.tree.autoExpandMaxDepth");
159 private Set
<DefaultMutableTreeNode
> myWillBeExpaned
= new HashSet
<DefaultMutableTreeNode
>();
161 protected void init(AbstractTreeBuilder builder
,
163 DefaultTreeModel treeModel
,
164 AbstractTreeStructure treeStructure
,
165 @Nullable Comparator
<NodeDescriptor
> comparator
,
166 boolean updateIfInactive
) {
169 myTreeModel
= treeModel
;
170 TREE_NODE_WRAPPER
= getBuilder().createSearchingTreeNodeWrapper();
171 myTree
.setModel(myTreeModel
);
172 setRootNode((DefaultMutableTreeNode
)treeModel
.getRoot());
173 setTreeStructure(treeStructure
);
174 myNodeDescriptorComparator
= comparator
;
175 myUpdateIfInactive
= updateIfInactive
;
177 myExpansionListener
= new MyExpansionListener();
178 myTree
.addTreeExpansionListener(myExpansionListener
);
180 mySelectionListener
= new MySelectionListener();
181 myTree
.addTreeSelectionListener(mySelectionListener
);
183 setUpdater(getBuilder().createUpdater());
184 myProgress
= getBuilder().createProgressIndicator();
185 Disposer
.register(getBuilder(), getUpdater());
187 final UiNotifyConnector uiNotify
= new UiNotifyConnector(tree
, new Activatable() {
188 public void showNotify() {
190 myWasEverShown
= true;
191 if (!isReleaseRequested()) {
196 public void hideNotify() {
198 if (!validateReleaseRequested()) {
203 Disposer
.register(getBuilder(), uiNotify
);
205 myTree
.addFocusListener(myFocusListener
);
209 boolean isNodeActionsPending() {
210 return !myNodeActions
.isEmpty() || !myNodeChildrenActions
.isEmpty();
213 private void clearNodeActions() {
214 myNodeActions
.clear();
215 myNodeChildrenActions
.clear();
218 private void maybeSetBusyAndScheduleWaiterForReady(boolean forcedBusy
) {
219 if (!myShowBusyIndicator
.asBoolean() || !canYield()) return;
221 if (myTree
instanceof com
.intellij
.ui
.treeStructure
.Tree
) {
222 final com
.intellij
.ui
.treeStructure
.Tree tree
= (Tree
)myTree
;
223 final boolean isBusy
= !isReady() || forcedBusy
;
224 if (isBusy
&& tree
.isShowing()) {
225 tree
.setPaintBusy(true);
226 myBusyAlarm
.cancelAllRequests();
227 myBusyAlarm
.addRequest(myWaiterForReady
, myWaitForReadyTime
.asInteger());
230 tree
.setPaintBusy(false);
235 private void initClearanceServiceIfNeeded() {
236 if (ourClearanceService
!= null) return;
238 ourClearanceService
= ConcurrencyUtil
.newSingleScheduledThreadExecutor("AbstractTreeBuilder's janitor", Thread
.MIN_PRIORITY
+ 1);
239 ourClearanceService
.scheduleWithFixedDelay(new Runnable() {
243 }, myJanitorPollPeriod
, myJanitorPollPeriod
, TimeUnit
.MILLISECONDS
);
246 private void cleanUpAll() {
247 final long now
= System
.currentTimeMillis();
248 final AbstractTreeUi
[] uis
= ourUi2Countdown
.keySet().toArray(new AbstractTreeUi
[ourUi2Countdown
.size()]);
249 for (AbstractTreeUi eachUi
: uis
) {
250 if (eachUi
== null) continue;
251 final Long timeToCleanup
= ourUi2Countdown
.get(eachUi
);
252 if (timeToCleanup
== null) continue;
253 if (now
>= timeToCleanup
.longValue()) {
254 ourUi2Countdown
.remove(eachUi
);
255 Runnable runnable
= new Runnable() {
257 getBuilder().cleanUp();
260 if (isPassthroughMode()) {
263 UIUtil
.invokeAndWaitIfNeeded(runnable
);
269 protected void doCleanUp() {
270 Runnable cleanup
= new Runnable() {
272 if (!isReleaseRequested()) {
278 if (isPassthroughMode()) {
281 UIUtil
.invokeLaterIfNeeded(cleanup
);
285 private void disposeClearanceService() {
287 if (ourClearanceService
!= null) {
288 ourClearanceService
.shutdown();
289 ourClearanceService
= null;
292 catch (AccessControlException e
) {
297 public void activate(boolean byShowing
) {
298 myCanProcessDeferredSelections
= true;
299 ourUi2Countdown
.remove(this);
301 if (!myWasEverShown
|| myUpdateFromRootRequested
|| myUpdateIfInactive
) {
302 getBuilder().updateFromRoot();
305 getUpdater().showNotify();
307 myWasEverShown
|= byShowing
;
310 public void deactivate() {
311 getUpdater().hideNotify();
312 myBusyAlarm
.cancelAllRequests();
314 if (!myWasEverShown
) return;
316 if (isNodeActionsPending()) {
317 cancelBackgroundLoading();
318 myUpdateFromRootRequested
= true;
321 if (getClearOnHideDelay() >= 0) {
322 ourUi2Countdown
.put(this, System
.currentTimeMillis() + getClearOnHideDelay());
323 initClearanceServiceIfNeeded();
327 public void requestRelease() {
328 if (isReleaseRequested()) return;
330 assertIsDispatchThread();
332 myReleaseRequested
= true;
334 getUpdater().requestRelease();
339 private void releaseNow() {
340 myTree
.removeTreeExpansionListener(myExpansionListener
);
341 myTree
.removeTreeSelectionListener(mySelectionListener
);
342 myTree
.removeFocusListener(myFocusListener
);
344 disposeNode(getRootNode());
345 myElementToNodeMap
.clear();
346 getUpdater().cancelAllRequests();
347 if (myWorker
!= null) {
348 myWorker
.dispose(true);
351 TREE_NODE_WRAPPER
.setValue(null);
352 if (myProgress
!= null) {
355 disposeClearanceService();
360 myTreeStructure
= null;
361 myBuilder
.releaseUi();
366 myDeferredSelections
.clear();
367 myDeferredExpansions
.clear();
368 myYeildingDoneRunnables
.clear();
371 public boolean isReleaseRequested() {
372 return myReleaseRequested
;
375 public boolean validateReleaseRequested() {
376 if (isReleaseRequested()) {
377 SwingUtilities
.invokeLater(new Runnable() {
388 public boolean isReleased() {
389 return myBuilder
== null;
392 protected void doExpandNodeChildren(final DefaultMutableTreeNode node
) {
393 if (!myUnbuiltNodes
.contains(node
)) return;
394 if (isLoadedInBackground(getElementFor(node
))) return;
396 if (!isReleaseRequested()) {
397 getTreeStructure().commit();
398 addSubtreeToUpdate(node
);
399 getUpdater().performUpdate();
401 processNodeActionsIfReady(node
);
405 public final AbstractTreeStructure
getTreeStructure() {
406 return myTreeStructure
;
409 public final JTree
getTree() {
414 private NodeDescriptor
getDescriptorFrom(DefaultMutableTreeNode node
) {
415 return (NodeDescriptor
)node
.getUserObject();
419 public final DefaultMutableTreeNode
getNodeForElement(Object element
, final boolean validateAgainstStructure
) {
420 DefaultMutableTreeNode result
= null;
421 if (validateAgainstStructure
) {
424 final DefaultMutableTreeNode node
= findNode(element
, index
);
425 if (node
== null) break;
427 if (isNodeValidForElement(element
, node
)) {
436 result
= getFirstNode(element
);
440 if (result
!= null && !isNodeInStructure(result
)) {
448 private boolean isNodeInStructure(DefaultMutableTreeNode node
) {
449 return TreeUtil
.isAncestor(getRootNode(), node
) && getRootNode() == myTreeModel
.getRoot();
452 private boolean isNodeValidForElement(final Object element
, final DefaultMutableTreeNode node
) {
453 return isSameHierarchy(element
, node
) || isValidChildOfParent(element
, node
);
456 private boolean isValidChildOfParent(final Object element
, final DefaultMutableTreeNode node
) {
457 final DefaultMutableTreeNode parent
= (DefaultMutableTreeNode
)node
.getParent();
458 final Object parentElement
= getElementFor(parent
);
459 if (!isInStructure(parentElement
)) return false;
461 if (parent
instanceof ElementNode
) {
462 return ((ElementNode
)parent
).isValidChild(element
);
465 for (int i
= 0; i
< parent
.getChildCount(); i
++) {
466 final TreeNode child
= parent
.getChildAt(i
);
467 final Object eachElement
= getElementFor(child
);
468 if (element
.equals(eachElement
)) return true;
475 private boolean isSameHierarchy(Object eachParent
, DefaultMutableTreeNode eachParentNode
) {
476 boolean valid
= true;
478 if (eachParent
== null) {
479 valid
= eachParentNode
== null;
483 if (!eachParent
.equals(getElementFor(eachParentNode
))) {
488 eachParent
= getTreeStructure().getParentElement(eachParent
);
489 eachParentNode
= (DefaultMutableTreeNode
)eachParentNode
.getParent();
494 public final DefaultMutableTreeNode
getNodeForPath(Object
[] path
) {
495 DefaultMutableTreeNode node
= null;
496 for (final Object pathElement
: path
) {
497 node
= node
== null ?
getFirstNode(pathElement
) : findNodeForChildElement(node
, pathElement
);
505 public final void buildNodeForElement(Object element
) {
506 getUpdater().performUpdate();
507 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
509 final java
.util
.List
<Object
> elements
= new ArrayList
<Object
>();
511 element
= getTreeStructure().getParentElement(element
);
512 if (element
== null) {
515 elements
.add(0, element
);
518 for (final Object element1
: elements
) {
519 node
= getNodeForElement(element1
, false);
527 public final void buildNodeForPath(Object
[] path
) {
528 getUpdater().performUpdate();
529 DefaultMutableTreeNode node
= null;
530 for (final Object pathElement
: path
) {
531 node
= node
== null ?
getFirstNode(pathElement
) : findNodeForChildElement(node
, pathElement
);
532 if (node
!= null && node
!= path
[path
.length
- 1]) {
538 public final void setNodeDescriptorComparator(Comparator
<NodeDescriptor
> nodeDescriptorComparator
) {
539 myNodeDescriptorComparator
= nodeDescriptorComparator
;
540 myLastComparatorStamp
= -1;
541 getBuilder().queueUpdateFrom(getTreeStructure().getRootElement(), true);
544 protected AbstractTreeBuilder
getBuilder() {
548 protected final void initRootNode() {
549 if (myUpdateIfInactive
) {
553 myUpdateFromRootRequested
= true;
557 private boolean initRootNodeNowIfNeeded(final TreeUpdatePass pass
) {
558 boolean wasCleanedUp
= false;
559 if (myRootNodeWasInitialized
) {
560 Object root
= getTreeStructure().getRootElement();
561 assert root
!= null : "Root element cannot be null";
563 Object currentRoot
= getElementFor(myRootNode
);
565 if (Comparing
.equal(root
, currentRoot
)) return false;
567 Object rootAgain
= getTreeStructure().getRootElement();
568 if (root
!= rootAgain
&& !root
.equals(rootAgain
)) {
569 assert false : "getRootElement() if called twice must return either root1 == root2 or root1.equals(root2)";
576 myRootNodeWasInitialized
= true;
578 final Object rootElement
= getTreeStructure().getRootElement();
579 addNodeAction(rootElement
, new NodeAction() {
580 public void onReady(final DefaultMutableTreeNode node
) {
581 processDeferredActions();
586 final Ref
<NodeDescriptor
> rootDescriptor
= new Ref
<NodeDescriptor
>(null);
587 final boolean bgLoading
= getTreeStructure().isToBuildChildrenInBackground(rootElement
);
589 Runnable build
= new Runnable() {
591 rootDescriptor
.set(getTreeStructure().createDescriptor(rootElement
, null));
592 getRootNode().setUserObject(rootDescriptor
.get());
593 update(rootDescriptor
.get(), true);
598 Runnable update
= new Runnable() {
600 if (getElementFromDescriptor(rootDescriptor
.get()) != null) {
601 createMapping(getElementFromDescriptor(rootDescriptor
.get()), getRootNode());
605 insertLoadingNode(getRootNode(), true);
607 boolean willUpdate
= false;
608 if (isAutoExpand(rootDescriptor
.get())) {
609 willUpdate
= myUnbuiltNodes
.contains(getRootNode());
610 expand(getRootNode(), true);
613 updateNodeChildren(getRootNode(), pass
, null, false, false, false, true);
615 if (getRootNode().getChildCount() == 0) {
616 myTreeModel
.nodeChanged(getRootNode());
622 queueToBackground(build
, update
);
632 private boolean isAutoExpand(NodeDescriptor descriptor
) {
633 return isAutoExpand(descriptor
, true);
636 private boolean isAutoExpand(NodeDescriptor descriptor
, boolean validate
) {
637 boolean autoExpand
= false;
639 if (descriptor
!= null) {
640 autoExpand
= getBuilder().isAutoExpandNode(descriptor
);
643 Object element
= getElementFromDescriptor(descriptor
);
645 autoExpand
= validateAutoExpand(autoExpand
, element
);
648 if (!autoExpand
&& !myTree
.isRootVisible()) {
649 if (element
!= null && element
.equals(getTreeStructure().getRootElement())) return true;
655 private boolean validateAutoExpand(boolean autoExpand
, Object element
) {
657 int distance
= getDistanceToAutoExpandRoot(element
);
659 myAutoExpandRoots
.add(element
);
661 if (distance
>= myAutoExpandDepth
.asInteger() - 1) {
667 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
668 if (isInVisibleAutoExpandChain(node
)) {
678 private boolean isInVisibleAutoExpandChain(DefaultMutableTreeNode child
) {
679 TreeNode eachParent
= child
;
680 while (eachParent
!= null) {
682 if (myRootNode
== eachParent
) return true;
684 NodeDescriptor eachDescriptor
= getDescriptorFrom((DefaultMutableTreeNode
)eachParent
);
685 if (!isAutoExpand(eachDescriptor
, false)) {
686 TreePath path
= getPathFor(eachParent
);
687 if (myWillBeExpaned
.contains(path
.getLastPathComponent()) || (myTree
.isExpanded(path
) && myTree
.isVisible(path
))) {
693 eachParent
= eachParent
.getParent();
699 private int getDistanceToAutoExpandRoot(Object element
) {
702 Object eachParent
= element
;
703 while (eachParent
!= null) {
704 if (myAutoExpandRoots
.contains(eachParent
)) break;
705 eachParent
= getTreeStructure().getParentElement(eachParent
);
709 return eachParent
!= null ? distance
: -1;
712 private boolean isAutoExpand(DefaultMutableTreeNode node
) {
713 return isAutoExpand(getDescriptorFrom(node
));
716 private AsyncResult
<Boolean
> update(final NodeDescriptor nodeDescriptor
, boolean now
) {
717 final AsyncResult
<Boolean
> result
= new AsyncResult
<Boolean
>();
719 if (now
|| isPassthroughMode()) {
720 return new AsyncResult
<Boolean
>().setDone(_update(nodeDescriptor
));
723 Object element
= getElementFromDescriptor(nodeDescriptor
);
724 boolean bgLoading
= getTreeStructure().isToBuildChildrenInBackground(element
);
726 boolean edt
= isEdt();
729 final Ref
<Boolean
> changes
= new Ref
<Boolean
>(false);
730 queueToBackground(new Runnable() {
732 changes
.set(_update(nodeDescriptor
));
736 result
.setDone(changes
.get());
741 result
.setDone(_update(nodeDescriptor
));
745 if (edt
|| !myWasEverShown
) {
746 result
.setDone(_update(nodeDescriptor
));
749 UIUtil
.invokeLaterIfNeeded(new Runnable() {
751 if (!validateReleaseRequested()) {
752 result
.setDone(_update(nodeDescriptor
));
755 result
.setRejected();
762 result
.doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
763 public void run(Boolean changes
) {
765 final long updateStamp
= nodeDescriptor
.getUpdateCount();
766 UIUtil
.invokeLaterIfNeeded(new Runnable() {
768 Object element
= nodeDescriptor
.getElement();
769 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
771 TreePath path
= getPathFor(node
);
772 if (path
!= null && myTree
.isVisible(path
)) {
773 updateNodeImageAndPosition(node
, false);
786 private boolean _update(NodeDescriptor nodeDescriptor
) {
787 nodeDescriptor
.setUpdateCount(nodeDescriptor
.getUpdateCount() + 1);
788 return getBuilder().updateNodeDescriptor(nodeDescriptor
);
791 private void assertIsDispatchThread() {
792 if (isPassthroughMode()) return;
794 if (isTreeShowing() && !isEdt()) {
795 LOG
.error("Must be in event-dispatch thread");
799 private boolean isEdt() {
800 return SwingUtilities
.isEventDispatchThread();
803 private boolean isTreeShowing() {
807 private void assertNotDispatchThread() {
808 if (isPassthroughMode()) return;
811 LOG
.error("Must not be in event-dispatch thread");
815 private void processDeferredActions() {
816 processDeferredActions(myDeferredSelections
);
817 processDeferredActions(myDeferredExpansions
);
820 private void processDeferredActions(Set
<Runnable
> actions
) {
821 final Runnable
[] runnables
= actions
.toArray(new Runnable
[actions
.size()]);
823 for (Runnable runnable
: runnables
) {
828 //todo: to make real callback
829 public ActionCallback
queueUpdate(Object element
) {
830 AbstractTreeUpdater updater
= getUpdater();
831 if (updater
== null) {
832 return new ActionCallback
.Rejected();
835 final ActionCallback result
= new ActionCallback();
836 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
838 addSubtreeToUpdate(node
);
841 addSubtreeToUpdate(getRootNode());
844 updater
.runAfterUpdate(new Runnable() {
852 public void doUpdateFromRoot() {
853 updateSubtree(getRootNode(), false);
856 public ActionCallback
doUpdateFromRootCB() {
857 final ActionCallback cb
= new ActionCallback();
858 getUpdater().runAfterUpdate(new Runnable() {
863 updateSubtree(getRootNode(), false);
867 public final void updateSubtree(DefaultMutableTreeNode node
, boolean canSmartExpand
) {
868 updateSubtree(new TreeUpdatePass(node
), canSmartExpand
);
871 public final void updateSubtree(TreeUpdatePass pass
, boolean canSmartExpand
) {
872 if (getUpdater() != null) {
873 getUpdater().addSubtreeToUpdate(pass
);
876 updateSubtreeNow(pass
, canSmartExpand
);
880 final void updateSubtreeNow(TreeUpdatePass pass
, boolean canSmartExpand
) {
881 maybeSetBusyAndScheduleWaiterForReady(true);
883 boolean consumed
= initRootNodeNowIfNeeded(pass
);
884 if (consumed
) return;
886 final DefaultMutableTreeNode node
= pass
.getNode();
888 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
890 setUpdaterState(new UpdaterTreeState(this)).beforeSubtreeUpdate();
892 boolean forceUpdate
= true;
893 TreePath path
= getPathFor(node
);
894 boolean invisible
= !myTree
.isExpanded(path
) && (path
.getParentPath() == null || !myTree
.isExpanded(path
.getParentPath()));
896 if (invisible
&& myUnbuiltNodes
.contains(node
)) {
900 updateNodeChildren(node
, pass
, null, false, canSmartExpand
, forceUpdate
, false);
903 private boolean isToBuildInBackground(NodeDescriptor descriptor
) {
904 return getTreeStructure().isToBuildChildrenInBackground(getBuilder().getTreeStructureElement(descriptor
));
908 UpdaterTreeState
setUpdaterState(UpdaterTreeState state
) {
909 final UpdaterTreeState oldState
= myUpdaterState
;
910 if (oldState
== null) {
911 myUpdaterState
= state
;
915 oldState
.addAll(state
);
920 protected void doUpdateNode(final DefaultMutableTreeNode node
) {
921 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
922 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
923 final Object prevElement
= getElementFromDescriptor(descriptor
);
924 if (prevElement
== null) return;
925 update(descriptor
, false).doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
926 public void run(Boolean changes
) {
927 if (!isValid(descriptor
)) {
928 if (isInStructure(prevElement
)) {
929 getUpdater().addSubtreeToUpdateByElement(getTreeStructure().getParentElement(prevElement
));
934 updateNodeImageAndPosition(node
, true);
940 public Object
getElementFromDescriptor(NodeDescriptor descriptor
) {
941 return getBuilder().getTreeStructureElement(descriptor
);
944 private void updateNodeChildren(final DefaultMutableTreeNode node
,
945 final TreeUpdatePass pass
,
946 @Nullable LoadedChildren loadedChildren
,
948 final boolean toSmartExpand
,
950 final boolean descriptorIsUpToDate
) {
952 getTreeStructure().commit();
955 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
956 if (descriptor
== null) {
957 removeLoading(node
, true);
961 final boolean wasExpanded
= myTree
.isExpanded(new TreePath(node
.getPath())) || isAutoExpand(node
);
962 final boolean wasLeaf
= node
.getChildCount() == 0;
965 boolean bgBuild
= isToBuildInBackground(descriptor
);
966 boolean notRequiredToUpdateChildren
= !forcedNow
&& !wasExpanded
;
968 if (notRequiredToUpdateChildren
&& forceUpdate
&& !wasExpanded
) {
969 boolean alwaysPlus
= getBuilder().isAlwaysShowPlus(descriptor
);
970 if (alwaysPlus
&& wasLeaf
) {
971 notRequiredToUpdateChildren
= false;
973 notRequiredToUpdateChildren
= alwaysPlus
;
977 final Ref
<LoadedChildren
> preloaded
= new Ref
<LoadedChildren
>(loadedChildren
);
978 boolean descriptorWasUpdated
= descriptorIsUpToDate
;
980 if (notRequiredToUpdateChildren
) {
981 if (myUnbuiltNodes
.contains(node
) && node
.getChildCount() == 0) {
982 insertLoadingNode(node
, true);
989 if (myUnbuiltNodes
.contains(node
)) {
990 if (!descriptorWasUpdated
) {
991 update(descriptor
, true);
992 descriptorWasUpdated
= true;
995 if (processAlwaysLeaf(node
)) return;
997 Pair
<Boolean
, LoadedChildren
> unbuilt
= processUnbuilt(node
, descriptor
, pass
, wasExpanded
, null);
998 if (unbuilt
.getFirst()) return;
999 preloaded
.set(unbuilt
.getSecond());
1005 final boolean childForceUpdate
= isChildNodeForceUpdate(node
, forceUpdate
, wasExpanded
);
1007 if (!forcedNow
&& isToBuildInBackground(descriptor
)) {
1008 if (processAlwaysLeaf(node
)) return;
1010 queueBackgroundUpdate(
1011 new UpdateInfo(descriptor
, pass
, canSmartExpand(node
, toSmartExpand
), wasExpanded
, childForceUpdate
, descriptorWasUpdated
), node
);
1015 if (!descriptorWasUpdated
) {
1016 update(descriptor
, false).doWhenDone(new Runnable() {
1018 if (processAlwaysLeaf(node
)) return;
1019 updateNodeChildrenNow(node
, pass
, preloaded
.get(), toSmartExpand
, wasExpanded
, wasLeaf
, childForceUpdate
);
1024 if (processAlwaysLeaf(node
)) return;
1026 updateNodeChildrenNow(node
, pass
, preloaded
.get(), toSmartExpand
, wasExpanded
, wasLeaf
, childForceUpdate
);
1031 processNodeActionsIfReady(node
);
1035 private boolean processAlwaysLeaf(DefaultMutableTreeNode node
) {
1036 Object element
= getElementFor(node
);
1037 NodeDescriptor desc
= getDescriptorFrom(node
);
1039 if (desc
== null) return false;
1041 if (getTreeStructure().isAlwaysLeaf(element
)) {
1042 removeLoading(node
, true);
1044 if (node
.getChildCount() > 0) {
1045 final TreeNode
[] children
= new TreeNode
[node
.getChildCount()];
1046 for (int i
= 0; i
< node
.getChildCount(); i
++) {
1047 children
[i
] = node
.getChildAt(i
);
1050 if (isSelectionInside(node
)) {
1051 addSelectionPath(getPathFor(node
), true, Condition
.TRUE
, null);
1054 processInnerChange(new Runnable() {
1056 for (TreeNode each
: children
) {
1057 removeNodeFromParent((MutableTreeNode
)each
, true);
1058 disposeNode((DefaultMutableTreeNode
)each
);
1064 removeFromUnbuilt(node
);
1065 desc
.setWasDeclaredAlwaysLeaf(true);
1066 processNodeActionsIfReady(node
);
1069 boolean wasLeaf
= desc
.isWasDeclaredAlwaysLeaf();
1070 desc
.setWasDeclaredAlwaysLeaf(false);
1073 insertLoadingNode(node
, true);
1080 private boolean isChildNodeForceUpdate(DefaultMutableTreeNode node
, boolean parentForceUpdate
, boolean parentExpanded
) {
1081 TreePath path
= getPathFor(node
);
1082 return parentForceUpdate
&& (parentExpanded
|| myTree
.isExpanded(path
));
1085 private void updateNodeChildrenNow(final DefaultMutableTreeNode node
,
1086 final TreeUpdatePass pass
,
1087 final LoadedChildren preloadedChildren
,
1088 final boolean toSmartExpand
,
1089 final boolean wasExpanded
,
1090 final boolean wasLeaf
,
1091 final boolean forceUpdate
) {
1092 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
1094 final MutualMap
<Object
, Integer
> elementToIndexMap
= loadElementsFromStructure(descriptor
, preloadedChildren
);
1095 final LoadedChildren loadedChildren
=
1096 preloadedChildren
!= null ? preloadedChildren
: new LoadedChildren(elementToIndexMap
.getKeys().toArray());
1099 addToUpdating(node
);
1100 pass
.setCurrentNode(node
);
1102 final boolean canSmartExpand
= canSmartExpand(node
, toSmartExpand
);
1104 processExistingNodes(node
, elementToIndexMap
, pass
, canSmartExpand(node
, toSmartExpand
), forceUpdate
, wasExpanded
, preloadedChildren
)
1105 .doWhenDone(new Runnable() {
1107 if (isDisposed(node
)) {
1108 removeFromUpdating(node
);
1112 removeLoading(node
, false);
1114 final boolean expanded
= isExpanded(node
, wasExpanded
);
1117 myWillBeExpaned
.add(node
);
1119 myWillBeExpaned
.remove(node
);
1122 collectNodesToInsert(descriptor
, elementToIndexMap
, node
, expanded
, loadedChildren
)
1123 .doWhenDone(new AsyncResult
.Handler
<ArrayList
<TreeNode
>>() {
1124 public void run(ArrayList
<TreeNode
> nodesToInsert
) {
1125 insertNodesInto(nodesToInsert
, node
);
1126 updateNodesToInsert(nodesToInsert
, pass
, canSmartExpand
, isChildNodeForceUpdate(node
, forceUpdate
, expanded
));
1127 removeLoading(node
, true);
1128 removeFromUpdating(node
);
1130 if (node
.getChildCount() > 0) {
1132 expand(node
, canSmartExpand
);
1136 final Object element
= getElementFor(node
);
1137 addNodeAction(element
, new NodeAction() {
1138 public void onReady(final DefaultMutableTreeNode node
) {
1139 removeLoading(node
, false);
1143 processNodeActionsIfReady(node
);
1145 }).doWhenProcessed(new Runnable() {
1147 myWillBeExpaned
.remove(node
);
1148 removeFromUpdating(node
);
1149 processNodeActionsIfReady(node
);
1153 }).doWhenRejected(new Runnable() {
1155 removeFromUpdating(node
);
1156 processNodeActionsIfReady(node
);
1161 private boolean isDisposed(DefaultMutableTreeNode node
) {
1162 return !node
.isNodeAncestor((DefaultMutableTreeNode
)myTree
.getModel().getRoot());
1165 private void expand(DefaultMutableTreeNode node
, boolean canSmartExpand
) {
1166 expand(new TreePath(node
.getPath()), canSmartExpand
);
1169 private void expand(final TreePath path
, boolean canSmartExpand
) {
1170 if (path
== null) return;
1173 final Object last
= path
.getLastPathComponent();
1174 boolean isLeaf
= myTree
.getModel().isLeaf(path
.getLastPathComponent());
1175 final boolean isRoot
= last
== myTree
.getModel().getRoot();
1176 final TreePath parent
= path
.getParentPath();
1177 if (isRoot
&& !myTree
.isExpanded(path
)) {
1178 if (myTree
.isRootVisible() || myUnbuiltNodes
.contains(last
)) {
1179 insertLoadingNode((DefaultMutableTreeNode
)last
, false);
1181 expandPath(path
, canSmartExpand
);
1183 else if (myTree
.isExpanded(path
) || (isLeaf
&& parent
!= null && myTree
.isExpanded(parent
) && !myUnbuiltNodes
.contains(last
))) {
1184 if (last
instanceof DefaultMutableTreeNode
) {
1185 processNodeActionsIfReady((DefaultMutableTreeNode
)last
);
1189 if (isLeaf
&& myUnbuiltNodes
.contains(last
)) {
1190 insertLoadingNode((DefaultMutableTreeNode
)last
, true);
1191 expandPath(path
, canSmartExpand
);
1193 else if (isLeaf
&& parent
!= null) {
1194 final DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)parent
.getLastPathComponent();
1195 if (parentNode
!= null) {
1196 addToUnbuilt(parentNode
);
1198 expandPath(parent
, canSmartExpand
);
1201 expandPath(path
, canSmartExpand
);
1206 private void addToUnbuilt(DefaultMutableTreeNode node
) {
1207 myUnbuiltNodes
.add(node
);
1210 private void removeFromUnbuilt(DefaultMutableTreeNode node
) {
1211 myUnbuiltNodes
.remove(node
);
1214 private Pair
<Boolean
, LoadedChildren
> processUnbuilt(final DefaultMutableTreeNode node
,
1215 final NodeDescriptor descriptor
,
1216 final TreeUpdatePass pass
,
1218 final LoadedChildren loadedChildren
) {
1219 if (!isExpanded
&& getBuilder().isAlwaysShowPlus(descriptor
)) {
1220 return new Pair
<Boolean
, LoadedChildren
>(true, null);
1223 final Object element
= getElementFor(node
);
1225 final LoadedChildren children
= loadedChildren
!= null ? loadedChildren
: new LoadedChildren(getChildrenFor(element
));
1229 if (children
.getElements().size() == 0) {
1230 removeLoading(node
, true);
1234 if (isAutoExpand(node
)) {
1235 addNodeAction(getElementFor(node
), new NodeAction() {
1236 public void onReady(final DefaultMutableTreeNode node
) {
1237 final TreePath path
= new TreePath(node
.getPath());
1238 if (getTree().isExpanded(path
) || children
.getElements().size() == 0) {
1239 removeLoading(node
, false);
1242 maybeYeild(new ActiveRunnable() {
1243 public ActionCallback
run() {
1244 expand(element
, null);
1245 return new ActionCallback
.Done();
1255 processNodeActionsIfReady(node
);
1257 return new Pair
<Boolean
, LoadedChildren
>(processed
, children
);
1260 private boolean removeIfLoading(TreeNode node
) {
1261 if (isLoadingNode(node
)) {
1262 moveSelectionToParentIfNeeded(node
);
1263 removeNodeFromParent((MutableTreeNode
)node
, false);
1270 private void moveSelectionToParentIfNeeded(TreeNode node
) {
1271 TreePath path
= getPathFor(node
);
1272 if (myTree
.getSelectionModel().isPathSelected(path
)) {
1273 TreePath parentPath
= path
.getParentPath();
1274 myTree
.getSelectionModel().removeSelectionPath(path
);
1275 if (parentPath
!= null) {
1276 myTree
.getSelectionModel().addSelectionPath(parentPath
);
1281 //todo [kirillk] temporary consistency check
1282 private Object
[] getChildrenFor(final Object element
) {
1283 final Object
[] passOne
;
1285 passOne
= getTreeStructure().getChildElements(element
);
1287 catch (IndexNotReadyException e
) {
1288 if (!myWasEverIndexNotReady
) {
1289 myWasEverIndexNotReady
= true;
1290 LOG
.warn("Tree is not dumb-mode-aware; treeBuilder=" + getBuilder() + " treeStructure=" + getTreeStructure());
1292 return ArrayUtil
.EMPTY_OBJECT_ARRAY
;
1295 if (!myCheckStructure
) return passOne
;
1297 final Object
[] passTwo
= getTreeStructure().getChildElements(element
);
1299 final HashSet two
= new HashSet(Arrays
.asList(passTwo
));
1301 if (passOne
.length
!= passTwo
.length
) {
1303 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
1307 for (Object eachInOne
: passOne
) {
1308 if (!two
.contains(eachInOne
)) {
1310 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
1320 private void updateNodesToInsert(final ArrayList
<TreeNode
> nodesToInsert
,
1321 TreeUpdatePass pass
,
1322 boolean canSmartExpand
,
1323 boolean forceUpdate
) {
1324 for (TreeNode aNodesToInsert
: nodesToInsert
) {
1325 DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)aNodesToInsert
;
1326 updateNodeChildren(childNode
, pass
, null, false, canSmartExpand
, forceUpdate
, true);
1330 private ActionCallback
processExistingNodes(final DefaultMutableTreeNode node
,
1331 final MutualMap
<Object
, Integer
> elementToIndexMap
,
1332 final TreeUpdatePass pass
,
1333 final boolean canSmartExpand
,
1334 final boolean forceUpdate
,
1335 final boolean wasExpaned
,
1336 final LoadedChildren preloaded
) {
1338 final ArrayList
<TreeNode
> childNodes
= TreeUtil
.childrenToArray(node
);
1339 return maybeYeild(new ActiveRunnable() {
1340 public ActionCallback
run() {
1341 if (pass
.isExpired()) return new ActionCallback
.Rejected();
1342 if (childNodes
.size() == 0) return new ActionCallback
.Done();
1345 final ActionCallback result
= new ActionCallback(childNodes
.size());
1347 for (TreeNode each
: childNodes
) {
1348 final DefaultMutableTreeNode eachChild
= (DefaultMutableTreeNode
)each
;
1349 if (isLoadingNode(eachChild
)) {
1354 final boolean childForceUpdate
= isChildNodeForceUpdate(eachChild
, forceUpdate
, wasExpaned
);
1356 maybeYeild(new ActiveRunnable() {
1358 public ActionCallback
run() {
1359 return processExistingNode(eachChild
, getDescriptorFrom(eachChild
), node
, elementToIndexMap
, pass
, canSmartExpand
,
1360 childForceUpdate
, preloaded
);
1362 }, pass
, node
).notify(result
);
1364 if (result
.isRejected()) {
1374 private boolean isRerunNeeded(TreeUpdatePass pass
) {
1375 if (pass
.isExpired()) return false;
1377 final boolean rerunBecauseTreeIsHidden
= !pass
.isExpired() && !isTreeShowing() && getUpdater().isInPostponeMode();
1379 return rerunBecauseTreeIsHidden
|| getUpdater().isRerunNeededFor(pass
);
1382 private ActionCallback
maybeYeild(final ActiveRunnable processRunnable
, final TreeUpdatePass pass
, final DefaultMutableTreeNode node
) {
1383 final ActionCallback result
= new ActionCallback();
1385 if (isRerunNeeded(pass
)) {
1386 getUpdater().addSubtreeToUpdate(pass
);
1387 result
.setRejected();
1390 if (isToYieldUpdateFor(node
)) {
1391 pass
.setCurrentNode(node
);
1392 boolean wasRun
= yieldAndRun(new Runnable() {
1394 if (validateReleaseRequested()) {
1395 result
.setRejected();
1399 if (pass
.isExpired()) {
1400 result
.setRejected();
1404 if (isRerunNeeded(pass
)) {
1405 runDone(new Runnable() {
1407 if (!pass
.isExpired()) {
1408 getUpdater().addSubtreeToUpdate(pass
);
1412 result
.setRejected();
1416 processRunnable
.run().notify(result
);
1418 catch (ProcessCanceledException e
) {
1420 result
.setRejected();
1426 result
.setRejected();
1431 processRunnable
.run().notify(result
);
1433 catch (ProcessCanceledException e
) {
1435 result
.setRejected();
1443 private boolean yieldAndRun(final Runnable runnable
, final TreeUpdatePass pass
) {
1444 if (validateReleaseRequested()) return false;
1446 myYeildingPasses
.add(pass
);
1447 myYeildingNow
= true;
1448 yield(new Runnable() {
1450 runOnYieldingDone(new Runnable() {
1452 executeYieldingRequest(runnable
, pass
);
1461 public boolean isYeildingNow() {
1462 return myYeildingNow
;
1465 private boolean hasSheduledUpdates() {
1466 return getUpdater().hasNodesToUpdate() || isLoadingInBackgroundNow();
1469 public boolean isReady() {
1470 return isIdle() && !hasPendingWork() && !isNodeActionsPending();
1473 public boolean hasPendingWork() {
1474 return hasNodesToUpdate() || (myUpdaterState
!= null && myUpdaterState
.isProcessingNow());
1477 public boolean isIdle() {
1478 return !isYeildingNow() && !isWorkerBusy() && (!hasSheduledUpdates() || getUpdater().isInPostponeMode());
1481 private void executeYieldingRequest(Runnable runnable
, TreeUpdatePass pass
) {
1483 myYeildingPasses
.remove(pass
);
1487 maybeYeildingFinished();
1491 private void maybeYeildingFinished() {
1492 if (myYeildingPasses
.size() == 0) {
1493 myYeildingNow
= false;
1494 flushPendingNodeActions();
1499 if (isReleased()) return;
1502 if (isReleaseRequested()) {
1507 if (myTree
.isShowing() || myUpdateIfInactive
) {
1508 myInitialized
.setDone();
1512 if (myUpdaterState
!= null && !myUpdaterState
.isProcessingNow()) {
1513 UpdaterTreeState oldState
= myUpdaterState
;
1514 if (!myUpdaterState
.restore(null)) {
1515 setUpdaterState(oldState
);
1523 if (myTree
.isShowing()) {
1524 if (getBuilder().isToEnsureSelectionOnFocusGained() && Registry
.is("ide.tree.ensureSelectionOnFocusGained")) {
1525 TreeUtil
.ensureSelection(myTree
);
1529 if (myInitialized
.isDone()) {
1530 for (ActionCallback each
: getReadyCallbacks(true)) {
1537 private void flushPendingNodeActions() {
1538 final DefaultMutableTreeNode
[] nodes
= myPendingNodeActions
.toArray(new DefaultMutableTreeNode
[myPendingNodeActions
.size()]);
1539 myPendingNodeActions
.clear();
1541 for (DefaultMutableTreeNode each
: nodes
) {
1542 processNodeActionsIfReady(each
);
1545 final Runnable
[] actions
= myYeildingDoneRunnables
.toArray(new Runnable
[myYeildingDoneRunnables
.size()]);
1546 for (Runnable each
: actions
) {
1547 if (!isYeildingNow()) {
1548 myYeildingDoneRunnables
.remove(each
);
1556 protected void runOnYieldingDone(Runnable onDone
) {
1557 getBuilder().runOnYeildingDone(onDone
);
1560 protected void yield(Runnable runnable
) {
1561 getBuilder().yield(runnable
);
1564 private boolean isToYieldUpdateFor(final DefaultMutableTreeNode node
) {
1565 if (!canYield()) return false;
1566 return getBuilder().isToYieldUpdateFor(node
);
1569 private MutualMap
<Object
, Integer
> loadElementsFromStructure(final NodeDescriptor descriptor
,
1570 @Nullable LoadedChildren preloadedChildren
) {
1571 MutualMap
<Object
, Integer
> elementToIndexMap
= new MutualMap
<Object
, Integer
>(true);
1572 List children
= preloadedChildren
!= null
1573 ? preloadedChildren
.getElements()
1574 : Arrays
.asList(getChildrenFor(getBuilder().getTreeStructureElement(descriptor
)));
1576 for (Object child
: children
) {
1577 if (!isValid(child
)) continue;
1578 elementToIndexMap
.put(child
, Integer
.valueOf(index
));
1581 return elementToIndexMap
;
1584 private void expand(final DefaultMutableTreeNode node
,
1585 final NodeDescriptor descriptor
,
1586 final boolean wasLeaf
,
1587 final boolean canSmartExpand
) {
1588 final Alarm alarm
= new Alarm(Alarm
.ThreadToUse
.SHARED_THREAD
);
1589 alarm
.addRequest(new Runnable() {
1591 myTree
.setCursor(Cursor
.getPredefinedCursor(Cursor
.WAIT_CURSOR
));
1593 }, WAIT_CURSOR_DELAY
);
1595 if (wasLeaf
&& isAutoExpand(descriptor
)) {
1596 expand(node
, canSmartExpand
);
1599 ArrayList
<TreeNode
> nodes
= TreeUtil
.childrenToArray(node
);
1600 for (TreeNode node1
: nodes
) {
1601 final DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)node1
;
1602 if (isLoadingNode(childNode
)) continue;
1603 NodeDescriptor childDescr
= getDescriptorFrom(childNode
);
1604 if (isAutoExpand(childDescr
)) {
1605 addNodeAction(getElementFor(childNode
), new NodeAction() {
1606 public void onReady(DefaultMutableTreeNode node
) {
1607 expand(childNode
, canSmartExpand
);
1610 addSubtreeToUpdate(childNode
);
1614 int n
= alarm
.cancelAllRequests();
1616 myTree
.setCursor(Cursor
.getDefaultCursor());
1620 public static boolean isLoadingNode(final Object node
) {
1621 return node
instanceof LoadingNode
;
1624 private AsyncResult
<ArrayList
<TreeNode
>> collectNodesToInsert(final NodeDescriptor descriptor
,
1625 final MutualMap
<Object
, Integer
> elementToIndexMap
,
1626 final DefaultMutableTreeNode parent
,
1627 final boolean addLoadingNode
,
1628 @NotNull final LoadedChildren loadedChildren
) {
1629 final AsyncResult
<ArrayList
<TreeNode
>> result
= new AsyncResult
<ArrayList
<TreeNode
>>();
1631 final ArrayList
<TreeNode
> nodesToInsert
= new ArrayList
<TreeNode
>();
1632 final Collection
<Object
> allElements
= elementToIndexMap
.getKeys();
1634 final ActionCallback processingDone
= new ActionCallback(allElements
.size());
1636 for (final Object child
: allElements
) {
1637 Integer index
= elementToIndexMap
.getValue(child
);
1638 final Ref
<NodeDescriptor
> childDescr
= new Ref
<NodeDescriptor
>(loadedChildren
.getDescriptor(child
));
1639 boolean needToUpdate
= false;
1640 if (childDescr
.get() == null) {
1641 childDescr
.set(getTreeStructure().createDescriptor(child
, descriptor
));
1642 needToUpdate
= true;
1645 //noinspection ConstantConditions
1646 if (childDescr
.get() == null) {
1647 LOG
.error("childDescr == null, treeStructure = " + getTreeStructure() + ", child = " + child
);
1648 processingDone
.setDone();
1651 childDescr
.get().setIndex(index
.intValue());
1653 final ActionCallback update
= new ActionCallback();
1655 update(childDescr
.get(), false).doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
1656 public void run(Boolean changes
) {
1657 loadedChildren
.putDescriptor(child
, childDescr
.get(), changes
);
1666 update
.doWhenDone(new Runnable() {
1668 Object element
= getElementFromDescriptor(childDescr
.get());
1669 if (element
== null) {
1670 processingDone
.setDone();
1673 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
1674 if (node
== null || node
.getParent() != parent
) {
1675 final DefaultMutableTreeNode childNode
= createChildNode(childDescr
.get());
1676 if (addLoadingNode
|| getBuilder().isAlwaysShowPlus(childDescr
.get())) {
1677 insertLoadingNode(childNode
, true);
1680 addToUnbuilt(childNode
);
1682 nodesToInsert
.add(childNode
);
1683 createMapping(element
, childNode
);
1685 processingDone
.setDone();
1691 processingDone
.doWhenDone(new Runnable() {
1693 result
.setDone(nodesToInsert
);
1700 protected DefaultMutableTreeNode
createChildNode(final NodeDescriptor descriptor
) {
1701 return new ElementNode(this, descriptor
);
1704 protected boolean canYield() {
1705 return myCanYield
&& myYeildingUpdate
.asBoolean();
1708 public long getClearOnHideDelay() {
1709 return myClearOnHideDelay
> 0 ? myClearOnHideDelay
: Registry
.intValue("ide.tree.clearOnHideTime");
1712 public ActionCallback
getInitialized() {
1713 return myInitialized
;
1716 public ActionCallback
getReady(Object requestor
) {
1718 return new ActionCallback
.Done();
1721 return addReadyCallback(requestor
);
1725 private void addToUpdating(DefaultMutableTreeNode node
) {
1726 synchronized (myUpdatingChildren
) {
1727 myUpdatingChildren
.add(node
);
1731 private void removeFromUpdating(DefaultMutableTreeNode node
) {
1732 synchronized (myUpdatingChildren
) {
1733 myUpdatingChildren
.remove(node
);
1737 public boolean isUpdatingNow(DefaultMutableTreeNode node
) {
1738 synchronized (myUpdatingChildren
) {
1739 return myUpdatingChildren
.contains(node
);
1743 boolean hasUpdatingNow() {
1744 synchronized (myUpdatingChildren
) {
1745 return myUpdatingChildren
.size() > 0;
1749 public Map
getNodeActions() {
1750 return myNodeActions
;
1753 public List
<Object
> getLoadedChildrenFor(Object element
) {
1754 List
<Object
> result
= new ArrayList
<Object
>();
1756 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)getNodeForElement(element
, false);
1758 for (int i
= 0; i
< node
.getChildCount(); i
++) {
1759 TreeNode each
= node
.getChildAt(i
);
1760 if (isLoadingNode(each
)) continue;
1762 result
.add(getElementFor(each
));
1769 public boolean hasNodesToUpdate() {
1770 return getUpdater().hasNodesToUpdate() || hasUpdatingNow() || isLoadingInBackgroundNow();
1773 public List
<Object
> getExpandedElements() {
1774 List
<Object
> result
= new ArrayList
<Object
>();
1775 Enumeration
<TreePath
> enumeration
= myTree
.getExpandedDescendants(getPathFor(getRootNode()));
1776 while (enumeration
.hasMoreElements()) {
1777 TreePath each
= enumeration
.nextElement();
1778 Object eachElement
= getElementFor(each
.getLastPathComponent());
1779 if (eachElement
!= null) {
1780 result
.add(eachElement
);
1787 static class ElementNode
extends DefaultMutableTreeNode
{
1789 Set
<Object
> myElements
= new HashSet
<Object
>();
1790 AbstractTreeUi myUi
;
1792 ElementNode(AbstractTreeUi ui
, NodeDescriptor descriptor
) {
1798 public void insert(final MutableTreeNode newChild
, final int childIndex
) {
1799 super.insert(newChild
, childIndex
);
1800 final Object element
= myUi
.getElementFor(newChild
);
1801 if (element
!= null) {
1802 myElements
.add(element
);
1807 public void remove(final int childIndex
) {
1808 final TreeNode node
= getChildAt(childIndex
);
1809 super.remove(childIndex
);
1810 final Object element
= myUi
.getElementFor(node
);
1811 if (element
!= null) {
1812 myElements
.remove(element
);
1816 boolean isValidChild(Object childElement
) {
1817 return myElements
.contains(childElement
);
1821 public String
toString() {
1822 return String
.valueOf(getUserObject());
1826 private boolean isUpdatingParent(DefaultMutableTreeNode kid
) {
1827 return getUpdatingParent(kid
) != null;
1830 private DefaultMutableTreeNode
getUpdatingParent(DefaultMutableTreeNode kid
) {
1831 DefaultMutableTreeNode eachParent
= kid
;
1832 while (eachParent
!= null) {
1833 if (isUpdatingNow(eachParent
)) return eachParent
;
1834 eachParent
= (DefaultMutableTreeNode
)eachParent
.getParent();
1840 private boolean isLoadedInBackground(Object element
) {
1841 return getLoadedInBackground(element
) != null;
1844 private UpdateInfo
getLoadedInBackground(Object element
) {
1845 synchronized (myLoadedInBackground
) {
1846 return myLoadedInBackground
.get(element
);
1850 private void addToLoadedInBackground(Object element
, UpdateInfo info
) {
1851 synchronized (myLoadedInBackground
) {
1852 myLoadedInBackground
.put(element
, info
);
1856 private void removeFromLoadedInBackground(final Object element
) {
1857 synchronized (myLoadedInBackground
) {
1858 myLoadedInBackground
.remove(element
);
1862 private boolean isLoadingInBackgroundNow() {
1863 synchronized (myLoadedInBackground
) {
1864 return myLoadedInBackground
.size() > 0;
1868 private boolean queueBackgroundUpdate(final UpdateInfo updateInfo
, final DefaultMutableTreeNode node
) {
1869 assertIsDispatchThread();
1871 if (validateReleaseRequested()) return false;
1873 final Object oldElementFromDescriptor
= getElementFromDescriptor(updateInfo
.getDescriptor());
1875 UpdateInfo loaded
= getLoadedInBackground(oldElementFromDescriptor
);
1876 if (loaded
!= null) {
1877 loaded
.apply(updateInfo
);
1881 addToLoadedInBackground(oldElementFromDescriptor
, updateInfo
);
1883 if (!isNodeBeingBuilt(node
)) {
1884 LoadingNode loadingNode
= new LoadingNode(getLoadingNodeText());
1885 myTreeModel
.insertNodeInto(loadingNode
, node
, node
.getChildCount());
1888 final Ref
<LoadedChildren
> children
= new Ref
<LoadedChildren
>();
1889 final Ref
<Object
> elementFromDescriptor
= new Ref
<Object
>();
1891 final DefaultMutableTreeNode
[] nodeToProcessActions
= new DefaultMutableTreeNode
[1];
1893 final Runnable finalizeRunnable
= new Runnable() {
1895 removeLoading(node
, true);
1896 removeFromLoadedInBackground(elementFromDescriptor
.get());
1897 removeFromLoadedInBackground(oldElementFromDescriptor
);
1899 if (nodeToProcessActions
[0] != null) {
1900 processNodeActionsIfReady(nodeToProcessActions
[0]);
1906 Runnable buildRunnable
= new Runnable() {
1908 if (updateInfo
.getPass().isExpired()) {
1909 finalizeRunnable
.run();
1914 if (!updateInfo
.isDescriptorIsUpToDate()) {
1915 update(updateInfo
.getDescriptor(), true);
1918 Object element
= getElementFromDescriptor(updateInfo
.getDescriptor());
1919 if (element
== null) {
1920 removeFromLoadedInBackground(oldElementFromDescriptor
);
1921 finalizeRunnable
.run();
1925 elementFromDescriptor
.set(element
);
1927 Object
[] loadedElements
= getChildrenFor(getBuilder().getTreeStructureElement(updateInfo
.getDescriptor()));
1928 LoadedChildren loaded
= new LoadedChildren(loadedElements
);
1929 for (Object each
: loadedElements
) {
1930 NodeDescriptor eachChildDescriptor
= getTreeStructure().createDescriptor(each
, updateInfo
.getDescriptor());
1931 loaded
.putDescriptor(each
, eachChildDescriptor
, update(eachChildDescriptor
, true).getResult());
1934 children
.set(loaded
);
1938 Runnable updateRunnable
= new Runnable() {
1940 if (updateInfo
.getPass().isExpired()) {
1941 finalizeRunnable
.run();
1945 if (children
.get() == null) {
1946 finalizeRunnable
.run();
1950 if (isRerunNeeded(updateInfo
.getPass())) {
1951 removeFromLoadedInBackground(elementFromDescriptor
.get());
1952 getUpdater().addSubtreeToUpdate(updateInfo
.getPass());
1956 removeFromLoadedInBackground(elementFromDescriptor
.get());
1958 if (myUnbuiltNodes
.contains(node
)) {
1959 Pair
<Boolean
, LoadedChildren
> unbuilt
=
1960 processUnbuilt(node
, updateInfo
.getDescriptor(), updateInfo
.getPass(), isExpanded(node
, updateInfo
.isWasExpanded()),
1962 if (unbuilt
.getFirst()) {
1963 nodeToProcessActions
[0] = node
;
1968 updateNodeChildren(node
, updateInfo
.getPass(), children
.get(), true, updateInfo
.isCanSmartExpand(), updateInfo
.isForceUpdate(),
1972 if (isRerunNeeded(updateInfo
.getPass())) {
1973 getUpdater().addSubtreeToUpdate(updateInfo
.getPass());
1977 Object element
= elementFromDescriptor
.get();
1979 if (element
!= null) {
1980 removeLoading(node
, true);
1981 nodeToProcessActions
[0] = node
;
1985 queueToBackground(buildRunnable
, updateRunnable
).doWhenProcessed(finalizeRunnable
).doWhenRejected(new Runnable() {
1987 updateInfo
.getPass().expire();
1994 private boolean isExpanded(DefaultMutableTreeNode node
, boolean isExpanded
) {
1995 return isExpanded
|| myTree
.isExpanded(getPathFor(node
));
1998 private void removeLoading(DefaultMutableTreeNode parent
, boolean removeFromUnbuilt
) {
1999 for (int i
= 0; i
< parent
.getChildCount(); i
++) {
2000 TreeNode child
= parent
.getChildAt(i
);
2001 if (removeIfLoading(child
)) {
2006 if (removeFromUnbuilt
) {
2007 removeFromUnbuilt(parent
);
2010 if (parent
== getRootNode() && !myTree
.isRootVisible() && parent
.getChildCount() == 0) {
2011 insertLoadingNode(parent
, false);
2017 private void processNodeActionsIfReady(final DefaultMutableTreeNode node
) {
2018 assertIsDispatchThread();
2020 if (isNodeBeingBuilt(node
)) return;
2022 final Object o
= node
.getUserObject();
2023 if (!(o
instanceof NodeDescriptor
)) return;
2026 if (isYeildingNow()) {
2027 myPendingNodeActions
.add(node
);
2031 final Object element
= getBuilder().getTreeStructureElement((NodeDescriptor
)o
);
2033 boolean childrenReady
= !isLoadedInBackground(element
);
2035 processActions(node
, element
, myNodeActions
, childrenReady ? myNodeChildrenActions
: null);
2036 if (childrenReady
) {
2037 processActions(node
, element
, myNodeChildrenActions
, null);
2040 if (!isUpdatingParent(node
) && !isWorkerBusy()) {
2041 final UpdaterTreeState state
= myUpdaterState
;
2042 if (myNodeActions
.size() == 0 && state
!= null && !state
.isProcessingNow()) {
2043 if (!state
.restore(childrenReady ? node
: null)) {
2044 setUpdaterState(state
);
2053 private void processActions(DefaultMutableTreeNode node
, Object element
, final Map
<Object
, List
<NodeAction
>> nodeActions
, @Nullable final Map
<Object
, List
<NodeAction
>> secondaryNodeAction
) {
2054 final List
<NodeAction
> actions
= nodeActions
.get(element
);
2055 if (actions
!= null) {
2056 nodeActions
.remove(element
);
2058 List
<NodeAction
> secondary
= secondaryNodeAction
!= null ? secondaryNodeAction
.get(element
) : null;
2059 for (NodeAction each
: actions
) {
2060 if (secondary
!= null && secondary
.contains(each
)) {
2061 secondary
.remove(each
);
2069 private boolean canSmartExpand(DefaultMutableTreeNode node
, boolean canSmartExpand
) {
2070 if (!getBuilder().isSmartExpand()) return false;
2072 boolean smartExpand
= !myNotForSmartExpand
.contains(node
) && canSmartExpand
;
2073 return smartExpand ?
validateAutoExpand(smartExpand
, getElementFor(node
)) : false;
2076 private void processSmartExpand(final DefaultMutableTreeNode node
, final boolean canSmartExpand
, boolean forced
) {
2077 if (!getBuilder().isSmartExpand()) return;
2079 boolean can
= canSmartExpand(node
, canSmartExpand
);
2081 if (!can
&& !forced
) return;
2083 if (isNodeBeingBuilt(node
) && !forced
) {
2084 addNodeAction(getElementFor(node
), new NodeAction() {
2085 public void onReady(DefaultMutableTreeNode node
) {
2086 processSmartExpand(node
, canSmartExpand
, true);
2091 TreeNode child
= getChildForSmartExpand(node
);
2092 if (child
!= null) {
2093 final TreePath childPath
= new TreePath(node
.getPath()).pathByAddingChild(child
);
2094 processInnerChange(new Runnable() {
2096 myTree
.expandPath(childPath
);
2104 private TreeNode
getChildForSmartExpand(DefaultMutableTreeNode node
) {
2105 int realChildCount
= 0;
2106 TreeNode nodeToExpand
= null;
2108 for (int i
= 0; i
< node
.getChildCount(); i
++) {
2109 TreeNode eachChild
= node
.getChildAt(i
);
2111 if (!isLoadingNode(eachChild
)) {
2113 if (nodeToExpand
== null) {
2114 nodeToExpand
= eachChild
;
2118 if (realChildCount
> 1) {
2119 nodeToExpand
= null;
2124 return nodeToExpand
;
2127 public boolean isLoadingChildrenFor(final Object nodeObject
) {
2128 if (!(nodeObject
instanceof DefaultMutableTreeNode
)) return false;
2130 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)nodeObject
;
2132 int loadingNodes
= 0;
2133 for (int i
= 0; i
< Math
.min(node
.getChildCount(), 2); i
++) {
2134 TreeNode child
= node
.getChildAt(i
);
2135 if (isLoadingNode(child
)) {
2139 return loadingNodes
> 0 && loadingNodes
== node
.getChildCount();
2142 private boolean isParentLoading(Object nodeObject
) {
2143 return getParentLoading(nodeObject
) != null;
2146 private DefaultMutableTreeNode
getParentLoading(Object nodeObject
) {
2147 if (!(nodeObject
instanceof DefaultMutableTreeNode
)) return null;
2149 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)nodeObject
;
2151 TreeNode eachParent
= node
.getParent();
2153 while (eachParent
!= null) {
2154 eachParent
= eachParent
.getParent();
2155 if (eachParent
instanceof DefaultMutableTreeNode
) {
2156 final Object eachElement
= getElementFor((DefaultMutableTreeNode
)eachParent
);
2157 if (isLoadedInBackground(eachElement
)) return (DefaultMutableTreeNode
)eachParent
;
2164 protected String
getLoadingNodeText() {
2165 return IdeBundle
.message("progress.searching");
2168 private ActionCallback
processExistingNode(final DefaultMutableTreeNode childNode
,
2169 final NodeDescriptor childDescriptor
,
2170 final DefaultMutableTreeNode parentNode
,
2171 final MutualMap
<Object
, Integer
> elementToIndexMap
,
2172 final TreeUpdatePass pass
,
2173 final boolean canSmartExpand
,
2174 final boolean forceUpdate
,
2175 LoadedChildren parentPreloadedChildren
) {
2177 final ActionCallback result
= new ActionCallback();
2179 if (pass
.isExpired()) {
2180 return new ActionCallback
.Rejected();
2183 final Ref
<NodeDescriptor
> childDesc
= new Ref
<NodeDescriptor
>(childDescriptor
);
2185 if (childDesc
.get() == null) {
2187 return new ActionCallback
.Rejected();
2189 final Object oldElement
= getElementFromDescriptor(childDesc
.get());
2190 if (oldElement
== null) {
2192 return new ActionCallback
.Rejected();
2195 AsyncResult
<Boolean
> update
= new AsyncResult
<Boolean
>();
2196 if (parentPreloadedChildren
!= null && parentPreloadedChildren
.getDescriptor(oldElement
) != null) {
2197 update
.setDone(parentPreloadedChildren
.isUpdated(oldElement
));
2200 update
= update(childDesc
.get(), false);
2203 update
.doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
2204 public void run(Boolean isChanged
) {
2205 final Ref
<Boolean
> changes
= new Ref
<Boolean
>(isChanged
);
2207 final Ref
<Boolean
> forceRemapping
= new Ref
<Boolean
>(false);
2208 final Ref
<Object
> newElement
= new Ref
<Object
>(getElementFromDescriptor(childDesc
.get()));
2210 final Integer index
= newElement
.get() != null ? elementToIndexMap
.getValue(getBuilder().getTreeStructureElement(childDesc
.get())) : null;
2211 final AsyncResult
<Boolean
> updateIndexDone
= new AsyncResult
<Boolean
>();
2212 final ActionCallback indexReady
= new ActionCallback();
2213 if (index
!= null) {
2214 final Object elementFromMap
= elementToIndexMap
.getKey(index
);
2215 if (elementFromMap
!= newElement
.get() && elementFromMap
.equals(newElement
.get())) {
2216 if (isInStructure(elementFromMap
) && isInStructure(newElement
.get())) {
2217 if (parentNode
.getUserObject() instanceof NodeDescriptor
) {
2218 final NodeDescriptor parentDescriptor
= getDescriptorFrom(parentNode
);
2219 childDesc
.set(getTreeStructure().createDescriptor(elementFromMap
, parentDescriptor
));
2220 childNode
.setUserObject(childDesc
.get());
2221 newElement
.set(elementFromMap
);
2222 forceRemapping
.set(true);
2223 update(childDesc
.get(), false).doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
2224 public void run(Boolean isChanged
) {
2225 changes
.set(isChanged
);
2226 updateIndexDone
.setDone(isChanged
);
2232 updateIndexDone
.setDone(changes
.get());
2235 updateIndexDone
.setDone(changes
.get());
2238 updateIndexDone
.doWhenDone(new Runnable() {
2240 if (childDesc
.get().getIndex() != index
.intValue()) {
2243 childDesc
.get().setIndex(index
.intValue());
2244 indexReady
.setDone();
2249 updateIndexDone
.setDone();
2252 updateIndexDone
.doWhenDone(new Runnable() {
2254 if (index
!= null && changes
.get()) {
2255 updateNodeImageAndPosition(childNode
, false);
2257 if (!oldElement
.equals(newElement
.get()) | forceRemapping
.get()) {
2258 removeMapping(oldElement
, childNode
, newElement
.get());
2259 if (newElement
.get() != null) {
2260 createMapping(newElement
.get(), childNode
);
2262 NodeDescriptor parentDescriptor
= getDescriptorFrom(parentNode
);
2263 if (parentDescriptor
!= null) {
2264 parentDescriptor
.setChildrenSortingStamp(-1);
2268 if (index
== null) {
2269 int selectedIndex
= -1;
2270 if (TreeBuilderUtil
.isNodeOrChildSelected(myTree
, childNode
)) {
2271 selectedIndex
= parentNode
.getIndex(childNode
);
2274 if (childNode
.getParent() instanceof DefaultMutableTreeNode
) {
2275 final DefaultMutableTreeNode parent
= (DefaultMutableTreeNode
)childNode
.getParent();
2276 if (myTree
.isExpanded(new TreePath(parent
.getPath()))) {
2277 if (parent
.getChildCount() == 1 && parent
.getChildAt(0) == childNode
) {
2278 insertLoadingNode(parent
, false);
2283 Object disposedElement
= getElementFor(childNode
);
2285 removeNodeFromParent(childNode
, selectedIndex
>= 0);
2286 disposeNode(childNode
);
2288 adjustSelectionOnChildRemove(parentNode
, selectedIndex
, disposedElement
);
2291 elementToIndexMap
.remove(getBuilder().getTreeStructureElement(childDesc
.get()));
2292 updateNodeChildren(childNode
, pass
, null, false, canSmartExpand
, forceUpdate
, true);
2295 if (parentNode
.equals(getRootNode())) {
2296 myTreeModel
.nodeChanged(getRootNode());
2309 private void adjustSelectionOnChildRemove(DefaultMutableTreeNode parentNode
, int selectedIndex
, Object disposedElement
) {
2310 DefaultMutableTreeNode node
= getNodeForElement(disposedElement
, false);
2311 if (node
!= null && isValidForSelectionAdjusting(node
)) {
2312 Object newElement
= getElementFor(node
);
2313 addSelectionPath(getPathFor(node
), true, getExpiredElementCondition(newElement
), disposedElement
);
2318 if (selectedIndex
>= 0) {
2319 if (parentNode
.getChildCount() > 0) {
2320 if (parentNode
.getChildCount() > selectedIndex
) {
2321 TreeNode newChildNode
= parentNode
.getChildAt(selectedIndex
);
2322 if (isValidForSelectionAdjusting(newChildNode
)) {
2323 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(newChildNode
)), true, getExpiredElementCondition(disposedElement
), disposedElement
);
2327 TreeNode newChild
= parentNode
.getChildAt(parentNode
.getChildCount() - 1);
2328 if (isValidForSelectionAdjusting(newChild
)) {
2329 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(newChild
)), true, getExpiredElementCondition(disposedElement
), disposedElement
);
2334 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(parentNode
)), true, getExpiredElementCondition(disposedElement
), disposedElement
);
2339 private boolean isValidForSelectionAdjusting(TreeNode node
) {
2340 if (!myTree
.isRootVisible() && getRootNode() == node
) return false;
2342 if (isLoadingNode(node
)) return true;
2344 final Object elementInTree
= getElementFor(node
);
2345 if (elementInTree
== null) return false;
2347 final TreeNode parentNode
= node
.getParent();
2348 final Object parentElementInTree
= getElementFor(parentNode
);
2349 if (parentElementInTree
== null) return false;
2351 final Object parentElement
= getTreeStructure().getParentElement(elementInTree
);
2353 return parentElementInTree
.equals(parentElement
);
2356 public Condition
getExpiredElementCondition(final Object element
) {
2357 return new Condition() {
2358 public boolean value(final Object o
) {
2359 return isInStructure(element
);
2364 private void addSelectionPath(final TreePath path
, final boolean isAdjustedSelection
, final Condition isExpiredAdjustement
, @Nullable final Object adjustmentCause
) {
2365 processInnerChange(new Runnable() {
2367 TreePath toSelect
= null;
2369 if (isLoadingNode(path
.getLastPathComponent())) {
2370 final TreePath parentPath
= path
.getParentPath();
2371 if (parentPath
!= null) {
2372 if (isValidForSelectionAdjusting((TreeNode
)parentPath
.getLastPathComponent())) {
2373 toSelect
= parentPath
;
2384 if (toSelect
!= null) {
2385 myTree
.addSelectionPath(toSelect
);
2387 if (isAdjustedSelection
&& myUpdaterState
!= null) {
2388 final Object toSelectElement
= getElementFor(toSelect
.getLastPathComponent());
2389 myUpdaterState
.addAdjustedSelection(toSelectElement
, isExpiredAdjustement
, adjustmentCause
);
2396 private static TreePath
getPathFor(TreeNode node
) {
2397 if (node
instanceof DefaultMutableTreeNode
) {
2398 return new TreePath(((DefaultMutableTreeNode
)node
).getPath());
2401 ArrayList nodes
= new ArrayList();
2402 TreeNode eachParent
= node
;
2403 while (eachParent
!= null) {
2404 nodes
.add(eachParent
);
2405 eachParent
= eachParent
.getParent();
2408 return new TreePath(ArrayUtil
.toObjectArray(nodes
));
2413 private void removeNodeFromParent(final MutableTreeNode node
, final boolean willAdjustSelection
) {
2414 processInnerChange(new Runnable() {
2416 if (willAdjustSelection
) {
2417 final TreePath path
= getPathFor(node
);
2418 if (myTree
.isPathSelected(path
)) {
2419 myTree
.removeSelectionPath(path
);
2423 if (node
.getParent() != null) {
2424 myTreeModel
.removeNodeFromParent(node
);
2430 private void expandPath(final TreePath path
, final boolean canSmartExpand
) {
2431 processInnerChange(new Runnable() {
2433 if (path
.getLastPathComponent() instanceof DefaultMutableTreeNode
) {
2434 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
2435 if (node
.getChildCount() > 0 && !myTree
.isExpanded(path
)) {
2436 if (!canSmartExpand
) {
2437 myNotForSmartExpand
.add(node
);
2440 myRequestedExpand
= path
;
2441 myTree
.expandPath(path
);
2442 processSmartExpand(node
, canSmartExpand
, false);
2445 myNotForSmartExpand
.remove(node
);
2446 myRequestedExpand
= null;
2450 processNodeActionsIfReady(node
);
2457 private void processInnerChange(Runnable runnable
) {
2458 if (myUpdaterState
== null) {
2459 setUpdaterState(new UpdaterTreeState(this));
2462 myUpdaterState
.process(runnable
);
2465 private boolean isInnerChange() {
2466 return myUpdaterState
!= null && myUpdaterState
.isProcessingNow();
2469 protected boolean doUpdateNodeDescriptor(final NodeDescriptor descriptor
) {
2470 return descriptor
.update();
2473 private void makeLoadingOrLeafIfNoChildren(final DefaultMutableTreeNode node
) {
2474 TreePath path
= getPathFor(node
);
2475 if (path
== null) return;
2477 insertLoadingNode(node
, true);
2479 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
2480 if (descriptor
== null) return;
2482 descriptor
.setChildrenSortingStamp(-1);
2484 if (getBuilder().isAlwaysShowPlus(descriptor
)) return;
2487 TreePath parentPath
= path
.getParentPath();
2488 if (myTree
.isVisible(path
) || (parentPath
!= null && myTree
.isExpanded(parentPath
))) {
2489 if (myTree
.isExpanded(path
)) {
2490 addSubtreeToUpdate(node
);
2493 insertLoadingNode(node
, false);
2499 private boolean isValid(DefaultMutableTreeNode node
) {
2500 if (node
== null) return false;
2501 final Object object
= node
.getUserObject();
2502 if (object
instanceof NodeDescriptor
) {
2503 return isValid((NodeDescriptor
)object
);
2509 private boolean isValid(NodeDescriptor descriptor
) {
2510 if (descriptor
== null) return false;
2511 return isValid(getElementFromDescriptor(descriptor
));
2514 private boolean isValid(Object element
) {
2515 if (element
instanceof ValidateableNode
) {
2516 if (!((ValidateableNode
)element
).isValid()) return false;
2518 return getBuilder().validateNode(element
);
2521 private void insertLoadingNode(final DefaultMutableTreeNode node
, boolean addToUnbuilt
) {
2522 if (!isLoadingChildrenFor(node
)) {
2523 myTreeModel
.insertNodeInto(new LoadingNode(), node
, 0);
2532 protected ActionCallback
queueToBackground(@NotNull final Runnable bgBuildAction
,
2533 @Nullable final Runnable edtPostRunnable
) {
2534 if (validateReleaseRequested()) return new ActionCallback
.Rejected();
2536 final ActionCallback result
= new ActionCallback();
2538 final Ref
<Boolean
> fail
= new Ref
<Boolean
>(false);
2539 final Runnable finalizer
= new Runnable() {
2542 result
.setRejected();
2549 registerWorkerTask(bgBuildAction
);
2551 final Runnable pooledThreadWithProgressRunnable
= new Runnable() {
2553 final AbstractTreeBuilder builder
= getBuilder();
2555 builder
.runBackgroundLoading(new Runnable() {
2557 assertNotDispatchThread();
2560 bgBuildAction
.run();
2562 if (edtPostRunnable
!= null) {
2563 builder
.updateAfterLoadedInBackground(new Runnable() {
2566 assertIsDispatchThread();
2568 edtPostRunnable
.run();
2569 } catch (ProcessCanceledException e
) {
2573 unregisterWorkerTask(bgBuildAction
, finalizer
);
2579 unregisterWorkerTask(bgBuildAction
, finalizer
);
2582 catch (ProcessCanceledException e
) {
2584 unregisterWorkerTask(bgBuildAction
, finalizer
);
2586 catch (Throwable t
) {
2587 unregisterWorkerTask(bgBuildAction
, finalizer
);
2588 throw new RuntimeException(t
);
2595 Runnable pooledThreadRunnable
= new Runnable() {
2598 if (myProgress
!= null) {
2599 ProgressManager
.getInstance().runProcess(pooledThreadWithProgressRunnable
, myProgress
);
2602 pooledThreadWithProgressRunnable
.run();
2605 catch (ProcessCanceledException e
) {
2607 unregisterWorkerTask(bgBuildAction
, finalizer
);
2612 if (isPassthroughMode()) {
2615 if (myWorker
== null || myWorker
.isDisposed()) {
2616 myWorker
= new WorkerThread("AbstractTreeBuilder.Worker", 1);
2618 myWorker
.addTaskFirst(pooledThreadRunnable
);
2619 myWorker
.dispose(false);
2622 myWorker
.addTaskFirst(pooledThreadRunnable
);
2629 private void registerWorkerTask(Runnable runnable
) {
2630 synchronized (myActiveWorkerTasks
) {
2631 myActiveWorkerTasks
.add(runnable
);
2635 private void unregisterWorkerTask(Runnable runnable
, @Nullable Runnable finalizeRunnable
) {
2637 synchronized (myActiveWorkerTasks
) {
2638 wasRemoved
= myActiveWorkerTasks
.remove(runnable
);
2641 if (wasRemoved
&& finalizeRunnable
!= null) {
2642 finalizeRunnable
.run();
2648 public boolean isWorkerBusy() {
2649 synchronized (myActiveWorkerTasks
) {
2650 return myActiveWorkerTasks
.size() > 0;
2654 private void clearWorkerTasks() {
2655 synchronized (myActiveWorkerTasks
) {
2656 myActiveWorkerTasks
.clear();
2660 private void updateNodeImageAndPosition(final DefaultMutableTreeNode node
, boolean updatePosition
) {
2661 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
2662 NodeDescriptor descriptor
= getDescriptorFrom(node
);
2663 if (getElementFromDescriptor(descriptor
) == null) return;
2665 boolean notified
= false;
2666 if (updatePosition
) {
2667 DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)node
.getParent();
2668 if (parentNode
!= null) {
2669 int oldIndex
= parentNode
.getIndex(node
);
2670 int newIndex
= oldIndex
;
2671 if (isLoadingChildrenFor(node
.getParent()) || getBuilder().isChildrenResortingNeeded(descriptor
)) {
2672 final ArrayList
<TreeNode
> children
= new ArrayList
<TreeNode
>(parentNode
.getChildCount());
2673 for (int i
= 0; i
< parentNode
.getChildCount(); i
++) {
2674 children
.add(parentNode
.getChildAt(i
));
2676 sortChildren(node
, children
, true, false);
2677 newIndex
= children
.indexOf(node
);
2680 if (oldIndex
!= newIndex
) {
2681 List
<Object
> pathsToExpand
= new ArrayList
<Object
>();
2682 List
<Object
> selectionPaths
= new ArrayList
<Object
>();
2683 TreeBuilderUtil
.storePaths(getBuilder(), node
, pathsToExpand
, selectionPaths
, false);
2684 removeNodeFromParent(node
, false);
2685 myTreeModel
.insertNodeInto(node
, parentNode
, newIndex
);
2686 TreeBuilderUtil
.restorePaths(getBuilder(), pathsToExpand
, selectionPaths
, false);
2690 myTreeModel
.nodeChanged(node
);
2695 myTreeModel
.nodeChanged(node
);
2701 myTreeModel
.nodeChanged(node
);
2706 public DefaultTreeModel
getTreeModel() {
2710 private void insertNodesInto(final ArrayList
<TreeNode
> toInsert
, final DefaultMutableTreeNode parentNode
) {
2711 sortChildren(parentNode
, toInsert
, false, true);
2712 final ArrayList
<TreeNode
> all
= new ArrayList
<TreeNode
>(toInsert
.size() + parentNode
.getChildCount());
2713 all
.addAll(toInsert
);
2714 all
.addAll(TreeUtil
.childrenToArray(parentNode
));
2716 if (toInsert
.size() > 0) {
2717 sortChildren(parentNode
, all
, true, true);
2719 int[] newNodeIndices
= new int[toInsert
.size()];
2720 int eachNewNodeIndex
= 0;
2721 TreeMap
<Integer
, TreeNode
> insertSet
= new TreeMap
<Integer
, TreeNode
>();
2722 for (int i
= 0; i
< toInsert
.size(); i
++) {
2723 TreeNode eachNewNode
= toInsert
.get(i
);
2724 while (all
.get(eachNewNodeIndex
) != eachNewNode
) {
2727 newNodeIndices
[i
] = eachNewNodeIndex
;
2728 insertSet
.put(eachNewNodeIndex
, eachNewNode
);
2731 Iterator
<Integer
> indices
= insertSet
.keySet().iterator();
2732 while (indices
.hasNext()) {
2733 Integer eachIndex
= indices
.next();
2734 TreeNode eachNode
= insertSet
.get(eachIndex
);
2735 parentNode
.insert((MutableTreeNode
)eachNode
, eachIndex
);
2738 myTreeModel
.nodesWereInserted(parentNode
, newNodeIndices
);
2741 ArrayList
<TreeNode
> before
= new ArrayList
<TreeNode
>();
2744 sortChildren(parentNode
, all
, true, false);
2745 if (!before
.equals(all
)) {
2746 processInnerChange(new Runnable() {
2748 parentNode
.removeAllChildren();
2749 for (TreeNode each
: all
) {
2750 parentNode
.add((MutableTreeNode
)each
);
2752 myTreeModel
.nodeStructureChanged(parentNode
);
2759 private void sortChildren(DefaultMutableTreeNode node
, ArrayList
<TreeNode
> children
, boolean updateStamp
, boolean forceSort
) {
2760 NodeDescriptor descriptor
= getDescriptorFrom(node
);
2761 assert descriptor
!= null;
2763 if (descriptor
.getChildrenSortingStamp() >= getComparatorStamp() && !forceSort
) return;
2764 if (children
.size() > 0) {
2765 getBuilder().sortChildren(myNodeComparator
, node
, children
);
2769 descriptor
.setChildrenSortingStamp(getComparatorStamp());
2773 private void disposeNode(DefaultMutableTreeNode node
) {
2774 TreeNode parent
= node
.getParent();
2775 if (parent
instanceof DefaultMutableTreeNode
) {
2776 addToUnbuilt((DefaultMutableTreeNode
)parent
);
2779 if (node
.getChildCount() > 0) {
2780 for (DefaultMutableTreeNode _node
= (DefaultMutableTreeNode
)node
.getFirstChild(); _node
!= null; _node
= _node
.getNextSibling()) {
2785 removeFromUpdating(node
);
2786 removeFromUnbuilt(node
);
2788 if (isLoadingNode(node
)) return;
2789 NodeDescriptor descriptor
= getDescriptorFrom(node
);
2790 if (descriptor
== null) return;
2791 final Object element
= getElementFromDescriptor(descriptor
);
2792 removeMapping(element
, node
, null);
2793 myAutoExpandRoots
.remove(element
);
2794 node
.setUserObject(null);
2795 node
.removeAllChildren();
2798 public boolean addSubtreeToUpdate(final DefaultMutableTreeNode root
) {
2799 return addSubtreeToUpdate(root
, null);
2802 public boolean addSubtreeToUpdate(final DefaultMutableTreeNode root
, Runnable runAfterUpdate
) {
2803 Object element
= getElementFor(root
);
2804 if (getTreeStructure().isAlwaysLeaf(element
)) {
2805 removeLoading(root
, true);
2807 if (runAfterUpdate
!= null) {
2808 getReady(this).doWhenDone(runAfterUpdate
);
2813 if (isReleaseRequested()) {
2814 processNodeActionsIfReady(root
);
2816 getUpdater().runAfterUpdate(runAfterUpdate
);
2817 getUpdater().addSubtreeToUpdate(root
);
2823 public boolean wasRootNodeInitialized() {
2824 return myRootNodeWasInitialized
;
2827 private boolean isRootNodeBuilt() {
2828 return myRootNodeWasInitialized
&& isNodeBeingBuilt(myRootNode
);
2831 public void select(final Object
[] elements
, @Nullable final Runnable onDone
) {
2832 select(elements
, onDone
, false);
2835 public void select(final Object
[] elements
, @Nullable final Runnable onDone
, boolean addToSelection
) {
2836 select(elements
, onDone
, addToSelection
, false);
2839 public void select(final Object
[] elements
, @Nullable final Runnable onDone
, boolean addToSelection
, boolean deferred
) {
2840 _select(elements
, onDone
, addToSelection
, true, false, true, deferred
, false, false);
2843 void _select(final Object
[] elements
,
2844 final Runnable onDone
,
2845 final boolean addToSelection
,
2846 final boolean checkCurrentSelection
,
2847 final boolean checkIfInStructure
) {
2849 _select(elements
, onDone
, addToSelection
, checkCurrentSelection
, checkIfInStructure
, true, false, false, false);
2852 void _select(final Object
[] elements
,
2853 final Runnable onDone
,
2854 final boolean addToSelection
,
2855 final boolean checkCurrentSelection
,
2856 final boolean checkIfInStructure
,
2857 final boolean scrollToVisible
) {
2859 _select(elements
, onDone
, addToSelection
, checkCurrentSelection
, checkIfInStructure
, scrollToVisible
, false, false, false);
2862 public void userSelect(final Object
[] elements
,
2863 final Runnable onDone
,
2864 final boolean addToSelection
,
2866 _select(elements
, onDone
, addToSelection
, true, false, scroll
, false, true, true);
2869 void _select(final Object
[] elements
,
2870 final Runnable onDone
,
2871 final boolean addToSelection
,
2872 final boolean checkCurrentSelection
,
2873 final boolean checkIfInStructure
,
2874 final boolean scrollToVisible
,
2875 final boolean deferred
,
2876 final boolean canSmartExpand
,
2877 final boolean mayQueue
) {
2879 AbstractTreeUpdater updater
= getUpdater();
2880 if (mayQueue
&& updater
!= null) {
2881 updater
.queueSelection(new SelectionRequest(elements
, onDone
, addToSelection
, checkCurrentSelection
, checkIfInStructure
, scrollToVisible
, deferred
, canSmartExpand
));
2885 boolean willAffectSelection
= elements
.length
> 0 || (elements
.length
== 0 && addToSelection
);
2886 if (!willAffectSelection
) {
2891 final boolean oldCanProcessDeferredSelection
= myCanProcessDeferredSelections
;
2893 if (!deferred
&& wasRootNodeInitialized() && willAffectSelection
) {
2894 myCanProcessDeferredSelections
= false;
2897 if (!checkDeferred(deferred
, onDone
)) return;
2899 if (!deferred
&& oldCanProcessDeferredSelection
&& !myCanProcessDeferredSelections
) {
2900 getTree().clearSelection();
2904 runDone(new Runnable() {
2906 if (!checkDeferred(deferred
, onDone
)) return;
2908 final Set
<Object
> currentElements
= getSelectedElements();
2910 if (checkCurrentSelection
&& currentElements
.size() > 0 && elements
.length
== currentElements
.size()) {
2911 boolean runSelection
= false;
2912 for (Object eachToSelect
: elements
) {
2913 if (!currentElements
.contains(eachToSelect
)) {
2914 runSelection
= true;
2919 if (!runSelection
) {
2920 if (elements
.length
> 0) {
2921 selectVisible(elements
[0], onDone
, true, true, scrollToVisible
);
2927 Set
<Object
> toSelect
= new HashSet
<Object
>();
2928 myTree
.clearSelection();
2929 toSelect
.addAll(Arrays
.asList(elements
));
2930 if (addToSelection
) {
2931 toSelect
.addAll(currentElements
);
2934 if (checkIfInStructure
) {
2935 final Iterator
<Object
> allToSelect
= toSelect
.iterator();
2936 while (allToSelect
.hasNext()) {
2937 Object each
= allToSelect
.next();
2938 if (!isInStructure(each
)) {
2939 allToSelect
.remove();
2944 final Object
[] elementsToSelect
= ArrayUtil
.toObjectArray(toSelect
);
2946 if (wasRootNodeInitialized()) {
2947 final int[] originalRows
= myTree
.getSelectionRows();
2948 if (!addToSelection
) {
2949 myTree
.clearSelection();
2951 addNext(elementsToSelect
, 0, new Runnable() {
2953 if (getTree().isSelectionEmpty()) {
2954 processInnerChange(new Runnable() {
2956 restoreSelection(currentElements
);
2962 }, originalRows
, deferred
, scrollToVisible
, canSmartExpand
);
2965 addToDeferred(elementsToSelect
, onDone
);
2971 private void restoreSelection(Set
<Object
> selection
) {
2972 for (Object each
: selection
) {
2973 DefaultMutableTreeNode node
= getNodeForElement(each
, false);
2974 if (node
!= null && isValidForSelectionAdjusting(node
)) {
2975 addSelectionPath(getPathFor(node
), false, null, null);
2981 private void addToDeferred(final Object
[] elementsToSelect
, final Runnable onDone
) {
2982 myDeferredSelections
.clear();
2983 myDeferredSelections
.add(new Runnable() {
2985 select(elementsToSelect
, onDone
, false, true);
2990 private boolean checkDeferred(boolean isDeferred
, @Nullable Runnable onDone
) {
2991 if (!isDeferred
|| myCanProcessDeferredSelections
|| !wasRootNodeInitialized()) {
3001 final Set
<Object
> getSelectedElements() {
3002 final TreePath
[] paths
= myTree
.getSelectionPaths();
3004 Set
<Object
> result
= new HashSet
<Object
>();
3005 if (paths
!= null) {
3006 for (TreePath eachPath
: paths
) {
3007 if (eachPath
.getLastPathComponent() instanceof DefaultMutableTreeNode
) {
3008 final DefaultMutableTreeNode eachNode
= (DefaultMutableTreeNode
)eachPath
.getLastPathComponent();
3009 final Object eachElement
= getElementFor(eachNode
);
3010 if (eachElement
!= null) {
3011 result
.add(eachElement
);
3020 private void addNext(final Object
[] elements
,
3022 @Nullable final Runnable onDone
,
3023 final int[] originalRows
,
3024 final boolean deferred
,
3025 final boolean scrollToVisible
,
3026 final boolean canSmartExpand
) {
3027 if (i
>= elements
.length
) {
3028 if (myTree
.isSelectionEmpty()) {
3029 myTree
.setSelectionRows(originalRows
);
3034 if (!checkDeferred(deferred
, onDone
)) {
3038 doSelect(elements
[i
], new Runnable() {
3040 if (!checkDeferred(deferred
, onDone
)) return;
3042 addNext(elements
, i
+ 1, onDone
, originalRows
, deferred
, scrollToVisible
, canSmartExpand
);
3044 }, true, deferred
, i
== 0, scrollToVisible
, canSmartExpand
);
3048 public void select(final Object element
, @Nullable final Runnable onDone
) {
3049 select(element
, onDone
, false);
3052 public void select(final Object element
, @Nullable final Runnable onDone
, boolean addToSelection
) {
3053 _select(new Object
[]{element
}, onDone
, addToSelection
, true, false);
3056 private void doSelect(final Object element
,
3057 final Runnable onDone
,
3058 final boolean addToSelection
,
3059 final boolean deferred
,
3060 final boolean canBeCentered
,
3061 final boolean scrollToVisible
,
3062 boolean canSmartExpand
) {
3063 final Runnable _onDone
= new Runnable() {
3065 if (!checkDeferred(deferred
, onDone
)) return;
3066 selectVisible(element
, onDone
, addToSelection
, canBeCentered
, scrollToVisible
);
3069 _expand(element
, _onDone
, true, false, canSmartExpand
);
3072 public void scrollSelectionToVisible(@Nullable Runnable onDone
, boolean shouldBeCentered
) {
3073 int[] rows
= myTree
.getSelectionRows();
3074 if (rows
== null || rows
.length
== 0) {
3080 Object toSelect
= null;
3081 for (int eachRow
: rows
) {
3082 TreePath path
= myTree
.getPathForRow(eachRow
);
3083 toSelect
= getElementFor(path
.getLastPathComponent());
3084 if (toSelect
!= null) break;
3087 if (toSelect
!= null) {
3088 selectVisible(toSelect
, onDone
, true, shouldBeCentered
, true);
3092 private void selectVisible(Object element
, final Runnable onDone
, boolean addToSelection
, boolean canBeCentered
, final boolean scroll
) {
3093 final DefaultMutableTreeNode toSelect
= getNodeForElement(element
, false);
3095 if (toSelect
== null) {
3100 if (getRootNode() == toSelect
&& !myTree
.isRootVisible()) {
3105 final int row
= myTree
.getRowForPath(new TreePath(toSelect
.getPath()));
3107 if (myUpdaterState
!= null) {
3108 myUpdaterState
.addSelection(element
);
3111 if (Registry
.is("ide.tree.autoscrollToVCenter") && canBeCentered
) {
3112 runDone(new Runnable() {
3114 TreeUtil
.showRowCentered(myTree
, row
, false, scroll
).doWhenDone(new Runnable() {
3123 TreeUtil
.showAndSelect(myTree
, row
- 2, row
+ 2, row
, -1, addToSelection
, scroll
).doWhenDone(new Runnable() {
3131 public void expand(final Object element
, @Nullable final Runnable onDone
) {
3132 expand(new Object
[]{element
}, onDone
);
3135 public void expand(final Object
[] element
, @Nullable final Runnable onDone
) {
3136 expand(element
, onDone
, false);
3140 void expand(final Object element
, @Nullable final Runnable onDone
, boolean checkIfInStructure
) {
3141 _expand(new Object
[]{element
}, onDone
== null ?
new EmptyRunnable() : onDone
, false, checkIfInStructure
, false);
3144 void expand(final Object
[] element
, @Nullable final Runnable onDone
, boolean checkIfInStructure
) {
3145 _expand(element
, onDone
== null ?
new EmptyRunnable() : onDone
, false, checkIfInStructure
, false);
3148 void _expand(final Object
[] element
,
3149 @NotNull final Runnable onDone
,
3150 final boolean parentsOnly
,
3151 final boolean checkIfInStructure
,
3152 final boolean canSmartExpand
) {
3154 runDone(new Runnable() {
3156 if (element
.length
== 0) {
3161 if (myUpdaterState
!= null) {
3162 myUpdaterState
.clearExpansion();
3166 final ActionCallback done
= new ActionCallback(element
.length
);
3167 done
.doWhenDone(new Runnable() {
3171 }).doWhenRejected(new Runnable() {
3177 expandNext(element
, 0, parentsOnly
, checkIfInStructure
, canSmartExpand
, done
);
3182 private void expandNext(final Object
[] elements
, final int index
, final boolean parentsOnly
, final boolean checkIfInStricture
, final boolean canSmartExpand
, final ActionCallback done
) {
3183 if (elements
.length
<= 0) {
3188 if (index
>= elements
.length
) {
3192 _expand(elements
[index
], new Runnable() {
3195 expandNext(elements
, index
+ 1, parentsOnly
, checkIfInStricture
, canSmartExpand
, done
);
3197 }, parentsOnly
, checkIfInStricture
, canSmartExpand
);
3200 public void collapseChildren(final Object element
, @Nullable final Runnable onDone
) {
3201 runDone(new Runnable() {
3203 final DefaultMutableTreeNode node
= getNodeForElement(element
, false);
3205 getTree().collapsePath(new TreePath(node
.getPath()));
3212 private void runDone(@Nullable Runnable done
) {
3213 if (done
== null) return;
3215 if (isYeildingNow()) {
3216 if (!myYeildingDoneRunnables
.contains(done
)) {
3217 myYeildingDoneRunnables
.add(done
);
3225 private void _expand(final Object element
,
3226 @NotNull final Runnable onDone
,
3227 final boolean parentsOnly
,
3228 boolean checkIfInStructure
,
3229 boolean canSmartExpand
) {
3231 if (checkIfInStructure
&& !isInStructure(element
)) {
3236 if (wasRootNodeInitialized()) {
3237 List
<Object
> kidsToExpand
= new ArrayList
<Object
>();
3238 Object eachElement
= element
;
3239 DefaultMutableTreeNode firstVisible
= null;
3241 if (!isValid(eachElement
)) break;
3243 firstVisible
= getNodeForElement(eachElement
, true);
3244 if (eachElement
!= element
|| !parentsOnly
) {
3245 assert !kidsToExpand
.contains(eachElement
) :
3246 "Not a valid tree structure, walking up the structure gives many entries for element=" +
3249 getTreeStructure().getRootElement();
3250 kidsToExpand
.add(eachElement
);
3252 if (firstVisible
!= null) break;
3253 eachElement
= getTreeStructure().getParentElement(eachElement
);
3254 if (eachElement
== null) {
3255 firstVisible
= null;
3261 if (firstVisible
== null) {
3264 else if (kidsToExpand
.size() == 0) {
3265 final DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)firstVisible
.getParent();
3266 if (parentNode
!= null) {
3267 final TreePath parentPath
= new TreePath(parentNode
.getPath());
3268 if (!myTree
.isExpanded(parentPath
)) {
3269 expand(parentPath
, canSmartExpand
);
3275 processExpand(firstVisible
, kidsToExpand
, kidsToExpand
.size() - 1, onDone
, canSmartExpand
);
3279 deferExpansion(element
, onDone
, parentsOnly
, canSmartExpand
);
3283 private void deferExpansion(final Object element
, final Runnable onDone
, final boolean parentsOnly
, final boolean canSmartExpand
) {
3284 myDeferredExpansions
.add(new Runnable() {
3286 _expand(element
, onDone
, parentsOnly
, false, canSmartExpand
);
3291 private void processExpand(final DefaultMutableTreeNode toExpand
,
3292 final List kidsToExpand
,
3293 final int expandIndex
,
3294 @NotNull final Runnable onDone
,
3295 final boolean canSmartExpand
) {
3297 final Object element
= getElementFor(toExpand
);
3298 if (element
== null) {
3303 addNodeAction(element
, new NodeAction() {
3304 public void onReady(final DefaultMutableTreeNode node
) {
3305 if (node
.getChildCount() > 0 && !myTree
.isExpanded(new TreePath(node
.getPath()))) {
3306 if (!isAutoExpand(node
)) {
3307 expand(node
, canSmartExpand
);
3311 if (expandIndex
<= 0) {
3316 final DefaultMutableTreeNode nextNode
= getNodeForElement(kidsToExpand
.get(expandIndex
- 1), false);
3317 if (nextNode
!= null) {
3318 processExpand(nextNode
, kidsToExpand
, expandIndex
- 1, onDone
, canSmartExpand
);
3327 boolean childrenToUpdate
= areChildrenToBeUpdated(toExpand
);
3328 boolean expanded
= myTree
.isExpanded(getPathFor(toExpand
));
3329 boolean unbuilt
= myUnbuiltNodes
.contains(toExpand
);
3332 if (unbuilt
&& !childrenToUpdate
) {
3333 addSubtreeToUpdate(toExpand
);
3336 expand(toExpand
, canSmartExpand
);
3339 if (!unbuilt
&& !childrenToUpdate
) {
3340 processNodeActionsIfReady(toExpand
);
3344 private boolean areChildrenToBeUpdated(DefaultMutableTreeNode node
) {
3345 return getUpdater().isEnqueuedToUpdate(node
) || isUpdatingParent(node
);
3348 private String
asString(DefaultMutableTreeNode node
) {
3349 if (node
== null) return null;
3351 StringBuffer children
= new StringBuffer(node
.toString());
3352 children
.append(" [");
3353 for (int i
= 0; i
< node
.getChildCount(); i
++) {
3354 children
.append(node
.getChildAt(i
));
3355 if (i
< node
.getChildCount() - 1) {
3356 children
.append(",");
3359 children
.append("]");
3361 return children
.toString();
3365 public Object
getElementFor(Object node
) {
3366 if (!(node
instanceof DefaultMutableTreeNode
)) return null;
3367 return getElementFor((DefaultMutableTreeNode
)node
);
3371 Object
getElementFor(DefaultMutableTreeNode node
) {
3373 final Object o
= node
.getUserObject();
3374 if (o
instanceof NodeDescriptor
) {
3375 return getElementFromDescriptor(((NodeDescriptor
)o
));
3382 public final boolean isNodeBeingBuilt(final TreePath path
) {
3383 return isNodeBeingBuilt(path
.getLastPathComponent());
3386 public final boolean isNodeBeingBuilt(Object node
) {
3387 if (isReleaseRequested()) return false;
3389 return getParentBuiltNode(node
) != null;
3392 public final DefaultMutableTreeNode
getParentBuiltNode(Object node
) {
3393 DefaultMutableTreeNode parent
= getParentLoading(node
);
3394 if (parent
!= null) return parent
;
3396 if (isLoadingParent(node
)) return (DefaultMutableTreeNode
)node
;
3398 final boolean childrenAreNoLoadedYet
= myUnbuiltNodes
.contains(node
);
3399 if (childrenAreNoLoadedYet
) {
3400 if (node
instanceof DefaultMutableTreeNode
) {
3401 final TreePath nodePath
= new TreePath(((DefaultMutableTreeNode
)node
).getPath());
3402 if (!myTree
.isExpanded(nodePath
)) return null;
3405 return (DefaultMutableTreeNode
)node
;
3412 private boolean isLoadingParent(Object node
) {
3413 if (!(node
instanceof DefaultMutableTreeNode
)) return false;
3414 return isLoadedInBackground(getElementFor((DefaultMutableTreeNode
)node
));
3417 public void setTreeStructure(final AbstractTreeStructure treeStructure
) {
3418 myTreeStructure
= treeStructure
;
3419 clearUpdaterState();
3422 public AbstractTreeUpdater
getUpdater() {
3426 public void setUpdater(final AbstractTreeUpdater updater
) {
3427 myUpdater
= updater
;
3428 if (updater
!= null && myUpdateIfInactive
) {
3429 updater
.showNotify();
3432 if (myUpdater
!= null) {
3433 myUpdater
.setPassThroughMode(myPassthroughMode
);
3437 public DefaultMutableTreeNode
getRootNode() {
3441 public void setRootNode(@NotNull final DefaultMutableTreeNode rootNode
) {
3442 myRootNode
= rootNode
;
3445 private void dropUpdaterStateIfExternalChange() {
3446 if (!isInnerChange()) {
3447 clearUpdaterState();
3448 myAutoExpandRoots
.clear();
3452 void clearUpdaterState() {
3453 myUpdaterState
= null;
3456 private void createMapping(Object element
, DefaultMutableTreeNode node
) {
3457 if (!myElementToNodeMap
.containsKey(element
)) {
3458 myElementToNodeMap
.put(element
, node
);
3461 final Object value
= myElementToNodeMap
.get(element
);
3462 final List
<DefaultMutableTreeNode
> nodes
;
3463 if (value
instanceof DefaultMutableTreeNode
) {
3464 nodes
= new ArrayList
<DefaultMutableTreeNode
>();
3465 nodes
.add((DefaultMutableTreeNode
)value
);
3466 myElementToNodeMap
.put(element
, nodes
);
3469 nodes
= (List
<DefaultMutableTreeNode
>)value
;
3475 private void removeMapping(Object element
, DefaultMutableTreeNode node
, @Nullable Object elementToPutNodeActionsFor
) {
3476 final Object value
= myElementToNodeMap
.get(element
);
3477 if (value
!= null) {
3478 if (value
instanceof DefaultMutableTreeNode
) {
3479 if (value
.equals(node
)) {
3480 myElementToNodeMap
.remove(element
);
3484 List
<DefaultMutableTreeNode
> nodes
= (List
<DefaultMutableTreeNode
>)value
;
3485 final boolean reallyRemoved
= nodes
.remove(node
);
3486 if (reallyRemoved
) {
3487 if (nodes
.isEmpty()) {
3488 myElementToNodeMap
.remove(element
);
3494 remapNodeActions(element
, elementToPutNodeActionsFor
);
3497 private void remapNodeActions(Object element
, Object elementToPutNodeActionsFor
) {
3498 _remapNodeActions(element
, elementToPutNodeActionsFor
, myNodeActions
);
3499 _remapNodeActions(element
, elementToPutNodeActionsFor
, myNodeChildrenActions
);
3502 private void _remapNodeActions(Object element
, Object elementToPutNodeActionsFor
, final Map
<Object
, List
<NodeAction
>> nodeActions
) {
3503 final List
<NodeAction
> actions
= nodeActions
.get(element
);
3504 nodeActions
.remove(element
);
3506 if (elementToPutNodeActionsFor
!= null && actions
!= null) {
3507 nodeActions
.put(elementToPutNodeActionsFor
, actions
);
3511 private DefaultMutableTreeNode
getFirstNode(Object element
) {
3512 return findNode(element
, 0);
3515 private DefaultMutableTreeNode
findNode(final Object element
, int startIndex
) {
3516 final Object value
= getBuilder().findNodeByElement(element
);
3517 if (value
== null) {
3520 if (value
instanceof DefaultMutableTreeNode
) {
3521 return startIndex
== 0 ?
(DefaultMutableTreeNode
)value
: null;
3523 final List
<DefaultMutableTreeNode
> nodes
= (List
<DefaultMutableTreeNode
>)value
;
3524 return startIndex
< nodes
.size() ? nodes
.get(startIndex
) : null;
3527 protected Object
findNodeByElement(Object element
) {
3528 if (myElementToNodeMap
.containsKey(element
)) {
3529 return myElementToNodeMap
.get(element
);
3533 TREE_NODE_WRAPPER
.setValue(element
);
3534 return myElementToNodeMap
.get(TREE_NODE_WRAPPER
);
3537 TREE_NODE_WRAPPER
.setValue(null);
3541 private DefaultMutableTreeNode
findNodeForChildElement(DefaultMutableTreeNode parentNode
, Object element
) {
3542 final Object value
= myElementToNodeMap
.get(element
);
3543 if (value
== null) {
3547 if (value
instanceof DefaultMutableTreeNode
) {
3548 final DefaultMutableTreeNode elementNode
= (DefaultMutableTreeNode
)value
;
3549 return parentNode
.equals(elementNode
.getParent()) ? elementNode
: null;
3552 final List
<DefaultMutableTreeNode
> allNodesForElement
= (List
<DefaultMutableTreeNode
>)value
;
3553 for (final DefaultMutableTreeNode elementNode
: allNodesForElement
) {
3554 if (parentNode
.equals(elementNode
.getParent())) {
3562 public void cancelBackgroundLoading() {
3563 if (myWorker
!= null) {
3564 myWorker
.cancelTasks();
3571 private void addNodeAction(Object element
, NodeAction action
, boolean shouldChildrenBeReady
) {
3572 _addNodeAction(element
, action
, myNodeActions
);
3573 if (shouldChildrenBeReady
) {
3574 _addNodeAction(element
, action
, myNodeChildrenActions
);
3579 private void _addNodeAction(Object element
, NodeAction action
, Map
<Object
, List
<NodeAction
>> map
) {
3580 maybeSetBusyAndScheduleWaiterForReady(true);
3581 List
<NodeAction
> list
= map
.get(element
);
3583 list
= new ArrayList
<NodeAction
>();
3584 map
.put(element
, list
);
3590 private void cleanUpNow() {
3591 if (isReleaseRequested()) return;
3593 final UpdaterTreeState state
= new UpdaterTreeState(this);
3595 myTree
.collapsePath(new TreePath(myTree
.getModel().getRoot()));
3596 myTree
.clearSelection();
3597 getRootNode().removeAllChildren();
3599 myRootNodeWasInitialized
= false;
3601 myElementToNodeMap
.clear();
3602 myDeferredSelections
.clear();
3603 myDeferredExpansions
.clear();
3604 myLoadedInBackground
.clear();
3605 myUnbuiltNodes
.clear();
3606 myUpdateFromRootRequested
= true;
3608 if (myWorker
!= null) {
3609 Disposer
.dispose(myWorker
);
3613 myTree
.invalidate();
3615 state
.restore(null);
3618 public AbstractTreeUi
setClearOnHideDelay(final long clearOnHideDelay
) {
3619 myClearOnHideDelay
= clearOnHideDelay
;
3623 public void setJantorPollPeriod(final long time
) {
3624 myJanitorPollPeriod
= time
;
3627 public void setCheckStructure(final boolean checkStructure
) {
3628 myCheckStructure
= checkStructure
;
3631 private class MySelectionListener
implements TreeSelectionListener
{
3632 public void valueChanged(final TreeSelectionEvent e
) {
3633 dropUpdaterStateIfExternalChange();
3638 private class MyExpansionListener
implements TreeExpansionListener
{
3639 public void treeExpanded(TreeExpansionEvent event
) {
3640 dropUpdaterStateIfExternalChange();
3642 TreePath path
= event
.getPath();
3644 if (myRequestedExpand
!= null && !myRequestedExpand
.equals(path
)) return;
3646 final DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
3648 if (!myUnbuiltNodes
.contains(node
)) {
3649 removeLoading(node
, false);
3651 Set
<DefaultMutableTreeNode
> childrenToUpdate
= new HashSet
<DefaultMutableTreeNode
>();
3652 for (int i
= 0; i
< node
.getChildCount(); i
++) {
3653 DefaultMutableTreeNode each
= (DefaultMutableTreeNode
)node
.getChildAt(i
);
3654 if (myUnbuiltNodes
.contains(each
)) {
3655 makeLoadingOrLeafIfNoChildren(each
);
3656 childrenToUpdate
.add(each
);
3660 if (childrenToUpdate
.size() > 0) {
3661 for (DefaultMutableTreeNode each
: childrenToUpdate
) {
3662 maybeUpdateSubtreeToUpdate(each
);
3667 getBuilder().expandNodeChildren(node
);
3670 processSmartExpand(node
, canSmartExpand(node
, true), false);
3671 processNodeActionsIfReady(node
);
3674 public void treeCollapsed(TreeExpansionEvent e
) {
3675 dropUpdaterStateIfExternalChange();
3677 final TreePath path
= e
.getPath();
3678 final DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
3679 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
3682 TreePath pathToSelect
= null;
3683 if (isSelectionInside(node
)) {
3684 pathToSelect
= new TreePath(node
.getPath());
3688 NodeDescriptor descriptor
= getDescriptorFrom(node
);
3689 if (getBuilder().isDisposeOnCollapsing(descriptor
)) {
3690 runDone(new Runnable() {
3692 if (isDisposed(node
)) return;
3694 TreePath nodePath
= new TreePath(node
.getPath());
3695 if (myTree
.isExpanded(nodePath
)) return;
3697 removeChildren(node
);
3698 makeLoadingOrLeafIfNoChildren(node
);
3701 if (node
.equals(getRootNode())) {
3702 if (myTree
.isRootVisible()) {
3703 //todo kirillk to investigate -- should be done by standard selction move
3704 //addSelectionPath(new TreePath(getRootNode().getPath()), true, Condition.FALSE);
3708 myTreeModel
.reload(node
);
3712 if (pathToSelect
!= null && myTree
.isSelectionEmpty()) {
3713 addSelectionPath(pathToSelect
, true, Condition
.FALSE
, null);
3717 private void removeChildren(DefaultMutableTreeNode node
) {
3718 EnumerationCopy copy
= new EnumerationCopy(node
.children());
3719 while (copy
.hasMoreElements()) {
3720 disposeNode((DefaultMutableTreeNode
)copy
.nextElement());
3722 node
.removeAllChildren();
3723 myTreeModel
.nodeStructureChanged(node
);
3727 private void maybeUpdateSubtreeToUpdate(final DefaultMutableTreeNode subtreeRoot
) {
3728 if (!myUnbuiltNodes
.contains(subtreeRoot
)) return;
3729 TreePath path
= getPathFor(subtreeRoot
);
3731 if (myTree
.getRowForPath(path
) == -1) return;
3733 DefaultMutableTreeNode parent
= getParentBuiltNode(subtreeRoot
);
3734 if (parent
== null) {
3735 addSubtreeToUpdate(subtreeRoot
);
3736 } else if (parent
!= subtreeRoot
) {
3737 addNodeAction(getElementFor(subtreeRoot
), new NodeAction() {
3738 public void onReady(DefaultMutableTreeNode parent
) {
3739 maybeUpdateSubtreeToUpdate(subtreeRoot
);
3745 private boolean isSelectionInside(DefaultMutableTreeNode parent
) {
3746 TreePath path
= new TreePath(myTreeModel
.getPathToRoot(parent
));
3747 TreePath
[] paths
= myTree
.getSelectionPaths();
3748 if (paths
== null) return false;
3749 for (TreePath path1
: paths
) {
3750 if (path
.isDescendant(path1
)) return true;
3755 public boolean isInStructure(@Nullable Object element
) {
3756 Object eachParent
= element
;
3757 while (eachParent
!= null) {
3758 if (getTreeStructure().getRootElement().equals(eachParent
)) return true;
3759 eachParent
= getTreeStructure().getParentElement(eachParent
);
3765 interface NodeAction
{
3766 void onReady(DefaultMutableTreeNode node
);
3769 public void setCanYield(final boolean canYield
) {
3770 myCanYield
= canYield
;
3773 public Collection
<TreeUpdatePass
> getYeildingPasses() {
3774 return myYeildingPasses
;
3777 public boolean isBuilt(Object element
) {
3778 if (!myElementToNodeMap
.containsKey(element
)) return false;
3779 final Object node
= myElementToNodeMap
.get(element
);
3780 return !myUnbuiltNodes
.contains(node
);
3783 static class LoadedChildren
{
3785 private List myElements
;
3786 private Map
<Object
, NodeDescriptor
> myDescriptors
= new HashMap
<Object
, NodeDescriptor
>();
3787 private Map
<NodeDescriptor
, Boolean
> myChanges
= new HashMap
<NodeDescriptor
, Boolean
>();
3789 LoadedChildren(Object
[] elements
) {
3790 myElements
= Arrays
.asList(elements
!= null ? elements
: new Object
[0]);
3793 void putDescriptor(Object element
, NodeDescriptor descriptor
, boolean isChanged
) {
3794 assert myElements
.contains(element
);
3795 myDescriptors
.put(element
, descriptor
);
3796 myChanges
.put(descriptor
, isChanged
);
3799 List
getElements() {
3803 NodeDescriptor
getDescriptor(Object element
) {
3804 return myDescriptors
.get(element
);
3808 public String
toString() {
3809 return Arrays
.asList(myElements
) + "->" + myChanges
;
3812 public boolean isUpdated(Object element
) {
3813 NodeDescriptor desc
= getDescriptor(element
);
3814 return myChanges
.get(desc
);
3818 UpdaterTreeState
getUpdaterState() {
3819 return myUpdaterState
;
3822 private ActionCallback
addReadyCallback(Object requestor
) {
3823 synchronized (myReadyCallbacks
) {
3824 ActionCallback cb
= myReadyCallbacks
.get(requestor
);
3826 cb
= new ActionCallback();
3827 myReadyCallbacks
.put(requestor
, cb
);
3834 private ActionCallback
[] getReadyCallbacks(boolean clear
) {
3835 synchronized (myReadyCallbacks
) {
3836 ActionCallback
[] result
= myReadyCallbacks
.values().toArray(new ActionCallback
[myReadyCallbacks
.size()]);
3838 myReadyCallbacks
.clear();
3844 private long getComparatorStamp() {
3845 if (myNodeDescriptorComparator
instanceof NodeDescriptor
.NodeComparator
) {
3846 long currentComparatorStamp
= ((NodeDescriptor
.NodeComparator
)myNodeDescriptorComparator
).getStamp();
3847 if (currentComparatorStamp
> myLastComparatorStamp
) {
3848 myOwnComparatorStamp
= Math
.max(myOwnComparatorStamp
, currentComparatorStamp
) + 1;
3850 myLastComparatorStamp
= currentComparatorStamp
;
3852 return Math
.max(currentComparatorStamp
, myOwnComparatorStamp
);
3855 return myOwnComparatorStamp
;
3859 public void incComparatorStamp() {
3860 myOwnComparatorStamp
= getComparatorStamp() + 1;
3863 public static class UpdateInfo
{
3864 NodeDescriptor myDescriptor
;
3865 TreeUpdatePass myPass
;
3866 boolean myCanSmartExpand
;
3867 boolean myWasExpanded
;
3868 boolean myForceUpdate
;
3869 boolean myDescriptorIsUpToDate
;
3871 public UpdateInfo(NodeDescriptor descriptor
,
3872 TreeUpdatePass pass
,
3873 boolean canSmartExpand
,
3874 boolean wasExpanded
,
3875 boolean forceUpdate
,
3876 boolean descriptorIsUpToDate
) {
3877 myDescriptor
= descriptor
;
3879 myCanSmartExpand
= canSmartExpand
;
3880 myWasExpanded
= wasExpanded
;
3881 myForceUpdate
= forceUpdate
;
3882 myDescriptorIsUpToDate
= descriptorIsUpToDate
;
3885 synchronized NodeDescriptor
getDescriptor() {
3886 return myDescriptor
;
3889 synchronized TreeUpdatePass
getPass() {
3893 synchronized boolean isCanSmartExpand() {
3894 return myCanSmartExpand
;
3897 synchronized boolean isWasExpanded() {
3898 return myWasExpanded
;
3901 synchronized boolean isForceUpdate() {
3902 return myForceUpdate
;
3905 synchronized boolean isDescriptorIsUpToDate() {
3906 return myDescriptorIsUpToDate
;
3909 public synchronized void apply(UpdateInfo updateInfo
) {
3910 myDescriptor
= updateInfo
.myDescriptor
;
3911 myPass
= updateInfo
.myPass
;
3912 myCanSmartExpand
= updateInfo
.myCanSmartExpand
;
3913 myWasExpanded
= updateInfo
.myWasExpanded
;
3914 myForceUpdate
= updateInfo
.myForceUpdate
;
3915 myDescriptorIsUpToDate
= updateInfo
.myDescriptorIsUpToDate
;
3918 public String
toString() {
3919 return "UpdateInfo: desc=" + myDescriptor
+ " pass=" + myPass
+ " canSmartExpand=" + myCanSmartExpand
+ " wasExpanded=" + myWasExpanded
+ " forceUpdate=" + myForceUpdate
+ " descriptorUpToDate=" + myDescriptorIsUpToDate
;
3924 public void setPassthroughMode(boolean passthrough
) {
3925 myPassthroughMode
= passthrough
;
3926 AbstractTreeUpdater updater
= getUpdater();
3928 if (updater
!= null) {
3929 updater
.setPassThroughMode(myPassthroughMode
);
3932 if (!isUnitTestingMode() && passthrough
) {
3933 // TODO: this assertion should be restored back as soon as possible [JamTreeTableView should be rewritten, etc]
3934 //LOG.error("Pass-through mode for TreeUi is allowed only for unit test mode");
3938 public boolean isPassthroughMode() {
3939 return myPassthroughMode
;
3942 private boolean isUnitTestingMode() {
3943 Application app
= ApplicationManager
.getApplication();
3944 return app
!= null && app
.isUnitTestMode();