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
.Time
;
34 import com
.intellij
.util
.concurrency
.WorkerThread
;
35 import com
.intellij
.util
.containers
.HashSet
;
36 import com
.intellij
.util
.enumeration
.EnumerationCopy
;
37 import com
.intellij
.util
.ui
.UIUtil
;
38 import com
.intellij
.util
.ui
.tree
.TreeUtil
;
39 import com
.intellij
.util
.ui
.update
.Activatable
;
40 import com
.intellij
.util
.ui
.update
.UiNotifyConnector
;
41 import org
.jetbrains
.annotations
.NotNull
;
42 import org
.jetbrains
.annotations
.Nullable
;
45 import javax
.swing
.event
.TreeExpansionEvent
;
46 import javax
.swing
.event
.TreeExpansionListener
;
47 import javax
.swing
.event
.TreeSelectionEvent
;
48 import javax
.swing
.event
.TreeSelectionListener
;
49 import javax
.swing
.tree
.*;
51 import java
.awt
.event
.FocusAdapter
;
52 import java
.awt
.event
.FocusEvent
;
53 import java
.security
.AccessControlException
;
55 import java
.util
.List
;
56 import java
.util
.concurrent
.ScheduledExecutorService
;
58 public class AbstractTreeUi
{
59 private static final Logger LOG
= Logger
.getInstance("#com.intellij.ide.util.treeView.AbstractTreeBuilder");
60 protected JTree myTree
;// protected for TestNG
61 @SuppressWarnings({"WeakerAccess"}) protected DefaultTreeModel myTreeModel
;
62 private AbstractTreeStructure myTreeStructure
;
63 private AbstractTreeUpdater myUpdater
;
65 private Comparator
<NodeDescriptor
> myNodeDescriptorComparator
;
66 private final Comparator
<TreeNode
> myNodeComparator
= new Comparator
<TreeNode
>() {
67 public int compare(TreeNode n1
, TreeNode n2
) {
68 if (isLoadingNode(n1
) || isLoadingNode(n2
)) return 0;
69 NodeDescriptor nodeDescriptor1
= getDescriptorFrom(((DefaultMutableTreeNode
)n1
));
70 NodeDescriptor nodeDescriptor2
= getDescriptorFrom(((DefaultMutableTreeNode
)n2
));
71 return myNodeDescriptorComparator
!= null
72 ? myNodeDescriptorComparator
.compare(nodeDescriptor1
, nodeDescriptor2
)
73 : nodeDescriptor1
.getIndex() - nodeDescriptor2
.getIndex();
76 long myOwnComparatorStamp
;
77 long myLastComparatorStamp
;
79 private DefaultMutableTreeNode myRootNode
;
80 private final HashMap
<Object
, Object
> myElementToNodeMap
= new HashMap
<Object
, Object
>();
81 private final HashSet
<DefaultMutableTreeNode
> myUnbuiltNodes
= new HashSet
<DefaultMutableTreeNode
>();
82 private TreeExpansionListener myExpansionListener
;
83 private MySelectionListener mySelectionListener
;
85 private WorkerThread myWorker
= null;
86 private final Set
<Runnable
> myActiveWorkerTasks
= new HashSet
<Runnable
>();
88 private ProgressIndicator myProgress
;
89 private static final int WAIT_CURSOR_DELAY
= 100;
90 private AbstractTreeNode
<Object
> TREE_NODE_WRAPPER
;
91 private boolean myRootNodeWasInitialized
= false;
92 private final Map
<Object
, List
<NodeAction
>> myNodeActions
= new HashMap
<Object
, List
<NodeAction
>>();
93 private boolean myUpdateFromRootRequested
;
94 private boolean myWasEverShown
;
95 private boolean myUpdateIfInactive
;
97 private final Map
<Object
, UpdateInfo
> myLoadedInBackground
= new HashMap
<Object
, UpdateInfo
>();
98 private final Map
<Object
, List
<NodeAction
>> myNodeChildrenActions
= new HashMap
<Object
, List
<NodeAction
>>();
100 private long myClearOnHideDelay
= -1;
101 private final Map
<AbstractTreeUi
, Long
> ourUi2Countdown
= Collections
.synchronizedMap(new WeakHashMap
<AbstractTreeUi
, Long
>());
103 private final Set
<Runnable
> myDeferredSelections
= new HashSet
<Runnable
>();
104 private final Set
<Runnable
> myDeferredExpansions
= new HashSet
<Runnable
>();
106 private boolean myCanProcessDeferredSelections
;
108 private UpdaterTreeState myUpdaterState
;
109 private AbstractTreeBuilder myBuilder
;
110 private boolean myReleaseRequested
;
112 private final Set
<DefaultMutableTreeNode
> myUpdatingChildren
= new HashSet
<DefaultMutableTreeNode
>();
113 private long myJanitorPollPeriod
= Time
.SECOND
* 10;
114 private boolean myCheckStructure
= false;
117 private boolean myCanYield
= false;
119 private final List
<TreeUpdatePass
> myYeildingPasses
= new ArrayList
<TreeUpdatePass
>();
121 private boolean myYeildingNow
;
123 private final Set
<DefaultMutableTreeNode
> myPendingNodeActions
= new HashSet
<DefaultMutableTreeNode
>();
124 private final Set
<Runnable
> myYeildingDoneRunnables
= new HashSet
<Runnable
>();
126 private final Alarm myBusyAlarm
= new Alarm();
127 private final Runnable myWaiterForReady
= new Runnable() {
129 maybeSetBusyAndScheduleWaiterForReady(false);
133 private final RegistryValue myYeildingUpdate
= Registry
.get("ide.tree.yeildingUiUpdate");
134 private final RegistryValue myShowBusyIndicator
= Registry
.get("ide.tree.showBusyIndicator");
135 private final RegistryValue myWaitForReadyTime
= Registry
.get("ide.tree.waitForReadyTimout");
137 private boolean myWasEverIndexNotReady
;
138 private boolean myShowing
;
139 private FocusAdapter myFocusListener
= new FocusAdapter() {
141 public void focusGained(FocusEvent e
) {
145 private Set
<DefaultMutableTreeNode
> myNotForSmartExpand
= new HashSet
<DefaultMutableTreeNode
>();
146 private TreePath myRequestedExpand
;
147 private ActionCallback myInitialized
= new ActionCallback();
148 private Map
<Object
, ActionCallback
> myReadyCallbacks
= new WeakHashMap
<Object
, ActionCallback
>();
150 private boolean myPassthroughMode
= false;
153 private Set
<Object
> myAutoExpandRoots
= new HashSet
<Object
>();
154 private final RegistryValue myAutoExpandDepth
= Registry
.get("ide.tree.autoExpandMaxDepth");
156 private Set
<DefaultMutableTreeNode
> myWillBeExpaned
= new HashSet
<DefaultMutableTreeNode
>();
157 private SimpleTimerTask myCleanupTask
;
159 protected void init(AbstractTreeBuilder builder
,
161 DefaultTreeModel treeModel
,
162 AbstractTreeStructure treeStructure
,
163 @Nullable Comparator
<NodeDescriptor
> comparator
,
164 boolean updateIfInactive
) {
167 myTreeModel
= treeModel
;
168 TREE_NODE_WRAPPER
= getBuilder().createSearchingTreeNodeWrapper();
169 myTree
.setModel(myTreeModel
);
170 setRootNode((DefaultMutableTreeNode
)treeModel
.getRoot());
171 setTreeStructure(treeStructure
);
172 myNodeDescriptorComparator
= comparator
;
173 myUpdateIfInactive
= updateIfInactive
;
175 myExpansionListener
= new MyExpansionListener();
176 myTree
.addTreeExpansionListener(myExpansionListener
);
178 mySelectionListener
= new MySelectionListener();
179 myTree
.addTreeSelectionListener(mySelectionListener
);
181 setUpdater(getBuilder().createUpdater());
182 myProgress
= getBuilder().createProgressIndicator();
183 Disposer
.register(getBuilder(), getUpdater());
185 final UiNotifyConnector uiNotify
= new UiNotifyConnector(tree
, new Activatable() {
186 public void showNotify() {
188 myWasEverShown
= true;
189 if (!isReleaseRequested()) {
194 public void hideNotify() {
196 if (!validateReleaseRequested()) {
201 Disposer
.register(getBuilder(), uiNotify
);
203 myTree
.addFocusListener(myFocusListener
);
207 boolean isNodeActionsPending() {
208 return !myNodeActions
.isEmpty() || !myNodeChildrenActions
.isEmpty();
211 private void clearNodeActions() {
212 myNodeActions
.clear();
213 myNodeChildrenActions
.clear();
216 private void maybeSetBusyAndScheduleWaiterForReady(boolean forcedBusy
) {
217 if (!myShowBusyIndicator
.asBoolean() || !canYield()) return;
219 if (myTree
instanceof com
.intellij
.ui
.treeStructure
.Tree
) {
220 final com
.intellij
.ui
.treeStructure
.Tree tree
= (Tree
)myTree
;
221 final boolean isBusy
= !isReady() || forcedBusy
;
222 if (isBusy
&& tree
.isShowing()) {
223 tree
.setPaintBusy(true);
224 myBusyAlarm
.cancelAllRequests();
225 myBusyAlarm
.addRequest(myWaiterForReady
, myWaitForReadyTime
.asInteger());
228 tree
.setPaintBusy(false);
233 private void cleanUpAll() {
234 final long now
= System
.currentTimeMillis();
235 final AbstractTreeUi
[] uis
= ourUi2Countdown
.keySet().toArray(new AbstractTreeUi
[ourUi2Countdown
.size()]);
236 for (AbstractTreeUi eachUi
: uis
) {
237 if (eachUi
== null) continue;
238 final Long timeToCleanup
= ourUi2Countdown
.get(eachUi
);
239 if (timeToCleanup
== null) continue;
240 if (now
>= timeToCleanup
.longValue()) {
241 ourUi2Countdown
.remove(eachUi
);
242 Runnable runnable
= new Runnable() {
244 myCleanupTask
= null;
245 getBuilder().cleanUp();
248 if (isPassthroughMode()) {
251 UIUtil
.invokeAndWaitIfNeeded(runnable
);
257 protected void doCleanUp() {
258 Runnable cleanup
= new Runnable() {
260 if (!isReleaseRequested()) {
266 if (isPassthroughMode()) {
269 UIUtil
.invokeLaterIfNeeded(cleanup
);
273 public void activate(boolean byShowing
) {
274 cancelCurrentCleanupTask();
276 myCanProcessDeferredSelections
= true;
277 ourUi2Countdown
.remove(this);
279 if (!myWasEverShown
|| myUpdateFromRootRequested
|| myUpdateIfInactive
) {
280 getBuilder().updateFromRoot();
283 getUpdater().showNotify();
285 myWasEverShown
|= byShowing
;
288 private void cancelCurrentCleanupTask() {
289 if (myCleanupTask
!= null) {
290 myCleanupTask
.cancel();
291 myCleanupTask
= null;
295 public void deactivate() {
296 getUpdater().hideNotify();
297 myBusyAlarm
.cancelAllRequests();
299 if (!myWasEverShown
) return;
301 if (isNodeActionsPending()) {
302 cancelBackgroundLoading();
303 myUpdateFromRootRequested
= true;
306 if (getClearOnHideDelay() >= 0) {
307 ourUi2Countdown
.put(this, System
.currentTimeMillis() + getClearOnHideDelay());
312 private void sheduleCleanUpAll() {
313 cancelCurrentCleanupTask();
315 myCleanupTask
= SimpleTimer
.getInstance().setUp(new Runnable() {
319 }, getClearOnHideDelay());
322 public void requestRelease() {
323 if (isReleaseRequested()) return;
325 assertIsDispatchThread();
327 myReleaseRequested
= true;
329 getUpdater().requestRelease();
334 private void releaseNow() {
335 myTree
.removeTreeExpansionListener(myExpansionListener
);
336 myTree
.removeTreeSelectionListener(mySelectionListener
);
337 myTree
.removeFocusListener(myFocusListener
);
339 disposeNode(getRootNode());
340 myElementToNodeMap
.clear();
341 getUpdater().cancelAllRequests();
342 if (myWorker
!= null) {
343 myWorker
.dispose(true);
346 TREE_NODE_WRAPPER
.setValue(null);
347 if (myProgress
!= null) {
351 cancelCurrentCleanupTask();
356 myTreeStructure
= null;
357 myBuilder
.releaseUi();
362 myDeferredSelections
.clear();
363 myDeferredExpansions
.clear();
364 myYeildingDoneRunnables
.clear();
367 public boolean isReleaseRequested() {
368 return myReleaseRequested
;
371 public boolean validateReleaseRequested() {
372 if (isReleaseRequested()) {
373 SwingUtilities
.invokeLater(new Runnable() {
384 public boolean isReleased() {
385 return myBuilder
== null;
388 protected void doExpandNodeChildren(final DefaultMutableTreeNode node
) {
389 if (!myUnbuiltNodes
.contains(node
)) return;
390 if (isLoadedInBackground(getElementFor(node
))) return;
392 if (!isReleaseRequested()) {
393 getTreeStructure().commit();
394 addSubtreeToUpdate(node
);
395 getUpdater().performUpdate();
397 processNodeActionsIfReady(node
);
401 public final AbstractTreeStructure
getTreeStructure() {
402 return myTreeStructure
;
405 public final JTree
getTree() {
410 private NodeDescriptor
getDescriptorFrom(DefaultMutableTreeNode node
) {
411 return (NodeDescriptor
)node
.getUserObject();
415 public final DefaultMutableTreeNode
getNodeForElement(Object element
, final boolean validateAgainstStructure
) {
416 DefaultMutableTreeNode result
= null;
417 if (validateAgainstStructure
) {
420 final DefaultMutableTreeNode node
= findNode(element
, index
);
421 if (node
== null) break;
423 if (isNodeValidForElement(element
, node
)) {
432 result
= getFirstNode(element
);
436 if (result
!= null && !isNodeInStructure(result
)) {
444 private boolean isNodeInStructure(DefaultMutableTreeNode node
) {
445 return TreeUtil
.isAncestor(getRootNode(), node
) && getRootNode() == myTreeModel
.getRoot();
448 private boolean isNodeValidForElement(final Object element
, final DefaultMutableTreeNode node
) {
449 return isSameHierarchy(element
, node
) || isValidChildOfParent(element
, node
);
452 private boolean isValidChildOfParent(final Object element
, final DefaultMutableTreeNode node
) {
453 final DefaultMutableTreeNode parent
= (DefaultMutableTreeNode
)node
.getParent();
454 final Object parentElement
= getElementFor(parent
);
455 if (!isInStructure(parentElement
)) return false;
457 if (parent
instanceof ElementNode
) {
458 return ((ElementNode
)parent
).isValidChild(element
);
461 for (int i
= 0; i
< parent
.getChildCount(); i
++) {
462 final TreeNode child
= parent
.getChildAt(i
);
463 final Object eachElement
= getElementFor(child
);
464 if (element
.equals(eachElement
)) return true;
471 private boolean isSameHierarchy(Object eachParent
, DefaultMutableTreeNode eachParentNode
) {
472 boolean valid
= true;
474 if (eachParent
== null) {
475 valid
= eachParentNode
== null;
479 if (!eachParent
.equals(getElementFor(eachParentNode
))) {
484 eachParent
= getTreeStructure().getParentElement(eachParent
);
485 eachParentNode
= (DefaultMutableTreeNode
)eachParentNode
.getParent();
490 public final DefaultMutableTreeNode
getNodeForPath(Object
[] path
) {
491 DefaultMutableTreeNode node
= null;
492 for (final Object pathElement
: path
) {
493 node
= node
== null ?
getFirstNode(pathElement
) : findNodeForChildElement(node
, pathElement
);
501 public final void buildNodeForElement(Object element
) {
502 getUpdater().performUpdate();
503 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
505 final java
.util
.List
<Object
> elements
= new ArrayList
<Object
>();
507 element
= getTreeStructure().getParentElement(element
);
508 if (element
== null) {
511 elements
.add(0, element
);
514 for (final Object element1
: elements
) {
515 node
= getNodeForElement(element1
, false);
523 public final void buildNodeForPath(Object
[] path
) {
524 getUpdater().performUpdate();
525 DefaultMutableTreeNode node
= null;
526 for (final Object pathElement
: path
) {
527 node
= node
== null ?
getFirstNode(pathElement
) : findNodeForChildElement(node
, pathElement
);
528 if (node
!= null && node
!= path
[path
.length
- 1]) {
534 public final void setNodeDescriptorComparator(Comparator
<NodeDescriptor
> nodeDescriptorComparator
) {
535 myNodeDescriptorComparator
= nodeDescriptorComparator
;
536 myLastComparatorStamp
= -1;
537 getBuilder().queueUpdateFrom(getTreeStructure().getRootElement(), true);
540 protected AbstractTreeBuilder
getBuilder() {
544 protected final void initRootNode() {
545 if (myUpdateIfInactive
) {
549 myUpdateFromRootRequested
= true;
553 private boolean initRootNodeNowIfNeeded(final TreeUpdatePass pass
) {
554 boolean wasCleanedUp
= false;
555 if (myRootNodeWasInitialized
) {
556 Object root
= getTreeStructure().getRootElement();
557 assert root
!= null : "Root element cannot be null";
559 Object currentRoot
= getElementFor(myRootNode
);
561 if (Comparing
.equal(root
, currentRoot
)) return false;
563 Object rootAgain
= getTreeStructure().getRootElement();
564 if (root
!= rootAgain
&& !root
.equals(rootAgain
)) {
565 assert false : "getRootElement() if called twice must return either root1 == root2 or root1.equals(root2)";
572 myRootNodeWasInitialized
= true;
574 final Object rootElement
= getTreeStructure().getRootElement();
575 addNodeAction(rootElement
, new NodeAction() {
576 public void onReady(final DefaultMutableTreeNode node
) {
577 processDeferredActions();
582 final Ref
<NodeDescriptor
> rootDescriptor
= new Ref
<NodeDescriptor
>(null);
583 final boolean bgLoading
= getTreeStructure().isToBuildChildrenInBackground(rootElement
);
585 Runnable build
= new Runnable() {
587 rootDescriptor
.set(getTreeStructure().createDescriptor(rootElement
, null));
588 getRootNode().setUserObject(rootDescriptor
.get());
589 update(rootDescriptor
.get(), true);
594 Runnable update
= new Runnable() {
596 if (getElementFromDescriptor(rootDescriptor
.get()) != null) {
597 createMapping(getElementFromDescriptor(rootDescriptor
.get()), getRootNode());
601 insertLoadingNode(getRootNode(), true);
603 boolean willUpdate
= false;
604 if (isAutoExpand(rootDescriptor
.get())) {
605 willUpdate
= myUnbuiltNodes
.contains(getRootNode());
606 expand(getRootNode(), true);
609 updateNodeChildren(getRootNode(), pass
, null, false, false, false, true);
611 if (getRootNode().getChildCount() == 0) {
612 myTreeModel
.nodeChanged(getRootNode());
618 queueToBackground(build
, update
);
628 private boolean isAutoExpand(NodeDescriptor descriptor
) {
629 return isAutoExpand(descriptor
, true);
632 private boolean isAutoExpand(NodeDescriptor descriptor
, boolean validate
) {
633 boolean autoExpand
= false;
635 if (descriptor
!= null) {
636 autoExpand
= getBuilder().isAutoExpandNode(descriptor
);
639 Object element
= getElementFromDescriptor(descriptor
);
641 autoExpand
= validateAutoExpand(autoExpand
, element
);
644 if (!autoExpand
&& !myTree
.isRootVisible()) {
645 if (element
!= null && element
.equals(getTreeStructure().getRootElement())) return true;
651 private boolean validateAutoExpand(boolean autoExpand
, Object element
) {
653 int distance
= getDistanceToAutoExpandRoot(element
);
655 myAutoExpandRoots
.add(element
);
657 if (distance
>= myAutoExpandDepth
.asInteger() - 1) {
663 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
664 if (isInVisibleAutoExpandChain(node
)) {
674 private boolean isInVisibleAutoExpandChain(DefaultMutableTreeNode child
) {
675 TreeNode eachParent
= child
;
676 while (eachParent
!= null) {
678 if (myRootNode
== eachParent
) return true;
680 NodeDescriptor eachDescriptor
= getDescriptorFrom((DefaultMutableTreeNode
)eachParent
);
681 if (!isAutoExpand(eachDescriptor
, false)) {
682 TreePath path
= getPathFor(eachParent
);
683 if (myWillBeExpaned
.contains(path
.getLastPathComponent()) || (myTree
.isExpanded(path
) && myTree
.isVisible(path
))) {
689 eachParent
= eachParent
.getParent();
695 private int getDistanceToAutoExpandRoot(Object element
) {
698 Object eachParent
= element
;
699 while (eachParent
!= null) {
700 if (myAutoExpandRoots
.contains(eachParent
)) break;
701 eachParent
= getTreeStructure().getParentElement(eachParent
);
705 return eachParent
!= null ? distance
: -1;
708 private boolean isAutoExpand(DefaultMutableTreeNode node
) {
709 return isAutoExpand(getDescriptorFrom(node
));
712 private AsyncResult
<Boolean
> update(final NodeDescriptor nodeDescriptor
, boolean now
) {
713 final AsyncResult
<Boolean
> result
= new AsyncResult
<Boolean
>();
715 if (now
|| isPassthroughMode()) {
716 return new AsyncResult
<Boolean
>().setDone(_update(nodeDescriptor
));
719 Object element
= getElementFromDescriptor(nodeDescriptor
);
720 boolean bgLoading
= getTreeStructure().isToBuildChildrenInBackground(element
);
722 boolean edt
= isEdt();
725 final Ref
<Boolean
> changes
= new Ref
<Boolean
>(false);
726 queueToBackground(new Runnable() {
728 changes
.set(_update(nodeDescriptor
));
732 result
.setDone(changes
.get());
737 result
.setDone(_update(nodeDescriptor
));
741 if (edt
|| !myWasEverShown
) {
742 result
.setDone(_update(nodeDescriptor
));
745 UIUtil
.invokeLaterIfNeeded(new Runnable() {
747 if (!validateReleaseRequested()) {
748 result
.setDone(_update(nodeDescriptor
));
751 result
.setRejected();
758 result
.doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
759 public void run(Boolean changes
) {
761 final long updateStamp
= nodeDescriptor
.getUpdateCount();
762 UIUtil
.invokeLaterIfNeeded(new Runnable() {
764 Object element
= nodeDescriptor
.getElement();
765 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
767 TreePath path
= getPathFor(node
);
768 if (path
!= null && myTree
.isVisible(path
)) {
769 updateNodeImageAndPosition(node
, false);
782 private boolean _update(NodeDescriptor nodeDescriptor
) {
784 nodeDescriptor
.setUpdateCount(nodeDescriptor
.getUpdateCount() + 1);
785 return getBuilder().updateNodeDescriptor(nodeDescriptor
);
787 catch (IndexNotReadyException e
) {
788 warnOnIndexNotReady();
793 private void assertIsDispatchThread() {
794 if (isPassthroughMode()) return;
796 if (isTreeShowing() && !isEdt()) {
797 LOG
.error("Must be in event-dispatch thread");
801 private boolean isEdt() {
802 return SwingUtilities
.isEventDispatchThread();
805 private boolean isTreeShowing() {
809 private void assertNotDispatchThread() {
810 if (isPassthroughMode()) return;
813 LOG
.error("Must not be in event-dispatch thread");
817 private void processDeferredActions() {
818 processDeferredActions(myDeferredSelections
);
819 processDeferredActions(myDeferredExpansions
);
822 private void processDeferredActions(Set
<Runnable
> actions
) {
823 final Runnable
[] runnables
= actions
.toArray(new Runnable
[actions
.size()]);
825 for (Runnable runnable
: runnables
) {
830 //todo: to make real callback
831 public ActionCallback
queueUpdate(Object element
) {
832 AbstractTreeUpdater updater
= getUpdater();
833 if (updater
== null) {
834 return new ActionCallback
.Rejected();
837 final ActionCallback result
= new ActionCallback();
838 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
840 addSubtreeToUpdate(node
);
843 addSubtreeToUpdate(getRootNode());
846 updater
.runAfterUpdate(new Runnable() {
854 public void doUpdateFromRoot() {
855 updateSubtree(getRootNode(), false);
858 public ActionCallback
doUpdateFromRootCB() {
859 final ActionCallback cb
= new ActionCallback();
860 getUpdater().runAfterUpdate(new Runnable() {
865 updateSubtree(getRootNode(), false);
869 public final void updateSubtree(DefaultMutableTreeNode node
, boolean canSmartExpand
) {
870 updateSubtree(new TreeUpdatePass(node
), canSmartExpand
);
873 public final void updateSubtree(TreeUpdatePass pass
, boolean canSmartExpand
) {
874 if (getUpdater() != null) {
875 getUpdater().addSubtreeToUpdate(pass
);
878 updateSubtreeNow(pass
, canSmartExpand
);
882 final void updateSubtreeNow(TreeUpdatePass pass
, boolean canSmartExpand
) {
883 maybeSetBusyAndScheduleWaiterForReady(true);
885 boolean consumed
= initRootNodeNowIfNeeded(pass
);
886 if (consumed
) return;
888 final DefaultMutableTreeNode node
= pass
.getNode();
890 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
892 setUpdaterState(new UpdaterTreeState(this)).beforeSubtreeUpdate();
894 boolean forceUpdate
= true;
895 TreePath path
= getPathFor(node
);
896 boolean invisible
= !myTree
.isExpanded(path
) && (path
.getParentPath() == null || !myTree
.isExpanded(path
.getParentPath()));
898 if (invisible
&& myUnbuiltNodes
.contains(node
)) {
902 updateNodeChildren(node
, pass
, null, false, canSmartExpand
, forceUpdate
, false);
905 private boolean isToBuildInBackground(NodeDescriptor descriptor
) {
906 return getTreeStructure().isToBuildChildrenInBackground(getBuilder().getTreeStructureElement(descriptor
));
910 UpdaterTreeState
setUpdaterState(UpdaterTreeState state
) {
911 if (myUpdaterState
!= null && myUpdaterState
.equals(state
)) return state
;
913 final UpdaterTreeState oldState
= myUpdaterState
;
914 if (oldState
== null) {
915 myUpdaterState
= state
;
919 oldState
.addAll(state
);
924 protected void doUpdateNode(final DefaultMutableTreeNode node
) {
925 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
926 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
927 final Object prevElement
= getElementFromDescriptor(descriptor
);
928 if (prevElement
== null) return;
929 update(descriptor
, false).doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
930 public void run(Boolean changes
) {
931 if (!isValid(descriptor
)) {
932 if (isInStructure(prevElement
)) {
933 getUpdater().addSubtreeToUpdateByElement(getTreeStructure().getParentElement(prevElement
));
938 updateNodeImageAndPosition(node
, true);
944 public Object
getElementFromDescriptor(NodeDescriptor descriptor
) {
945 return getBuilder().getTreeStructureElement(descriptor
);
948 private void updateNodeChildren(final DefaultMutableTreeNode node
,
949 final TreeUpdatePass pass
,
950 @Nullable LoadedChildren loadedChildren
,
952 final boolean toSmartExpand
,
954 final boolean descriptorIsUpToDate
) {
956 getTreeStructure().commit();
959 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
960 if (descriptor
== null) {
961 removeLoading(node
, true);
965 final boolean wasExpanded
= myTree
.isExpanded(new TreePath(node
.getPath())) || isAutoExpand(node
);
966 final boolean wasLeaf
= node
.getChildCount() == 0;
969 boolean bgBuild
= isToBuildInBackground(descriptor
);
970 boolean notRequiredToUpdateChildren
= !forcedNow
&& !wasExpanded
;
972 if (notRequiredToUpdateChildren
&& forceUpdate
&& !wasExpanded
) {
973 boolean alwaysPlus
= getBuilder().isAlwaysShowPlus(descriptor
);
974 if (alwaysPlus
&& wasLeaf
) {
975 notRequiredToUpdateChildren
= false;
977 notRequiredToUpdateChildren
= alwaysPlus
;
981 final Ref
<LoadedChildren
> preloaded
= new Ref
<LoadedChildren
>(loadedChildren
);
982 boolean descriptorWasUpdated
= descriptorIsUpToDate
;
984 if (notRequiredToUpdateChildren
) {
985 if (myUnbuiltNodes
.contains(node
) && node
.getChildCount() == 0) {
986 insertLoadingNode(node
, true);
993 if (myUnbuiltNodes
.contains(node
)) {
994 if (!descriptorWasUpdated
) {
995 update(descriptor
, true);
996 descriptorWasUpdated
= true;
999 if (processAlwaysLeaf(node
)) return;
1001 Pair
<Boolean
, LoadedChildren
> unbuilt
= processUnbuilt(node
, descriptor
, pass
, wasExpanded
, null);
1002 if (unbuilt
.getFirst()) return;
1003 preloaded
.set(unbuilt
.getSecond());
1009 final boolean childForceUpdate
= isChildNodeForceUpdate(node
, forceUpdate
, wasExpanded
);
1011 if (!forcedNow
&& isToBuildInBackground(descriptor
)) {
1012 if (processAlwaysLeaf(node
)) return;
1014 queueBackgroundUpdate(
1015 new UpdateInfo(descriptor
, pass
, canSmartExpand(node
, toSmartExpand
), wasExpanded
, childForceUpdate
, descriptorWasUpdated
), node
);
1019 if (!descriptorWasUpdated
) {
1020 update(descriptor
, false).doWhenDone(new Runnable() {
1022 if (processAlwaysLeaf(node
)) return;
1023 updateNodeChildrenNow(node
, pass
, preloaded
.get(), toSmartExpand
, wasExpanded
, wasLeaf
, childForceUpdate
);
1028 if (processAlwaysLeaf(node
)) return;
1030 updateNodeChildrenNow(node
, pass
, preloaded
.get(), toSmartExpand
, wasExpanded
, wasLeaf
, childForceUpdate
);
1035 processNodeActionsIfReady(node
);
1039 private boolean processAlwaysLeaf(DefaultMutableTreeNode node
) {
1040 Object element
= getElementFor(node
);
1041 NodeDescriptor desc
= getDescriptorFrom(node
);
1043 if (desc
== null) return false;
1045 if (getTreeStructure().isAlwaysLeaf(element
)) {
1046 removeLoading(node
, true);
1048 if (node
.getChildCount() > 0) {
1049 final TreeNode
[] children
= new TreeNode
[node
.getChildCount()];
1050 for (int i
= 0; i
< node
.getChildCount(); i
++) {
1051 children
[i
] = node
.getChildAt(i
);
1054 if (isSelectionInside(node
)) {
1055 addSelectionPath(getPathFor(node
), true, Condition
.TRUE
, null);
1058 processInnerChange(new Runnable() {
1060 for (TreeNode each
: children
) {
1061 removeNodeFromParent((MutableTreeNode
)each
, true);
1062 disposeNode((DefaultMutableTreeNode
)each
);
1068 removeFromUnbuilt(node
);
1069 desc
.setWasDeclaredAlwaysLeaf(true);
1070 processNodeActionsIfReady(node
);
1073 boolean wasLeaf
= desc
.isWasDeclaredAlwaysLeaf();
1074 desc
.setWasDeclaredAlwaysLeaf(false);
1077 insertLoadingNode(node
, true);
1084 private boolean isChildNodeForceUpdate(DefaultMutableTreeNode node
, boolean parentForceUpdate
, boolean parentExpanded
) {
1085 TreePath path
= getPathFor(node
);
1086 return parentForceUpdate
&& (parentExpanded
|| myTree
.isExpanded(path
));
1089 private void updateNodeChildrenNow(final DefaultMutableTreeNode node
,
1090 final TreeUpdatePass pass
,
1091 final LoadedChildren preloadedChildren
,
1092 final boolean toSmartExpand
,
1093 final boolean wasExpanded
,
1094 final boolean wasLeaf
,
1095 final boolean forceUpdate
) {
1096 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
1098 final MutualMap
<Object
, Integer
> elementToIndexMap
= loadElementsFromStructure(descriptor
, preloadedChildren
);
1099 final LoadedChildren loadedChildren
=
1100 preloadedChildren
!= null ? preloadedChildren
: new LoadedChildren(elementToIndexMap
.getKeys().toArray());
1103 addToUpdating(node
);
1104 pass
.setCurrentNode(node
);
1106 final boolean canSmartExpand
= canSmartExpand(node
, toSmartExpand
);
1108 processExistingNodes(node
, elementToIndexMap
, pass
, canSmartExpand(node
, toSmartExpand
), forceUpdate
, wasExpanded
, preloadedChildren
)
1109 .doWhenDone(new Runnable() {
1111 if (isDisposed(node
)) {
1112 removeFromUpdating(node
);
1116 removeLoading(node
, false);
1118 final boolean expanded
= isExpanded(node
, wasExpanded
);
1121 myWillBeExpaned
.add(node
);
1123 myWillBeExpaned
.remove(node
);
1126 collectNodesToInsert(descriptor
, elementToIndexMap
, node
, expanded
, loadedChildren
)
1127 .doWhenDone(new AsyncResult
.Handler
<ArrayList
<TreeNode
>>() {
1128 public void run(ArrayList
<TreeNode
> nodesToInsert
) {
1129 insertNodesInto(nodesToInsert
, node
);
1130 updateNodesToInsert(nodesToInsert
, pass
, canSmartExpand
, isChildNodeForceUpdate(node
, forceUpdate
, expanded
));
1131 removeLoading(node
, true);
1132 removeFromUpdating(node
);
1134 if (node
.getChildCount() > 0) {
1136 expand(node
, canSmartExpand
);
1140 final Object element
= getElementFor(node
);
1141 addNodeAction(element
, new NodeAction() {
1142 public void onReady(final DefaultMutableTreeNode node
) {
1143 removeLoading(node
, false);
1147 processNodeActionsIfReady(node
);
1149 }).doWhenProcessed(new Runnable() {
1151 myWillBeExpaned
.remove(node
);
1152 removeFromUpdating(node
);
1153 processNodeActionsIfReady(node
);
1157 }).doWhenRejected(new Runnable() {
1159 removeFromUpdating(node
);
1160 processNodeActionsIfReady(node
);
1165 private boolean isDisposed(DefaultMutableTreeNode node
) {
1166 return !node
.isNodeAncestor((DefaultMutableTreeNode
)myTree
.getModel().getRoot());
1169 private void expand(DefaultMutableTreeNode node
, boolean canSmartExpand
) {
1170 expand(new TreePath(node
.getPath()), canSmartExpand
);
1173 private void expand(final TreePath path
, boolean canSmartExpand
) {
1174 if (path
== null) return;
1177 final Object last
= path
.getLastPathComponent();
1178 boolean isLeaf
= myTree
.getModel().isLeaf(path
.getLastPathComponent());
1179 final boolean isRoot
= last
== myTree
.getModel().getRoot();
1180 final TreePath parent
= path
.getParentPath();
1181 if (isRoot
&& !myTree
.isExpanded(path
)) {
1182 if (myTree
.isRootVisible() || myUnbuiltNodes
.contains(last
)) {
1183 insertLoadingNode((DefaultMutableTreeNode
)last
, false);
1185 expandPath(path
, canSmartExpand
);
1187 else if (myTree
.isExpanded(path
) || (isLeaf
&& parent
!= null && myTree
.isExpanded(parent
) && !myUnbuiltNodes
.contains(last
))) {
1188 if (last
instanceof DefaultMutableTreeNode
) {
1189 processNodeActionsIfReady((DefaultMutableTreeNode
)last
);
1193 if (isLeaf
&& myUnbuiltNodes
.contains(last
)) {
1194 insertLoadingNode((DefaultMutableTreeNode
)last
, true);
1195 expandPath(path
, canSmartExpand
);
1197 else if (isLeaf
&& parent
!= null) {
1198 final DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)parent
.getLastPathComponent();
1199 if (parentNode
!= null) {
1200 addToUnbuilt(parentNode
);
1202 expandPath(parent
, canSmartExpand
);
1205 expandPath(path
, canSmartExpand
);
1210 private void addToUnbuilt(DefaultMutableTreeNode node
) {
1211 myUnbuiltNodes
.add(node
);
1214 private void removeFromUnbuilt(DefaultMutableTreeNode node
) {
1215 myUnbuiltNodes
.remove(node
);
1218 private Pair
<Boolean
, LoadedChildren
> processUnbuilt(final DefaultMutableTreeNode node
,
1219 final NodeDescriptor descriptor
,
1220 final TreeUpdatePass pass
,
1222 final LoadedChildren loadedChildren
) {
1223 if (!isExpanded
&& getBuilder().isAlwaysShowPlus(descriptor
)) {
1224 return new Pair
<Boolean
, LoadedChildren
>(true, null);
1227 final Object element
= getElementFor(node
);
1229 final LoadedChildren children
= loadedChildren
!= null ? loadedChildren
: new LoadedChildren(getChildrenFor(element
));
1233 if (children
.getElements().size() == 0) {
1234 removeLoading(node
, true);
1238 if (isAutoExpand(node
)) {
1239 addNodeAction(getElementFor(node
), new NodeAction() {
1240 public void onReady(final DefaultMutableTreeNode node
) {
1241 final TreePath path
= new TreePath(node
.getPath());
1242 if (getTree().isExpanded(path
) || children
.getElements().size() == 0) {
1243 removeLoading(node
, false);
1246 maybeYeild(new ActiveRunnable() {
1247 public ActionCallback
run() {
1248 expand(element
, null);
1249 return new ActionCallback
.Done();
1259 processNodeActionsIfReady(node
);
1261 return new Pair
<Boolean
, LoadedChildren
>(processed
, children
);
1264 private boolean removeIfLoading(TreeNode node
) {
1265 if (isLoadingNode(node
)) {
1266 moveSelectionToParentIfNeeded(node
);
1267 removeNodeFromParent((MutableTreeNode
)node
, false);
1274 private void moveSelectionToParentIfNeeded(TreeNode node
) {
1275 TreePath path
= getPathFor(node
);
1276 if (myTree
.getSelectionModel().isPathSelected(path
)) {
1277 TreePath parentPath
= path
.getParentPath();
1278 myTree
.getSelectionModel().removeSelectionPath(path
);
1279 if (parentPath
!= null) {
1280 myTree
.getSelectionModel().addSelectionPath(parentPath
);
1285 //todo [kirillk] temporary consistency check
1286 private Object
[] getChildrenFor(final Object element
) {
1287 final Object
[] passOne
;
1289 passOne
= getTreeStructure().getChildElements(element
);
1291 catch (IndexNotReadyException e
) {
1292 warnOnIndexNotReady();
1293 return ArrayUtil
.EMPTY_OBJECT_ARRAY
;
1296 if (!myCheckStructure
) return passOne
;
1298 final Object
[] passTwo
= getTreeStructure().getChildElements(element
);
1300 final HashSet two
= new HashSet(Arrays
.asList(passTwo
));
1302 if (passOne
.length
!= passTwo
.length
) {
1304 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
1308 for (Object eachInOne
: passOne
) {
1309 if (!two
.contains(eachInOne
)) {
1311 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
1321 private void warnOnIndexNotReady() {
1322 if (!myWasEverIndexNotReady
) {
1323 myWasEverIndexNotReady
= true;
1324 LOG
.warn("Tree is not dumb-mode-aware; treeBuilder=" + getBuilder() + " treeStructure=" + getTreeStructure());
1328 private void updateNodesToInsert(final ArrayList
<TreeNode
> nodesToInsert
,
1329 TreeUpdatePass pass
,
1330 boolean canSmartExpand
,
1331 boolean forceUpdate
) {
1332 for (TreeNode aNodesToInsert
: nodesToInsert
) {
1333 DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)aNodesToInsert
;
1334 updateNodeChildren(childNode
, pass
, null, false, canSmartExpand
, forceUpdate
, true);
1338 private ActionCallback
processExistingNodes(final DefaultMutableTreeNode node
,
1339 final MutualMap
<Object
, Integer
> elementToIndexMap
,
1340 final TreeUpdatePass pass
,
1341 final boolean canSmartExpand
,
1342 final boolean forceUpdate
,
1343 final boolean wasExpaned
,
1344 final LoadedChildren preloaded
) {
1346 final ArrayList
<TreeNode
> childNodes
= TreeUtil
.childrenToArray(node
);
1347 return maybeYeild(new ActiveRunnable() {
1348 public ActionCallback
run() {
1349 if (pass
.isExpired()) return new ActionCallback
.Rejected();
1350 if (childNodes
.size() == 0) return new ActionCallback
.Done();
1353 final ActionCallback result
= new ActionCallback(childNodes
.size());
1355 for (TreeNode each
: childNodes
) {
1356 final DefaultMutableTreeNode eachChild
= (DefaultMutableTreeNode
)each
;
1357 if (isLoadingNode(eachChild
)) {
1362 final boolean childForceUpdate
= isChildNodeForceUpdate(eachChild
, forceUpdate
, wasExpaned
);
1364 maybeYeild(new ActiveRunnable() {
1366 public ActionCallback
run() {
1367 return processExistingNode(eachChild
, getDescriptorFrom(eachChild
), node
, elementToIndexMap
, pass
, canSmartExpand
,
1368 childForceUpdate
, preloaded
);
1370 }, pass
, node
).notify(result
);
1372 if (result
.isRejected()) {
1382 private boolean isRerunNeeded(TreeUpdatePass pass
) {
1383 if (pass
.isExpired()) return false;
1385 final boolean rerunBecauseTreeIsHidden
= !pass
.isExpired() && !isTreeShowing() && getUpdater().isInPostponeMode();
1387 return rerunBecauseTreeIsHidden
|| getUpdater().isRerunNeededFor(pass
);
1390 private ActionCallback
maybeYeild(final ActiveRunnable processRunnable
, final TreeUpdatePass pass
, final DefaultMutableTreeNode node
) {
1391 final ActionCallback result
= new ActionCallback();
1393 if (isRerunNeeded(pass
)) {
1394 getUpdater().addSubtreeToUpdate(pass
);
1395 result
.setRejected();
1398 if (isToYieldUpdateFor(node
)) {
1399 pass
.setCurrentNode(node
);
1400 boolean wasRun
= yieldAndRun(new Runnable() {
1402 if (validateReleaseRequested()) {
1403 result
.setRejected();
1407 if (pass
.isExpired()) {
1408 result
.setRejected();
1412 if (isRerunNeeded(pass
)) {
1413 runDone(new Runnable() {
1415 if (!pass
.isExpired()) {
1416 getUpdater().addSubtreeToUpdate(pass
);
1420 result
.setRejected();
1424 processRunnable
.run().notify(result
);
1426 catch (ProcessCanceledException e
) {
1428 result
.setRejected();
1434 result
.setRejected();
1439 processRunnable
.run().notify(result
);
1441 catch (ProcessCanceledException e
) {
1443 result
.setRejected();
1451 private boolean yieldAndRun(final Runnable runnable
, final TreeUpdatePass pass
) {
1452 if (validateReleaseRequested()) return false;
1454 myYeildingPasses
.add(pass
);
1455 myYeildingNow
= true;
1456 yield(new Runnable() {
1458 runOnYieldingDone(new Runnable() {
1460 executeYieldingRequest(runnable
, pass
);
1469 public boolean isYeildingNow() {
1470 return myYeildingNow
;
1473 private boolean hasSheduledUpdates() {
1474 return getUpdater().hasNodesToUpdate() || isLoadingInBackgroundNow();
1477 public boolean isReady() {
1478 return isIdle() && !hasPendingWork() && !isNodeActionsPending();
1481 public boolean hasPendingWork() {
1482 return hasNodesToUpdate() || (myUpdaterState
!= null && myUpdaterState
.isProcessingNow());
1485 public boolean isIdle() {
1486 return !isYeildingNow() && !isWorkerBusy() && (!hasSheduledUpdates() || getUpdater().isInPostponeMode());
1489 private void executeYieldingRequest(Runnable runnable
, TreeUpdatePass pass
) {
1491 myYeildingPasses
.remove(pass
);
1495 maybeYeildingFinished();
1499 private void maybeYeildingFinished() {
1500 if (myYeildingPasses
.size() == 0) {
1501 myYeildingNow
= false;
1502 flushPendingNodeActions();
1507 if (isReleased()) return;
1510 if (isReleaseRequested()) {
1515 if (myTree
.isShowing() || myUpdateIfInactive
) {
1516 myInitialized
.setDone();
1520 if (myUpdaterState
!= null && !myUpdaterState
.isProcessingNow()) {
1521 UpdaterTreeState oldState
= myUpdaterState
;
1522 if (!myUpdaterState
.restore(null)) {
1523 setUpdaterState(oldState
);
1531 if (myTree
.isShowing()) {
1532 if (getBuilder().isToEnsureSelectionOnFocusGained() && Registry
.is("ide.tree.ensureSelectionOnFocusGained")) {
1533 TreeUtil
.ensureSelection(myTree
);
1537 if (myInitialized
.isDone()) {
1538 for (ActionCallback each
: getReadyCallbacks(true)) {
1545 private void flushPendingNodeActions() {
1546 final DefaultMutableTreeNode
[] nodes
= myPendingNodeActions
.toArray(new DefaultMutableTreeNode
[myPendingNodeActions
.size()]);
1547 myPendingNodeActions
.clear();
1549 for (DefaultMutableTreeNode each
: nodes
) {
1550 processNodeActionsIfReady(each
);
1553 final Runnable
[] actions
= myYeildingDoneRunnables
.toArray(new Runnable
[myYeildingDoneRunnables
.size()]);
1554 for (Runnable each
: actions
) {
1555 if (!isYeildingNow()) {
1556 myYeildingDoneRunnables
.remove(each
);
1564 protected void runOnYieldingDone(Runnable onDone
) {
1565 getBuilder().runOnYeildingDone(onDone
);
1568 protected void yield(Runnable runnable
) {
1569 getBuilder().yield(runnable
);
1572 private boolean isToYieldUpdateFor(final DefaultMutableTreeNode node
) {
1573 if (!canYield()) return false;
1574 return getBuilder().isToYieldUpdateFor(node
);
1577 private MutualMap
<Object
, Integer
> loadElementsFromStructure(final NodeDescriptor descriptor
,
1578 @Nullable LoadedChildren preloadedChildren
) {
1579 MutualMap
<Object
, Integer
> elementToIndexMap
= new MutualMap
<Object
, Integer
>(true);
1580 List children
= preloadedChildren
!= null
1581 ? preloadedChildren
.getElements()
1582 : Arrays
.asList(getChildrenFor(getBuilder().getTreeStructureElement(descriptor
)));
1584 for (Object child
: children
) {
1585 if (!isValid(child
)) continue;
1586 elementToIndexMap
.put(child
, Integer
.valueOf(index
));
1589 return elementToIndexMap
;
1592 private void expand(final DefaultMutableTreeNode node
,
1593 final NodeDescriptor descriptor
,
1594 final boolean wasLeaf
,
1595 final boolean canSmartExpand
) {
1596 final Alarm alarm
= new Alarm(Alarm
.ThreadToUse
.SHARED_THREAD
);
1597 alarm
.addRequest(new Runnable() {
1599 myTree
.setCursor(Cursor
.getPredefinedCursor(Cursor
.WAIT_CURSOR
));
1601 }, WAIT_CURSOR_DELAY
);
1603 if (wasLeaf
&& isAutoExpand(descriptor
)) {
1604 expand(node
, canSmartExpand
);
1607 ArrayList
<TreeNode
> nodes
= TreeUtil
.childrenToArray(node
);
1608 for (TreeNode node1
: nodes
) {
1609 final DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)node1
;
1610 if (isLoadingNode(childNode
)) continue;
1611 NodeDescriptor childDescr
= getDescriptorFrom(childNode
);
1612 if (isAutoExpand(childDescr
)) {
1613 addNodeAction(getElementFor(childNode
), new NodeAction() {
1614 public void onReady(DefaultMutableTreeNode node
) {
1615 expand(childNode
, canSmartExpand
);
1618 addSubtreeToUpdate(childNode
);
1622 int n
= alarm
.cancelAllRequests();
1624 myTree
.setCursor(Cursor
.getDefaultCursor());
1628 public static boolean isLoadingNode(final Object node
) {
1629 return node
instanceof LoadingNode
;
1632 private AsyncResult
<ArrayList
<TreeNode
>> collectNodesToInsert(final NodeDescriptor descriptor
,
1633 final MutualMap
<Object
, Integer
> elementToIndexMap
,
1634 final DefaultMutableTreeNode parent
,
1635 final boolean addLoadingNode
,
1636 @NotNull final LoadedChildren loadedChildren
) {
1637 final AsyncResult
<ArrayList
<TreeNode
>> result
= new AsyncResult
<ArrayList
<TreeNode
>>();
1639 final ArrayList
<TreeNode
> nodesToInsert
= new ArrayList
<TreeNode
>();
1640 final Collection
<Object
> allElements
= elementToIndexMap
.getKeys();
1642 final ActionCallback processingDone
= new ActionCallback(allElements
.size());
1644 for (final Object child
: allElements
) {
1645 Integer index
= elementToIndexMap
.getValue(child
);
1646 final Ref
<NodeDescriptor
> childDescr
= new Ref
<NodeDescriptor
>(loadedChildren
.getDescriptor(child
));
1647 boolean needToUpdate
= false;
1648 if (childDescr
.get() == null) {
1649 childDescr
.set(getTreeStructure().createDescriptor(child
, descriptor
));
1650 needToUpdate
= true;
1653 if (childDescr
.get() == null) {
1654 processingDone
.setDone();
1657 childDescr
.get().setIndex(index
.intValue());
1659 final ActionCallback update
= new ActionCallback();
1661 update(childDescr
.get(), false).doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
1662 public void run(Boolean changes
) {
1663 loadedChildren
.putDescriptor(child
, childDescr
.get(), changes
);
1672 update
.doWhenDone(new Runnable() {
1674 Object element
= getElementFromDescriptor(childDescr
.get());
1675 if (element
== null) {
1676 processingDone
.setDone();
1679 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
1680 if (node
== null || node
.getParent() != parent
) {
1681 final DefaultMutableTreeNode childNode
= createChildNode(childDescr
.get());
1682 if (addLoadingNode
|| getBuilder().isAlwaysShowPlus(childDescr
.get())) {
1683 insertLoadingNode(childNode
, true);
1686 addToUnbuilt(childNode
);
1688 nodesToInsert
.add(childNode
);
1689 createMapping(element
, childNode
);
1691 processingDone
.setDone();
1697 processingDone
.doWhenDone(new Runnable() {
1699 result
.setDone(nodesToInsert
);
1706 protected DefaultMutableTreeNode
createChildNode(final NodeDescriptor descriptor
) {
1707 return new ElementNode(this, descriptor
);
1710 protected boolean canYield() {
1711 return myCanYield
&& myYeildingUpdate
.asBoolean();
1714 public long getClearOnHideDelay() {
1715 return myClearOnHideDelay
> 0 ? myClearOnHideDelay
: Registry
.intValue("ide.tree.clearOnHideTime");
1718 public ActionCallback
getInitialized() {
1719 return myInitialized
;
1722 public ActionCallback
getReady(Object requestor
) {
1724 return new ActionCallback
.Done();
1727 return addReadyCallback(requestor
);
1731 private void addToUpdating(DefaultMutableTreeNode node
) {
1732 synchronized (myUpdatingChildren
) {
1733 myUpdatingChildren
.add(node
);
1737 private void removeFromUpdating(DefaultMutableTreeNode node
) {
1738 synchronized (myUpdatingChildren
) {
1739 myUpdatingChildren
.remove(node
);
1743 public boolean isUpdatingNow(DefaultMutableTreeNode node
) {
1744 synchronized (myUpdatingChildren
) {
1745 return myUpdatingChildren
.contains(node
);
1749 boolean hasUpdatingNow() {
1750 synchronized (myUpdatingChildren
) {
1751 return myUpdatingChildren
.size() > 0;
1755 public Map
getNodeActions() {
1756 return myNodeActions
;
1759 public List
<Object
> getLoadedChildrenFor(Object element
) {
1760 List
<Object
> result
= new ArrayList
<Object
>();
1762 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)getNodeForElement(element
, false);
1764 for (int i
= 0; i
< node
.getChildCount(); i
++) {
1765 TreeNode each
= node
.getChildAt(i
);
1766 if (isLoadingNode(each
)) continue;
1768 result
.add(getElementFor(each
));
1775 public boolean hasNodesToUpdate() {
1776 return getUpdater().hasNodesToUpdate() || hasUpdatingNow() || isLoadingInBackgroundNow();
1779 public List
<Object
> getExpandedElements() {
1780 List
<Object
> result
= new ArrayList
<Object
>();
1781 Enumeration
<TreePath
> enumeration
= myTree
.getExpandedDescendants(getPathFor(getRootNode()));
1782 while (enumeration
.hasMoreElements()) {
1783 TreePath each
= enumeration
.nextElement();
1784 Object eachElement
= getElementFor(each
.getLastPathComponent());
1785 if (eachElement
!= null) {
1786 result
.add(eachElement
);
1793 static class ElementNode
extends DefaultMutableTreeNode
{
1795 Set
<Object
> myElements
= new HashSet
<Object
>();
1796 AbstractTreeUi myUi
;
1798 ElementNode(AbstractTreeUi ui
, NodeDescriptor descriptor
) {
1804 public void insert(final MutableTreeNode newChild
, final int childIndex
) {
1805 super.insert(newChild
, childIndex
);
1806 final Object element
= myUi
.getElementFor(newChild
);
1807 if (element
!= null) {
1808 myElements
.add(element
);
1813 public void remove(final int childIndex
) {
1814 final TreeNode node
= getChildAt(childIndex
);
1815 super.remove(childIndex
);
1816 final Object element
= myUi
.getElementFor(node
);
1817 if (element
!= null) {
1818 myElements
.remove(element
);
1822 boolean isValidChild(Object childElement
) {
1823 return myElements
.contains(childElement
);
1827 public String
toString() {
1828 return String
.valueOf(getUserObject());
1832 private boolean isUpdatingParent(DefaultMutableTreeNode kid
) {
1833 return getUpdatingParent(kid
) != null;
1836 private DefaultMutableTreeNode
getUpdatingParent(DefaultMutableTreeNode kid
) {
1837 DefaultMutableTreeNode eachParent
= kid
;
1838 while (eachParent
!= null) {
1839 if (isUpdatingNow(eachParent
)) return eachParent
;
1840 eachParent
= (DefaultMutableTreeNode
)eachParent
.getParent();
1846 private boolean isLoadedInBackground(Object element
) {
1847 return getLoadedInBackground(element
) != null;
1850 private UpdateInfo
getLoadedInBackground(Object element
) {
1851 synchronized (myLoadedInBackground
) {
1852 return myLoadedInBackground
.get(element
);
1856 private void addToLoadedInBackground(Object element
, UpdateInfo info
) {
1857 synchronized (myLoadedInBackground
) {
1858 myLoadedInBackground
.put(element
, info
);
1862 private void removeFromLoadedInBackground(final Object element
) {
1863 synchronized (myLoadedInBackground
) {
1864 myLoadedInBackground
.remove(element
);
1868 private boolean isLoadingInBackgroundNow() {
1869 synchronized (myLoadedInBackground
) {
1870 return myLoadedInBackground
.size() > 0;
1874 private boolean queueBackgroundUpdate(final UpdateInfo updateInfo
, final DefaultMutableTreeNode node
) {
1875 assertIsDispatchThread();
1877 if (validateReleaseRequested()) return false;
1879 final Object oldElementFromDescriptor
= getElementFromDescriptor(updateInfo
.getDescriptor());
1881 UpdateInfo loaded
= getLoadedInBackground(oldElementFromDescriptor
);
1882 if (loaded
!= null) {
1883 loaded
.apply(updateInfo
);
1887 addToLoadedInBackground(oldElementFromDescriptor
, updateInfo
);
1889 if (!isNodeBeingBuilt(node
)) {
1890 LoadingNode loadingNode
= new LoadingNode(getLoadingNodeText());
1891 myTreeModel
.insertNodeInto(loadingNode
, node
, node
.getChildCount());
1894 final Ref
<LoadedChildren
> children
= new Ref
<LoadedChildren
>();
1895 final Ref
<Object
> elementFromDescriptor
= new Ref
<Object
>();
1897 final DefaultMutableTreeNode
[] nodeToProcessActions
= new DefaultMutableTreeNode
[1];
1899 final Runnable finalizeRunnable
= new Runnable() {
1901 removeLoading(node
, true);
1902 removeFromLoadedInBackground(elementFromDescriptor
.get());
1903 removeFromLoadedInBackground(oldElementFromDescriptor
);
1905 if (nodeToProcessActions
[0] != null) {
1906 processNodeActionsIfReady(nodeToProcessActions
[0]);
1912 Runnable buildRunnable
= new Runnable() {
1914 if (updateInfo
.getPass().isExpired()) {
1915 finalizeRunnable
.run();
1920 if (!updateInfo
.isDescriptorIsUpToDate()) {
1921 update(updateInfo
.getDescriptor(), true);
1924 Object element
= getElementFromDescriptor(updateInfo
.getDescriptor());
1925 if (element
== null) {
1926 removeFromLoadedInBackground(oldElementFromDescriptor
);
1927 finalizeRunnable
.run();
1931 elementFromDescriptor
.set(element
);
1933 Object
[] loadedElements
= getChildrenFor(getBuilder().getTreeStructureElement(updateInfo
.getDescriptor()));
1934 LoadedChildren loaded
= new LoadedChildren(loadedElements
);
1935 for (Object each
: loadedElements
) {
1936 NodeDescriptor eachChildDescriptor
= getTreeStructure().createDescriptor(each
, updateInfo
.getDescriptor());
1937 loaded
.putDescriptor(each
, eachChildDescriptor
, update(eachChildDescriptor
, true).getResult());
1940 children
.set(loaded
);
1944 Runnable updateRunnable
= new Runnable() {
1946 if (updateInfo
.getPass().isExpired()) {
1947 finalizeRunnable
.run();
1951 if (children
.get() == null) {
1952 finalizeRunnable
.run();
1956 if (isRerunNeeded(updateInfo
.getPass())) {
1957 removeFromLoadedInBackground(elementFromDescriptor
.get());
1958 getUpdater().addSubtreeToUpdate(updateInfo
.getPass());
1962 removeFromLoadedInBackground(elementFromDescriptor
.get());
1964 if (myUnbuiltNodes
.contains(node
)) {
1965 Pair
<Boolean
, LoadedChildren
> unbuilt
=
1966 processUnbuilt(node
, updateInfo
.getDescriptor(), updateInfo
.getPass(), isExpanded(node
, updateInfo
.isWasExpanded()),
1968 if (unbuilt
.getFirst()) {
1969 nodeToProcessActions
[0] = node
;
1974 updateNodeChildren(node
, updateInfo
.getPass(), children
.get(), true, updateInfo
.isCanSmartExpand(), updateInfo
.isForceUpdate(),
1978 if (isRerunNeeded(updateInfo
.getPass())) {
1979 getUpdater().addSubtreeToUpdate(updateInfo
.getPass());
1983 Object element
= elementFromDescriptor
.get();
1985 if (element
!= null) {
1986 removeLoading(node
, true);
1987 nodeToProcessActions
[0] = node
;
1991 queueToBackground(buildRunnable
, updateRunnable
).doWhenProcessed(finalizeRunnable
).doWhenRejected(new Runnable() {
1993 updateInfo
.getPass().expire();
2000 private boolean isExpanded(DefaultMutableTreeNode node
, boolean isExpanded
) {
2001 return isExpanded
|| myTree
.isExpanded(getPathFor(node
));
2004 private void removeLoading(DefaultMutableTreeNode parent
, boolean removeFromUnbuilt
) {
2005 for (int i
= 0; i
< parent
.getChildCount(); i
++) {
2006 TreeNode child
= parent
.getChildAt(i
);
2007 if (removeIfLoading(child
)) {
2012 if (removeFromUnbuilt
) {
2013 removeFromUnbuilt(parent
);
2016 if (parent
== getRootNode() && !myTree
.isRootVisible() && parent
.getChildCount() == 0) {
2017 insertLoadingNode(parent
, false);
2023 private void processNodeActionsIfReady(final DefaultMutableTreeNode node
) {
2024 assertIsDispatchThread();
2026 if (isNodeBeingBuilt(node
)) return;
2028 final Object o
= node
.getUserObject();
2029 if (!(o
instanceof NodeDescriptor
)) return;
2032 if (isYeildingNow()) {
2033 myPendingNodeActions
.add(node
);
2037 final Object element
= getBuilder().getTreeStructureElement((NodeDescriptor
)o
);
2039 boolean childrenReady
= !isLoadedInBackground(element
);
2041 processActions(node
, element
, myNodeActions
, childrenReady ? myNodeChildrenActions
: null);
2042 if (childrenReady
) {
2043 processActions(node
, element
, myNodeChildrenActions
, null);
2046 if (!isUpdatingParent(node
) && !isWorkerBusy()) {
2047 final UpdaterTreeState state
= myUpdaterState
;
2048 if (myNodeActions
.size() == 0 && state
!= null && !state
.isProcessingNow()) {
2049 if (!state
.restore(childrenReady ? node
: null)) {
2050 setUpdaterState(state
);
2059 private void processActions(DefaultMutableTreeNode node
, Object element
, final Map
<Object
, List
<NodeAction
>> nodeActions
, @Nullable final Map
<Object
, List
<NodeAction
>> secondaryNodeAction
) {
2060 final List
<NodeAction
> actions
= nodeActions
.get(element
);
2061 if (actions
!= null) {
2062 nodeActions
.remove(element
);
2064 List
<NodeAction
> secondary
= secondaryNodeAction
!= null ? secondaryNodeAction
.get(element
) : null;
2065 for (NodeAction each
: actions
) {
2066 if (secondary
!= null && secondary
.contains(each
)) {
2067 secondary
.remove(each
);
2075 private boolean canSmartExpand(DefaultMutableTreeNode node
, boolean canSmartExpand
) {
2076 if (!getBuilder().isSmartExpand()) return false;
2078 boolean smartExpand
= !myNotForSmartExpand
.contains(node
) && canSmartExpand
;
2079 return smartExpand ?
validateAutoExpand(smartExpand
, getElementFor(node
)) : false;
2082 private void processSmartExpand(final DefaultMutableTreeNode node
, final boolean canSmartExpand
, boolean forced
) {
2083 if (!getBuilder().isSmartExpand()) return;
2085 boolean can
= canSmartExpand(node
, canSmartExpand
);
2087 if (!can
&& !forced
) return;
2089 if (isNodeBeingBuilt(node
) && !forced
) {
2090 addNodeAction(getElementFor(node
), new NodeAction() {
2091 public void onReady(DefaultMutableTreeNode node
) {
2092 processSmartExpand(node
, canSmartExpand
, true);
2097 TreeNode child
= getChildForSmartExpand(node
);
2098 if (child
!= null) {
2099 final TreePath childPath
= new TreePath(node
.getPath()).pathByAddingChild(child
);
2100 processInnerChange(new Runnable() {
2102 myTree
.expandPath(childPath
);
2110 private TreeNode
getChildForSmartExpand(DefaultMutableTreeNode node
) {
2111 int realChildCount
= 0;
2112 TreeNode nodeToExpand
= null;
2114 for (int i
= 0; i
< node
.getChildCount(); i
++) {
2115 TreeNode eachChild
= node
.getChildAt(i
);
2117 if (!isLoadingNode(eachChild
)) {
2119 if (nodeToExpand
== null) {
2120 nodeToExpand
= eachChild
;
2124 if (realChildCount
> 1) {
2125 nodeToExpand
= null;
2130 return nodeToExpand
;
2133 public boolean isLoadingChildrenFor(final Object nodeObject
) {
2134 if (!(nodeObject
instanceof DefaultMutableTreeNode
)) return false;
2136 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)nodeObject
;
2138 int loadingNodes
= 0;
2139 for (int i
= 0; i
< Math
.min(node
.getChildCount(), 2); i
++) {
2140 TreeNode child
= node
.getChildAt(i
);
2141 if (isLoadingNode(child
)) {
2145 return loadingNodes
> 0 && loadingNodes
== node
.getChildCount();
2148 private boolean isParentLoading(Object nodeObject
) {
2149 return getParentLoading(nodeObject
) != null;
2152 private DefaultMutableTreeNode
getParentLoading(Object nodeObject
) {
2153 if (!(nodeObject
instanceof DefaultMutableTreeNode
)) return null;
2155 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)nodeObject
;
2157 TreeNode eachParent
= node
.getParent();
2159 while (eachParent
!= null) {
2160 eachParent
= eachParent
.getParent();
2161 if (eachParent
instanceof DefaultMutableTreeNode
) {
2162 final Object eachElement
= getElementFor((DefaultMutableTreeNode
)eachParent
);
2163 if (isLoadedInBackground(eachElement
)) return (DefaultMutableTreeNode
)eachParent
;
2170 protected String
getLoadingNodeText() {
2171 return IdeBundle
.message("progress.searching");
2174 private ActionCallback
processExistingNode(final DefaultMutableTreeNode childNode
,
2175 final NodeDescriptor childDescriptor
,
2176 final DefaultMutableTreeNode parentNode
,
2177 final MutualMap
<Object
, Integer
> elementToIndexMap
,
2178 final TreeUpdatePass pass
,
2179 final boolean canSmartExpand
,
2180 final boolean forceUpdate
,
2181 LoadedChildren parentPreloadedChildren
) {
2183 final ActionCallback result
= new ActionCallback();
2185 if (pass
.isExpired()) {
2186 return new ActionCallback
.Rejected();
2189 final Ref
<NodeDescriptor
> childDesc
= new Ref
<NodeDescriptor
>(childDescriptor
);
2191 if (childDesc
.get() == null) {
2193 return new ActionCallback
.Rejected();
2195 final Object oldElement
= getElementFromDescriptor(childDesc
.get());
2196 if (oldElement
== null) {
2198 return new ActionCallback
.Rejected();
2201 AsyncResult
<Boolean
> update
= new AsyncResult
<Boolean
>();
2202 if (parentPreloadedChildren
!= null && parentPreloadedChildren
.getDescriptor(oldElement
) != null) {
2203 update
.setDone(parentPreloadedChildren
.isUpdated(oldElement
));
2206 update
= update(childDesc
.get(), false);
2209 update
.doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
2210 public void run(Boolean isChanged
) {
2211 final Ref
<Boolean
> changes
= new Ref
<Boolean
>(isChanged
);
2213 final Ref
<Boolean
> forceRemapping
= new Ref
<Boolean
>(false);
2214 final Ref
<Object
> newElement
= new Ref
<Object
>(getElementFromDescriptor(childDesc
.get()));
2216 final Integer index
= newElement
.get() != null ? elementToIndexMap
.getValue(getBuilder().getTreeStructureElement(childDesc
.get())) : null;
2217 final AsyncResult
<Boolean
> updateIndexDone
= new AsyncResult
<Boolean
>();
2218 final ActionCallback indexReady
= new ActionCallback();
2219 if (index
!= null) {
2220 final Object elementFromMap
= elementToIndexMap
.getKey(index
);
2221 if (elementFromMap
!= newElement
.get() && elementFromMap
.equals(newElement
.get())) {
2222 if (isInStructure(elementFromMap
) && isInStructure(newElement
.get())) {
2223 if (parentNode
.getUserObject() instanceof NodeDescriptor
) {
2224 final NodeDescriptor parentDescriptor
= getDescriptorFrom(parentNode
);
2225 childDesc
.set(getTreeStructure().createDescriptor(elementFromMap
, parentDescriptor
));
2226 childNode
.setUserObject(childDesc
.get());
2227 newElement
.set(elementFromMap
);
2228 forceRemapping
.set(true);
2229 update(childDesc
.get(), false).doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
2230 public void run(Boolean isChanged
) {
2231 changes
.set(isChanged
);
2232 updateIndexDone
.setDone(isChanged
);
2238 updateIndexDone
.setDone(changes
.get());
2241 updateIndexDone
.setDone(changes
.get());
2244 updateIndexDone
.doWhenDone(new Runnable() {
2246 if (childDesc
.get().getIndex() != index
.intValue()) {
2249 childDesc
.get().setIndex(index
.intValue());
2250 indexReady
.setDone();
2255 updateIndexDone
.setDone();
2258 updateIndexDone
.doWhenDone(new Runnable() {
2260 if (index
!= null && changes
.get()) {
2261 updateNodeImageAndPosition(childNode
, false);
2263 if (!oldElement
.equals(newElement
.get()) | forceRemapping
.get()) {
2264 removeMapping(oldElement
, childNode
, newElement
.get());
2265 if (newElement
.get() != null) {
2266 createMapping(newElement
.get(), childNode
);
2268 NodeDescriptor parentDescriptor
= getDescriptorFrom(parentNode
);
2269 if (parentDescriptor
!= null) {
2270 parentDescriptor
.setChildrenSortingStamp(-1);
2274 if (index
== null) {
2275 int selectedIndex
= -1;
2276 if (TreeBuilderUtil
.isNodeOrChildSelected(myTree
, childNode
)) {
2277 selectedIndex
= parentNode
.getIndex(childNode
);
2280 if (childNode
.getParent() instanceof DefaultMutableTreeNode
) {
2281 final DefaultMutableTreeNode parent
= (DefaultMutableTreeNode
)childNode
.getParent();
2282 if (myTree
.isExpanded(new TreePath(parent
.getPath()))) {
2283 if (parent
.getChildCount() == 1 && parent
.getChildAt(0) == childNode
) {
2284 insertLoadingNode(parent
, false);
2289 Object disposedElement
= getElementFor(childNode
);
2291 removeNodeFromParent(childNode
, selectedIndex
>= 0);
2292 disposeNode(childNode
);
2294 adjustSelectionOnChildRemove(parentNode
, selectedIndex
, disposedElement
);
2297 elementToIndexMap
.remove(getBuilder().getTreeStructureElement(childDesc
.get()));
2298 updateNodeChildren(childNode
, pass
, null, false, canSmartExpand
, forceUpdate
, true);
2301 if (parentNode
.equals(getRootNode())) {
2302 myTreeModel
.nodeChanged(getRootNode());
2315 private void adjustSelectionOnChildRemove(DefaultMutableTreeNode parentNode
, int selectedIndex
, Object disposedElement
) {
2316 DefaultMutableTreeNode node
= getNodeForElement(disposedElement
, false);
2317 if (node
!= null && isValidForSelectionAdjusting(node
)) {
2318 Object newElement
= getElementFor(node
);
2319 addSelectionPath(getPathFor(node
), true, getExpiredElementCondition(newElement
), disposedElement
);
2324 if (selectedIndex
>= 0) {
2325 if (parentNode
.getChildCount() > 0) {
2326 if (parentNode
.getChildCount() > selectedIndex
) {
2327 TreeNode newChildNode
= parentNode
.getChildAt(selectedIndex
);
2328 if (isValidForSelectionAdjusting(newChildNode
)) {
2329 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(newChildNode
)), true, getExpiredElementCondition(disposedElement
), disposedElement
);
2333 TreeNode newChild
= parentNode
.getChildAt(parentNode
.getChildCount() - 1);
2334 if (isValidForSelectionAdjusting(newChild
)) {
2335 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(newChild
)), true, getExpiredElementCondition(disposedElement
), disposedElement
);
2340 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(parentNode
)), true, getExpiredElementCondition(disposedElement
), disposedElement
);
2345 private boolean isValidForSelectionAdjusting(TreeNode node
) {
2346 if (!myTree
.isRootVisible() && getRootNode() == node
) return false;
2348 if (isLoadingNode(node
)) return true;
2350 final Object elementInTree
= getElementFor(node
);
2351 if (elementInTree
== null) return false;
2353 final TreeNode parentNode
= node
.getParent();
2354 final Object parentElementInTree
= getElementFor(parentNode
);
2355 if (parentElementInTree
== null) return false;
2357 final Object parentElement
= getTreeStructure().getParentElement(elementInTree
);
2359 return parentElementInTree
.equals(parentElement
);
2362 public Condition
getExpiredElementCondition(final Object element
) {
2363 return new Condition() {
2364 public boolean value(final Object o
) {
2365 return isInStructure(element
);
2370 private void addSelectionPath(final TreePath path
, final boolean isAdjustedSelection
, final Condition isExpiredAdjustement
, @Nullable final Object adjustmentCause
) {
2371 processInnerChange(new Runnable() {
2373 TreePath toSelect
= null;
2375 if (isLoadingNode(path
.getLastPathComponent())) {
2376 final TreePath parentPath
= path
.getParentPath();
2377 if (parentPath
!= null) {
2378 if (isValidForSelectionAdjusting((TreeNode
)parentPath
.getLastPathComponent())) {
2379 toSelect
= parentPath
;
2390 if (toSelect
!= null) {
2391 myTree
.addSelectionPath(toSelect
);
2393 if (isAdjustedSelection
&& myUpdaterState
!= null) {
2394 final Object toSelectElement
= getElementFor(toSelect
.getLastPathComponent());
2395 myUpdaterState
.addAdjustedSelection(toSelectElement
, isExpiredAdjustement
, adjustmentCause
);
2402 private static TreePath
getPathFor(TreeNode node
) {
2403 if (node
instanceof DefaultMutableTreeNode
) {
2404 return new TreePath(((DefaultMutableTreeNode
)node
).getPath());
2407 ArrayList nodes
= new ArrayList();
2408 TreeNode eachParent
= node
;
2409 while (eachParent
!= null) {
2410 nodes
.add(eachParent
);
2411 eachParent
= eachParent
.getParent();
2414 return new TreePath(ArrayUtil
.toObjectArray(nodes
));
2419 private void removeNodeFromParent(final MutableTreeNode node
, final boolean willAdjustSelection
) {
2420 processInnerChange(new Runnable() {
2422 if (willAdjustSelection
) {
2423 final TreePath path
= getPathFor(node
);
2424 if (myTree
.isPathSelected(path
)) {
2425 myTree
.removeSelectionPath(path
);
2429 if (node
.getParent() != null) {
2430 myTreeModel
.removeNodeFromParent(node
);
2436 private void expandPath(final TreePath path
, final boolean canSmartExpand
) {
2437 processInnerChange(new Runnable() {
2439 if (path
.getLastPathComponent() instanceof DefaultMutableTreeNode
) {
2440 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
2441 if (node
.getChildCount() > 0 && !myTree
.isExpanded(path
)) {
2442 if (!canSmartExpand
) {
2443 myNotForSmartExpand
.add(node
);
2446 myRequestedExpand
= path
;
2447 myTree
.expandPath(path
);
2448 processSmartExpand(node
, canSmartExpand
, false);
2451 myNotForSmartExpand
.remove(node
);
2452 myRequestedExpand
= null;
2456 processNodeActionsIfReady(node
);
2463 private void processInnerChange(Runnable runnable
) {
2464 if (myUpdaterState
== null) {
2465 setUpdaterState(new UpdaterTreeState(this));
2468 myUpdaterState
.process(runnable
);
2471 private boolean isInnerChange() {
2472 return myUpdaterState
!= null && myUpdaterState
.isProcessingNow();
2475 protected boolean doUpdateNodeDescriptor(final NodeDescriptor descriptor
) {
2476 return descriptor
.update();
2479 private void makeLoadingOrLeafIfNoChildren(final DefaultMutableTreeNode node
) {
2480 TreePath path
= getPathFor(node
);
2481 if (path
== null) return;
2483 insertLoadingNode(node
, true);
2485 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
2486 if (descriptor
== null) return;
2488 descriptor
.setChildrenSortingStamp(-1);
2490 if (getBuilder().isAlwaysShowPlus(descriptor
)) return;
2493 TreePath parentPath
= path
.getParentPath();
2494 if (myTree
.isVisible(path
) || (parentPath
!= null && myTree
.isExpanded(parentPath
))) {
2495 if (myTree
.isExpanded(path
)) {
2496 addSubtreeToUpdate(node
);
2499 insertLoadingNode(node
, false);
2505 private boolean isValid(DefaultMutableTreeNode node
) {
2506 if (node
== null) return false;
2507 final Object object
= node
.getUserObject();
2508 if (object
instanceof NodeDescriptor
) {
2509 return isValid((NodeDescriptor
)object
);
2515 private boolean isValid(NodeDescriptor descriptor
) {
2516 if (descriptor
== null) return false;
2517 return isValid(getElementFromDescriptor(descriptor
));
2520 private boolean isValid(Object element
) {
2521 if (element
instanceof ValidateableNode
) {
2522 if (!((ValidateableNode
)element
).isValid()) return false;
2524 return getBuilder().validateNode(element
);
2527 private void insertLoadingNode(final DefaultMutableTreeNode node
, boolean addToUnbuilt
) {
2528 if (!isLoadingChildrenFor(node
)) {
2529 myTreeModel
.insertNodeInto(new LoadingNode(), node
, 0);
2538 protected ActionCallback
queueToBackground(@NotNull final Runnable bgBuildAction
,
2539 @Nullable final Runnable edtPostRunnable
) {
2540 if (validateReleaseRequested()) return new ActionCallback
.Rejected();
2542 final ActionCallback result
= new ActionCallback();
2544 final Ref
<Boolean
> fail
= new Ref
<Boolean
>(false);
2545 final Runnable finalizer
= new Runnable() {
2548 result
.setRejected();
2555 registerWorkerTask(bgBuildAction
);
2557 final Runnable pooledThreadWithProgressRunnable
= new Runnable() {
2559 final AbstractTreeBuilder builder
= getBuilder();
2561 builder
.runBackgroundLoading(new Runnable() {
2563 assertNotDispatchThread();
2566 bgBuildAction
.run();
2568 if (edtPostRunnable
!= null) {
2569 builder
.updateAfterLoadedInBackground(new Runnable() {
2572 assertIsDispatchThread();
2574 edtPostRunnable
.run();
2575 } catch (ProcessCanceledException e
) {
2579 unregisterWorkerTask(bgBuildAction
, finalizer
);
2585 unregisterWorkerTask(bgBuildAction
, finalizer
);
2588 catch (ProcessCanceledException e
) {
2590 unregisterWorkerTask(bgBuildAction
, finalizer
);
2592 catch (Throwable t
) {
2593 unregisterWorkerTask(bgBuildAction
, finalizer
);
2594 throw new RuntimeException(t
);
2601 Runnable pooledThreadRunnable
= new Runnable() {
2604 if (myProgress
!= null) {
2605 ProgressManager
.getInstance().runProcess(pooledThreadWithProgressRunnable
, myProgress
);
2608 pooledThreadWithProgressRunnable
.run();
2611 catch (ProcessCanceledException e
) {
2613 unregisterWorkerTask(bgBuildAction
, finalizer
);
2618 if (isPassthroughMode()) {
2621 if (myWorker
== null || myWorker
.isDisposed()) {
2622 myWorker
= new WorkerThread("AbstractTreeBuilder.Worker", 1);
2624 myWorker
.addTaskFirst(pooledThreadRunnable
);
2625 myWorker
.dispose(false);
2628 myWorker
.addTaskFirst(pooledThreadRunnable
);
2635 private void registerWorkerTask(Runnable runnable
) {
2636 synchronized (myActiveWorkerTasks
) {
2637 myActiveWorkerTasks
.add(runnable
);
2641 private void unregisterWorkerTask(Runnable runnable
, @Nullable Runnable finalizeRunnable
) {
2643 synchronized (myActiveWorkerTasks
) {
2644 wasRemoved
= myActiveWorkerTasks
.remove(runnable
);
2647 if (wasRemoved
&& finalizeRunnable
!= null) {
2648 finalizeRunnable
.run();
2654 public boolean isWorkerBusy() {
2655 synchronized (myActiveWorkerTasks
) {
2656 return myActiveWorkerTasks
.size() > 0;
2660 private void clearWorkerTasks() {
2661 synchronized (myActiveWorkerTasks
) {
2662 myActiveWorkerTasks
.clear();
2666 private void updateNodeImageAndPosition(final DefaultMutableTreeNode node
, boolean updatePosition
) {
2667 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
2668 NodeDescriptor descriptor
= getDescriptorFrom(node
);
2669 if (getElementFromDescriptor(descriptor
) == null) return;
2671 boolean notified
= false;
2672 if (updatePosition
) {
2673 DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)node
.getParent();
2674 if (parentNode
!= null) {
2675 int oldIndex
= parentNode
.getIndex(node
);
2676 int newIndex
= oldIndex
;
2677 if (isLoadingChildrenFor(node
.getParent()) || getBuilder().isChildrenResortingNeeded(descriptor
)) {
2678 final ArrayList
<TreeNode
> children
= new ArrayList
<TreeNode
>(parentNode
.getChildCount());
2679 for (int i
= 0; i
< parentNode
.getChildCount(); i
++) {
2680 children
.add(parentNode
.getChildAt(i
));
2682 sortChildren(node
, children
, true, false);
2683 newIndex
= children
.indexOf(node
);
2686 if (oldIndex
!= newIndex
) {
2687 List
<Object
> pathsToExpand
= new ArrayList
<Object
>();
2688 List
<Object
> selectionPaths
= new ArrayList
<Object
>();
2689 TreeBuilderUtil
.storePaths(getBuilder(), node
, pathsToExpand
, selectionPaths
, false);
2690 removeNodeFromParent(node
, false);
2691 myTreeModel
.insertNodeInto(node
, parentNode
, newIndex
);
2692 TreeBuilderUtil
.restorePaths(getBuilder(), pathsToExpand
, selectionPaths
, false);
2696 myTreeModel
.nodeChanged(node
);
2701 myTreeModel
.nodeChanged(node
);
2707 myTreeModel
.nodeChanged(node
);
2712 public DefaultTreeModel
getTreeModel() {
2716 private void insertNodesInto(final ArrayList
<TreeNode
> toInsert
, final DefaultMutableTreeNode parentNode
) {
2717 sortChildren(parentNode
, toInsert
, false, true);
2718 final ArrayList
<TreeNode
> all
= new ArrayList
<TreeNode
>(toInsert
.size() + parentNode
.getChildCount());
2719 all
.addAll(toInsert
);
2720 all
.addAll(TreeUtil
.childrenToArray(parentNode
));
2722 if (toInsert
.size() > 0) {
2723 sortChildren(parentNode
, all
, true, true);
2725 int[] newNodeIndices
= new int[toInsert
.size()];
2726 int eachNewNodeIndex
= 0;
2727 TreeMap
<Integer
, TreeNode
> insertSet
= new TreeMap
<Integer
, TreeNode
>();
2728 for (int i
= 0; i
< toInsert
.size(); i
++) {
2729 TreeNode eachNewNode
= toInsert
.get(i
);
2730 while (all
.get(eachNewNodeIndex
) != eachNewNode
) {
2733 newNodeIndices
[i
] = eachNewNodeIndex
;
2734 insertSet
.put(eachNewNodeIndex
, eachNewNode
);
2737 Iterator
<Integer
> indices
= insertSet
.keySet().iterator();
2738 while (indices
.hasNext()) {
2739 Integer eachIndex
= indices
.next();
2740 TreeNode eachNode
= insertSet
.get(eachIndex
);
2741 parentNode
.insert((MutableTreeNode
)eachNode
, eachIndex
);
2744 myTreeModel
.nodesWereInserted(parentNode
, newNodeIndices
);
2747 ArrayList
<TreeNode
> before
= new ArrayList
<TreeNode
>();
2750 sortChildren(parentNode
, all
, true, false);
2751 if (!before
.equals(all
)) {
2752 processInnerChange(new Runnable() {
2754 parentNode
.removeAllChildren();
2755 for (TreeNode each
: all
) {
2756 parentNode
.add((MutableTreeNode
)each
);
2758 myTreeModel
.nodeStructureChanged(parentNode
);
2765 private void sortChildren(DefaultMutableTreeNode node
, ArrayList
<TreeNode
> children
, boolean updateStamp
, boolean forceSort
) {
2766 NodeDescriptor descriptor
= getDescriptorFrom(node
);
2767 assert descriptor
!= null;
2769 if (descriptor
.getChildrenSortingStamp() >= getComparatorStamp() && !forceSort
) return;
2770 if (children
.size() > 0) {
2771 getBuilder().sortChildren(myNodeComparator
, node
, children
);
2775 descriptor
.setChildrenSortingStamp(getComparatorStamp());
2779 private void disposeNode(DefaultMutableTreeNode node
) {
2780 TreeNode parent
= node
.getParent();
2781 if (parent
instanceof DefaultMutableTreeNode
) {
2782 addToUnbuilt((DefaultMutableTreeNode
)parent
);
2785 if (node
.getChildCount() > 0) {
2786 for (DefaultMutableTreeNode _node
= (DefaultMutableTreeNode
)node
.getFirstChild(); _node
!= null; _node
= _node
.getNextSibling()) {
2791 removeFromUpdating(node
);
2792 removeFromUnbuilt(node
);
2794 if (isLoadingNode(node
)) return;
2795 NodeDescriptor descriptor
= getDescriptorFrom(node
);
2796 if (descriptor
== null) return;
2797 final Object element
= getElementFromDescriptor(descriptor
);
2798 removeMapping(element
, node
, null);
2799 myAutoExpandRoots
.remove(element
);
2800 node
.setUserObject(null);
2801 node
.removeAllChildren();
2804 public boolean addSubtreeToUpdate(final DefaultMutableTreeNode root
) {
2805 return addSubtreeToUpdate(root
, null);
2808 public boolean addSubtreeToUpdate(final DefaultMutableTreeNode root
, Runnable runAfterUpdate
) {
2809 Object element
= getElementFor(root
);
2810 if (getTreeStructure().isAlwaysLeaf(element
)) {
2811 removeLoading(root
, true);
2813 if (runAfterUpdate
!= null) {
2814 getReady(this).doWhenDone(runAfterUpdate
);
2819 if (isReleaseRequested()) {
2820 processNodeActionsIfReady(root
);
2822 getUpdater().runAfterUpdate(runAfterUpdate
);
2823 getUpdater().addSubtreeToUpdate(root
);
2829 public boolean wasRootNodeInitialized() {
2830 return myRootNodeWasInitialized
;
2833 private boolean isRootNodeBuilt() {
2834 return myRootNodeWasInitialized
&& isNodeBeingBuilt(myRootNode
);
2837 public void select(final Object
[] elements
, @Nullable final Runnable onDone
) {
2838 select(elements
, onDone
, false);
2841 public void select(final Object
[] elements
, @Nullable final Runnable onDone
, boolean addToSelection
) {
2842 select(elements
, onDone
, addToSelection
, false);
2845 public void select(final Object
[] elements
, @Nullable final Runnable onDone
, boolean addToSelection
, boolean deferred
) {
2846 _select(elements
, onDone
, addToSelection
, true, false, true, deferred
, false, false);
2849 void _select(final Object
[] elements
,
2850 final Runnable onDone
,
2851 final boolean addToSelection
,
2852 final boolean checkCurrentSelection
,
2853 final boolean checkIfInStructure
) {
2855 _select(elements
, onDone
, addToSelection
, checkCurrentSelection
, checkIfInStructure
, true, false, false, false);
2858 void _select(final Object
[] elements
,
2859 final Runnable onDone
,
2860 final boolean addToSelection
,
2861 final boolean checkCurrentSelection
,
2862 final boolean checkIfInStructure
,
2863 final boolean scrollToVisible
) {
2865 _select(elements
, onDone
, addToSelection
, checkCurrentSelection
, checkIfInStructure
, scrollToVisible
, false, false, false);
2868 public void userSelect(final Object
[] elements
,
2869 final Runnable onDone
,
2870 final boolean addToSelection
,
2872 _select(elements
, onDone
, addToSelection
, true, false, scroll
, false, true, true);
2875 void _select(final Object
[] elements
,
2876 final Runnable onDone
,
2877 final boolean addToSelection
,
2878 final boolean checkCurrentSelection
,
2879 final boolean checkIfInStructure
,
2880 final boolean scrollToVisible
,
2881 final boolean deferred
,
2882 final boolean canSmartExpand
,
2883 final boolean mayQueue
) {
2885 AbstractTreeUpdater updater
= getUpdater();
2886 if (mayQueue
&& updater
!= null) {
2887 updater
.queueSelection(new SelectionRequest(elements
, onDone
, addToSelection
, checkCurrentSelection
, checkIfInStructure
, scrollToVisible
, deferred
, canSmartExpand
));
2891 boolean willAffectSelection
= elements
.length
> 0 || (elements
.length
== 0 && addToSelection
);
2892 if (!willAffectSelection
) {
2897 final boolean oldCanProcessDeferredSelection
= myCanProcessDeferredSelections
;
2899 if (!deferred
&& wasRootNodeInitialized() && willAffectSelection
) {
2900 myCanProcessDeferredSelections
= false;
2903 if (!checkDeferred(deferred
, onDone
)) return;
2905 if (!deferred
&& oldCanProcessDeferredSelection
&& !myCanProcessDeferredSelections
) {
2906 getTree().clearSelection();
2910 runDone(new Runnable() {
2912 if (!checkDeferred(deferred
, onDone
)) return;
2914 final Set
<Object
> currentElements
= getSelectedElements();
2916 if (checkCurrentSelection
&& currentElements
.size() > 0 && elements
.length
== currentElements
.size()) {
2917 boolean runSelection
= false;
2918 for (Object eachToSelect
: elements
) {
2919 if (!currentElements
.contains(eachToSelect
)) {
2920 runSelection
= true;
2925 if (!runSelection
) {
2926 if (elements
.length
> 0) {
2927 selectVisible(elements
[0], onDone
, true, true, scrollToVisible
);
2933 Set
<Object
> toSelect
= new HashSet
<Object
>();
2934 myTree
.clearSelection();
2935 toSelect
.addAll(Arrays
.asList(elements
));
2936 if (addToSelection
) {
2937 toSelect
.addAll(currentElements
);
2940 if (checkIfInStructure
) {
2941 final Iterator
<Object
> allToSelect
= toSelect
.iterator();
2942 while (allToSelect
.hasNext()) {
2943 Object each
= allToSelect
.next();
2944 if (!isInStructure(each
)) {
2945 allToSelect
.remove();
2950 final Object
[] elementsToSelect
= ArrayUtil
.toObjectArray(toSelect
);
2952 if (wasRootNodeInitialized()) {
2953 final int[] originalRows
= myTree
.getSelectionRows();
2954 if (!addToSelection
) {
2955 myTree
.clearSelection();
2957 addNext(elementsToSelect
, 0, new Runnable() {
2959 if (getTree().isSelectionEmpty()) {
2960 processInnerChange(new Runnable() {
2962 restoreSelection(currentElements
);
2968 }, originalRows
, deferred
, scrollToVisible
, canSmartExpand
);
2971 addToDeferred(elementsToSelect
, onDone
);
2977 private void restoreSelection(Set
<Object
> selection
) {
2978 for (Object each
: selection
) {
2979 DefaultMutableTreeNode node
= getNodeForElement(each
, false);
2980 if (node
!= null && isValidForSelectionAdjusting(node
)) {
2981 addSelectionPath(getPathFor(node
), false, null, null);
2987 private void addToDeferred(final Object
[] elementsToSelect
, final Runnable onDone
) {
2988 myDeferredSelections
.clear();
2989 myDeferredSelections
.add(new Runnable() {
2991 select(elementsToSelect
, onDone
, false, true);
2996 private boolean checkDeferred(boolean isDeferred
, @Nullable Runnable onDone
) {
2997 if (!isDeferred
|| myCanProcessDeferredSelections
|| !wasRootNodeInitialized()) {
3007 final Set
<Object
> getSelectedElements() {
3008 final TreePath
[] paths
= myTree
.getSelectionPaths();
3010 Set
<Object
> result
= new HashSet
<Object
>();
3011 if (paths
!= null) {
3012 for (TreePath eachPath
: paths
) {
3013 if (eachPath
.getLastPathComponent() instanceof DefaultMutableTreeNode
) {
3014 final DefaultMutableTreeNode eachNode
= (DefaultMutableTreeNode
)eachPath
.getLastPathComponent();
3015 final Object eachElement
= getElementFor(eachNode
);
3016 if (eachElement
!= null) {
3017 result
.add(eachElement
);
3026 private void addNext(final Object
[] elements
,
3028 @Nullable final Runnable onDone
,
3029 final int[] originalRows
,
3030 final boolean deferred
,
3031 final boolean scrollToVisible
,
3032 final boolean canSmartExpand
) {
3033 if (i
>= elements
.length
) {
3034 if (myTree
.isSelectionEmpty()) {
3035 myTree
.setSelectionRows(originalRows
);
3040 if (!checkDeferred(deferred
, onDone
)) {
3044 doSelect(elements
[i
], new Runnable() {
3046 if (!checkDeferred(deferred
, onDone
)) return;
3048 addNext(elements
, i
+ 1, onDone
, originalRows
, deferred
, scrollToVisible
, canSmartExpand
);
3050 }, true, deferred
, i
== 0, scrollToVisible
, canSmartExpand
);
3054 public void select(final Object element
, @Nullable final Runnable onDone
) {
3055 select(element
, onDone
, false);
3058 public void select(final Object element
, @Nullable final Runnable onDone
, boolean addToSelection
) {
3059 _select(new Object
[]{element
}, onDone
, addToSelection
, true, false);
3062 private void doSelect(final Object element
,
3063 final Runnable onDone
,
3064 final boolean addToSelection
,
3065 final boolean deferred
,
3066 final boolean canBeCentered
,
3067 final boolean scrollToVisible
,
3068 boolean canSmartExpand
) {
3069 final Runnable _onDone
= new Runnable() {
3071 if (!checkDeferred(deferred
, onDone
)) return;
3072 selectVisible(element
, onDone
, addToSelection
, canBeCentered
, scrollToVisible
);
3075 _expand(element
, _onDone
, true, false, canSmartExpand
);
3078 public void scrollSelectionToVisible(@Nullable Runnable onDone
, boolean shouldBeCentered
) {
3079 int[] rows
= myTree
.getSelectionRows();
3080 if (rows
== null || rows
.length
== 0) {
3086 Object toSelect
= null;
3087 for (int eachRow
: rows
) {
3088 TreePath path
= myTree
.getPathForRow(eachRow
);
3089 toSelect
= getElementFor(path
.getLastPathComponent());
3090 if (toSelect
!= null) break;
3093 if (toSelect
!= null) {
3094 selectVisible(toSelect
, onDone
, true, shouldBeCentered
, true);
3098 private void selectVisible(Object element
, final Runnable onDone
, boolean addToSelection
, boolean canBeCentered
, final boolean scroll
) {
3099 final DefaultMutableTreeNode toSelect
= getNodeForElement(element
, false);
3101 if (toSelect
== null) {
3106 if (getRootNode() == toSelect
&& !myTree
.isRootVisible()) {
3111 final int row
= myTree
.getRowForPath(new TreePath(toSelect
.getPath()));
3113 if (myUpdaterState
!= null) {
3114 myUpdaterState
.addSelection(element
);
3117 if (Registry
.is("ide.tree.autoscrollToVCenter") && canBeCentered
) {
3118 runDone(new Runnable() {
3120 TreeUtil
.showRowCentered(myTree
, row
, false, scroll
).doWhenDone(new Runnable() {
3129 TreeUtil
.showAndSelect(myTree
, row
- 2, row
+ 2, row
, -1, addToSelection
, scroll
).doWhenDone(new Runnable() {
3137 public void expand(final Object element
, @Nullable final Runnable onDone
) {
3138 expand(new Object
[]{element
}, onDone
);
3141 public void expand(final Object
[] element
, @Nullable final Runnable onDone
) {
3142 expand(element
, onDone
, false);
3146 void expand(final Object element
, @Nullable final Runnable onDone
, boolean checkIfInStructure
) {
3147 _expand(new Object
[]{element
}, onDone
== null ?
new EmptyRunnable() : onDone
, false, checkIfInStructure
, false);
3150 void expand(final Object
[] element
, @Nullable final Runnable onDone
, boolean checkIfInStructure
) {
3151 _expand(element
, onDone
== null ?
new EmptyRunnable() : onDone
, false, checkIfInStructure
, false);
3154 void _expand(final Object
[] element
,
3155 @NotNull final Runnable onDone
,
3156 final boolean parentsOnly
,
3157 final boolean checkIfInStructure
,
3158 final boolean canSmartExpand
) {
3160 runDone(new Runnable() {
3162 if (element
.length
== 0) {
3167 if (myUpdaterState
!= null) {
3168 myUpdaterState
.clearExpansion();
3172 final ActionCallback done
= new ActionCallback(element
.length
);
3173 done
.doWhenDone(new Runnable() {
3177 }).doWhenRejected(new Runnable() {
3183 expandNext(element
, 0, parentsOnly
, checkIfInStructure
, canSmartExpand
, done
);
3188 private void expandNext(final Object
[] elements
, final int index
, final boolean parentsOnly
, final boolean checkIfInStricture
, final boolean canSmartExpand
, final ActionCallback done
) {
3189 if (elements
.length
<= 0) {
3194 if (index
>= elements
.length
) {
3198 _expand(elements
[index
], new Runnable() {
3201 expandNext(elements
, index
+ 1, parentsOnly
, checkIfInStricture
, canSmartExpand
, done
);
3203 }, parentsOnly
, checkIfInStricture
, canSmartExpand
);
3206 public void collapseChildren(final Object element
, @Nullable final Runnable onDone
) {
3207 runDone(new Runnable() {
3209 final DefaultMutableTreeNode node
= getNodeForElement(element
, false);
3211 getTree().collapsePath(new TreePath(node
.getPath()));
3218 private void runDone(@Nullable Runnable done
) {
3219 if (done
== null) return;
3221 if (isYeildingNow()) {
3222 if (!myYeildingDoneRunnables
.contains(done
)) {
3223 myYeildingDoneRunnables
.add(done
);
3231 private void _expand(final Object element
,
3232 @NotNull final Runnable onDone
,
3233 final boolean parentsOnly
,
3234 boolean checkIfInStructure
,
3235 boolean canSmartExpand
) {
3237 if (checkIfInStructure
&& !isInStructure(element
)) {
3242 if (wasRootNodeInitialized()) {
3243 List
<Object
> kidsToExpand
= new ArrayList
<Object
>();
3244 Object eachElement
= element
;
3245 DefaultMutableTreeNode firstVisible
= null;
3247 if (!isValid(eachElement
)) break;
3249 firstVisible
= getNodeForElement(eachElement
, true);
3250 if (eachElement
!= element
|| !parentsOnly
) {
3251 assert !kidsToExpand
.contains(eachElement
) :
3252 "Not a valid tree structure, walking up the structure gives many entries for element=" +
3255 getTreeStructure().getRootElement();
3256 kidsToExpand
.add(eachElement
);
3258 if (firstVisible
!= null) break;
3259 eachElement
= getTreeStructure().getParentElement(eachElement
);
3260 if (eachElement
== null) {
3261 firstVisible
= null;
3267 if (firstVisible
== null) {
3270 else if (kidsToExpand
.size() == 0) {
3271 final DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)firstVisible
.getParent();
3272 if (parentNode
!= null) {
3273 final TreePath parentPath
= new TreePath(parentNode
.getPath());
3274 if (!myTree
.isExpanded(parentPath
)) {
3275 expand(parentPath
, canSmartExpand
);
3281 processExpand(firstVisible
, kidsToExpand
, kidsToExpand
.size() - 1, onDone
, canSmartExpand
);
3285 deferExpansion(element
, onDone
, parentsOnly
, canSmartExpand
);
3289 private void deferExpansion(final Object element
, final Runnable onDone
, final boolean parentsOnly
, final boolean canSmartExpand
) {
3290 myDeferredExpansions
.add(new Runnable() {
3292 _expand(element
, onDone
, parentsOnly
, false, canSmartExpand
);
3297 private void processExpand(final DefaultMutableTreeNode toExpand
,
3298 final List kidsToExpand
,
3299 final int expandIndex
,
3300 @NotNull final Runnable onDone
,
3301 final boolean canSmartExpand
) {
3303 final Object element
= getElementFor(toExpand
);
3304 if (element
== null) {
3309 addNodeAction(element
, new NodeAction() {
3310 public void onReady(final DefaultMutableTreeNode node
) {
3311 if (node
.getChildCount() > 0 && !myTree
.isExpanded(new TreePath(node
.getPath()))) {
3312 if (!isAutoExpand(node
)) {
3313 expand(node
, canSmartExpand
);
3317 if (expandIndex
<= 0) {
3322 final DefaultMutableTreeNode nextNode
= getNodeForElement(kidsToExpand
.get(expandIndex
- 1), false);
3323 if (nextNode
!= null) {
3324 processExpand(nextNode
, kidsToExpand
, expandIndex
- 1, onDone
, canSmartExpand
);
3333 boolean childrenToUpdate
= areChildrenToBeUpdated(toExpand
);
3334 boolean expanded
= myTree
.isExpanded(getPathFor(toExpand
));
3335 boolean unbuilt
= myUnbuiltNodes
.contains(toExpand
);
3338 if (unbuilt
&& !childrenToUpdate
) {
3339 addSubtreeToUpdate(toExpand
);
3342 expand(toExpand
, canSmartExpand
);
3345 if (!unbuilt
&& !childrenToUpdate
) {
3346 processNodeActionsIfReady(toExpand
);
3350 private boolean areChildrenToBeUpdated(DefaultMutableTreeNode node
) {
3351 return getUpdater().isEnqueuedToUpdate(node
) || isUpdatingParent(node
);
3354 private String
asString(DefaultMutableTreeNode node
) {
3355 if (node
== null) return null;
3357 StringBuffer children
= new StringBuffer(node
.toString());
3358 children
.append(" [");
3359 for (int i
= 0; i
< node
.getChildCount(); i
++) {
3360 children
.append(node
.getChildAt(i
));
3361 if (i
< node
.getChildCount() - 1) {
3362 children
.append(",");
3365 children
.append("]");
3367 return children
.toString();
3371 public Object
getElementFor(Object node
) {
3372 if (!(node
instanceof DefaultMutableTreeNode
)) return null;
3373 return getElementFor((DefaultMutableTreeNode
)node
);
3377 Object
getElementFor(DefaultMutableTreeNode node
) {
3379 final Object o
= node
.getUserObject();
3380 if (o
instanceof NodeDescriptor
) {
3381 return getElementFromDescriptor(((NodeDescriptor
)o
));
3388 public final boolean isNodeBeingBuilt(final TreePath path
) {
3389 return isNodeBeingBuilt(path
.getLastPathComponent());
3392 public final boolean isNodeBeingBuilt(Object node
) {
3393 if (isReleaseRequested()) return false;
3395 return getParentBuiltNode(node
) != null;
3398 public final DefaultMutableTreeNode
getParentBuiltNode(Object node
) {
3399 DefaultMutableTreeNode parent
= getParentLoading(node
);
3400 if (parent
!= null) return parent
;
3402 if (isLoadingParent(node
)) return (DefaultMutableTreeNode
)node
;
3404 final boolean childrenAreNoLoadedYet
= myUnbuiltNodes
.contains(node
);
3405 if (childrenAreNoLoadedYet
) {
3406 if (node
instanceof DefaultMutableTreeNode
) {
3407 final TreePath nodePath
= new TreePath(((DefaultMutableTreeNode
)node
).getPath());
3408 if (!myTree
.isExpanded(nodePath
)) return null;
3411 return (DefaultMutableTreeNode
)node
;
3418 private boolean isLoadingParent(Object node
) {
3419 if (!(node
instanceof DefaultMutableTreeNode
)) return false;
3420 return isLoadedInBackground(getElementFor((DefaultMutableTreeNode
)node
));
3423 public void setTreeStructure(final AbstractTreeStructure treeStructure
) {
3424 myTreeStructure
= treeStructure
;
3425 clearUpdaterState();
3428 public AbstractTreeUpdater
getUpdater() {
3432 public void setUpdater(final AbstractTreeUpdater updater
) {
3433 myUpdater
= updater
;
3434 if (updater
!= null && myUpdateIfInactive
) {
3435 updater
.showNotify();
3438 if (myUpdater
!= null) {
3439 myUpdater
.setPassThroughMode(myPassthroughMode
);
3443 public DefaultMutableTreeNode
getRootNode() {
3447 public void setRootNode(@NotNull final DefaultMutableTreeNode rootNode
) {
3448 myRootNode
= rootNode
;
3451 private void dropUpdaterStateIfExternalChange() {
3452 if (!isInnerChange()) {
3453 clearUpdaterState();
3454 myAutoExpandRoots
.clear();
3458 void clearUpdaterState() {
3459 myUpdaterState
= null;
3462 private void createMapping(Object element
, DefaultMutableTreeNode node
) {
3463 if (!myElementToNodeMap
.containsKey(element
)) {
3464 myElementToNodeMap
.put(element
, node
);
3467 final Object value
= myElementToNodeMap
.get(element
);
3468 final List
<DefaultMutableTreeNode
> nodes
;
3469 if (value
instanceof DefaultMutableTreeNode
) {
3470 nodes
= new ArrayList
<DefaultMutableTreeNode
>();
3471 nodes
.add((DefaultMutableTreeNode
)value
);
3472 myElementToNodeMap
.put(element
, nodes
);
3475 nodes
= (List
<DefaultMutableTreeNode
>)value
;
3481 private void removeMapping(Object element
, DefaultMutableTreeNode node
, @Nullable Object elementToPutNodeActionsFor
) {
3482 final Object value
= myElementToNodeMap
.get(element
);
3483 if (value
!= null) {
3484 if (value
instanceof DefaultMutableTreeNode
) {
3485 if (value
.equals(node
)) {
3486 myElementToNodeMap
.remove(element
);
3490 List
<DefaultMutableTreeNode
> nodes
= (List
<DefaultMutableTreeNode
>)value
;
3491 final boolean reallyRemoved
= nodes
.remove(node
);
3492 if (reallyRemoved
) {
3493 if (nodes
.isEmpty()) {
3494 myElementToNodeMap
.remove(element
);
3500 remapNodeActions(element
, elementToPutNodeActionsFor
);
3503 private void remapNodeActions(Object element
, Object elementToPutNodeActionsFor
) {
3504 _remapNodeActions(element
, elementToPutNodeActionsFor
, myNodeActions
);
3505 _remapNodeActions(element
, elementToPutNodeActionsFor
, myNodeChildrenActions
);
3508 private void _remapNodeActions(Object element
, Object elementToPutNodeActionsFor
, final Map
<Object
, List
<NodeAction
>> nodeActions
) {
3509 final List
<NodeAction
> actions
= nodeActions
.get(element
);
3510 nodeActions
.remove(element
);
3512 if (elementToPutNodeActionsFor
!= null && actions
!= null) {
3513 nodeActions
.put(elementToPutNodeActionsFor
, actions
);
3517 private DefaultMutableTreeNode
getFirstNode(Object element
) {
3518 return findNode(element
, 0);
3521 private DefaultMutableTreeNode
findNode(final Object element
, int startIndex
) {
3522 final Object value
= getBuilder().findNodeByElement(element
);
3523 if (value
== null) {
3526 if (value
instanceof DefaultMutableTreeNode
) {
3527 return startIndex
== 0 ?
(DefaultMutableTreeNode
)value
: null;
3529 final List
<DefaultMutableTreeNode
> nodes
= (List
<DefaultMutableTreeNode
>)value
;
3530 return startIndex
< nodes
.size() ? nodes
.get(startIndex
) : null;
3533 protected Object
findNodeByElement(Object element
) {
3534 if (myElementToNodeMap
.containsKey(element
)) {
3535 return myElementToNodeMap
.get(element
);
3539 TREE_NODE_WRAPPER
.setValue(element
);
3540 return myElementToNodeMap
.get(TREE_NODE_WRAPPER
);
3543 TREE_NODE_WRAPPER
.setValue(null);
3547 private DefaultMutableTreeNode
findNodeForChildElement(DefaultMutableTreeNode parentNode
, Object element
) {
3548 final Object value
= myElementToNodeMap
.get(element
);
3549 if (value
== null) {
3553 if (value
instanceof DefaultMutableTreeNode
) {
3554 final DefaultMutableTreeNode elementNode
= (DefaultMutableTreeNode
)value
;
3555 return parentNode
.equals(elementNode
.getParent()) ? elementNode
: null;
3558 final List
<DefaultMutableTreeNode
> allNodesForElement
= (List
<DefaultMutableTreeNode
>)value
;
3559 for (final DefaultMutableTreeNode elementNode
: allNodesForElement
) {
3560 if (parentNode
.equals(elementNode
.getParent())) {
3568 public void cancelBackgroundLoading() {
3569 if (myWorker
!= null) {
3570 myWorker
.cancelTasks();
3577 private void addNodeAction(Object element
, NodeAction action
, boolean shouldChildrenBeReady
) {
3578 _addNodeAction(element
, action
, myNodeActions
);
3579 if (shouldChildrenBeReady
) {
3580 _addNodeAction(element
, action
, myNodeChildrenActions
);
3585 private void _addNodeAction(Object element
, NodeAction action
, Map
<Object
, List
<NodeAction
>> map
) {
3586 maybeSetBusyAndScheduleWaiterForReady(true);
3587 List
<NodeAction
> list
= map
.get(element
);
3589 list
= new ArrayList
<NodeAction
>();
3590 map
.put(element
, list
);
3596 private void cleanUpNow() {
3597 if (isReleaseRequested()) return;
3599 final UpdaterTreeState state
= new UpdaterTreeState(this);
3601 myTree
.collapsePath(new TreePath(myTree
.getModel().getRoot()));
3602 myTree
.clearSelection();
3603 getRootNode().removeAllChildren();
3605 myRootNodeWasInitialized
= false;
3607 myElementToNodeMap
.clear();
3608 myDeferredSelections
.clear();
3609 myDeferredExpansions
.clear();
3610 myLoadedInBackground
.clear();
3611 myUnbuiltNodes
.clear();
3612 myUpdateFromRootRequested
= true;
3614 if (myWorker
!= null) {
3615 Disposer
.dispose(myWorker
);
3619 myTree
.invalidate();
3621 state
.restore(null);
3624 public AbstractTreeUi
setClearOnHideDelay(final long clearOnHideDelay
) {
3625 myClearOnHideDelay
= clearOnHideDelay
;
3629 public void setJantorPollPeriod(final long time
) {
3630 myJanitorPollPeriod
= time
;
3633 public void setCheckStructure(final boolean checkStructure
) {
3634 myCheckStructure
= checkStructure
;
3637 private class MySelectionListener
implements TreeSelectionListener
{
3638 public void valueChanged(final TreeSelectionEvent e
) {
3639 dropUpdaterStateIfExternalChange();
3644 private class MyExpansionListener
implements TreeExpansionListener
{
3645 public void treeExpanded(TreeExpansionEvent event
) {
3646 dropUpdaterStateIfExternalChange();
3648 TreePath path
= event
.getPath();
3650 if (myRequestedExpand
!= null && !myRequestedExpand
.equals(path
)) return;
3652 final DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
3654 if (!myUnbuiltNodes
.contains(node
)) {
3655 removeLoading(node
, false);
3657 Set
<DefaultMutableTreeNode
> childrenToUpdate
= new HashSet
<DefaultMutableTreeNode
>();
3658 for (int i
= 0; i
< node
.getChildCount(); i
++) {
3659 DefaultMutableTreeNode each
= (DefaultMutableTreeNode
)node
.getChildAt(i
);
3660 if (myUnbuiltNodes
.contains(each
)) {
3661 makeLoadingOrLeafIfNoChildren(each
);
3662 childrenToUpdate
.add(each
);
3666 if (childrenToUpdate
.size() > 0) {
3667 for (DefaultMutableTreeNode each
: childrenToUpdate
) {
3668 maybeUpdateSubtreeToUpdate(each
);
3673 getBuilder().expandNodeChildren(node
);
3676 processSmartExpand(node
, canSmartExpand(node
, true), false);
3677 processNodeActionsIfReady(node
);
3680 public void treeCollapsed(TreeExpansionEvent e
) {
3681 dropUpdaterStateIfExternalChange();
3683 final TreePath path
= e
.getPath();
3684 final DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
3685 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
3688 TreePath pathToSelect
= null;
3689 if (isSelectionInside(node
)) {
3690 pathToSelect
= new TreePath(node
.getPath());
3694 NodeDescriptor descriptor
= getDescriptorFrom(node
);
3695 if (getBuilder().isDisposeOnCollapsing(descriptor
)) {
3696 runDone(new Runnable() {
3698 if (isDisposed(node
)) return;
3700 TreePath nodePath
= new TreePath(node
.getPath());
3701 if (myTree
.isExpanded(nodePath
)) return;
3703 removeChildren(node
);
3704 makeLoadingOrLeafIfNoChildren(node
);
3707 if (node
.equals(getRootNode())) {
3708 if (myTree
.isRootVisible()) {
3709 //todo kirillk to investigate -- should be done by standard selction move
3710 //addSelectionPath(new TreePath(getRootNode().getPath()), true, Condition.FALSE);
3714 myTreeModel
.reload(node
);
3718 if (pathToSelect
!= null && myTree
.isSelectionEmpty()) {
3719 addSelectionPath(pathToSelect
, true, Condition
.FALSE
, null);
3723 private void removeChildren(DefaultMutableTreeNode node
) {
3724 EnumerationCopy copy
= new EnumerationCopy(node
.children());
3725 while (copy
.hasMoreElements()) {
3726 disposeNode((DefaultMutableTreeNode
)copy
.nextElement());
3728 node
.removeAllChildren();
3729 myTreeModel
.nodeStructureChanged(node
);
3733 private void maybeUpdateSubtreeToUpdate(final DefaultMutableTreeNode subtreeRoot
) {
3734 if (!myUnbuiltNodes
.contains(subtreeRoot
)) return;
3735 TreePath path
= getPathFor(subtreeRoot
);
3737 if (myTree
.getRowForPath(path
) == -1) return;
3739 DefaultMutableTreeNode parent
= getParentBuiltNode(subtreeRoot
);
3740 if (parent
== null) {
3741 addSubtreeToUpdate(subtreeRoot
);
3742 } else if (parent
!= subtreeRoot
) {
3743 addNodeAction(getElementFor(subtreeRoot
), new NodeAction() {
3744 public void onReady(DefaultMutableTreeNode parent
) {
3745 maybeUpdateSubtreeToUpdate(subtreeRoot
);
3751 private boolean isSelectionInside(DefaultMutableTreeNode parent
) {
3752 TreePath path
= new TreePath(myTreeModel
.getPathToRoot(parent
));
3753 TreePath
[] paths
= myTree
.getSelectionPaths();
3754 if (paths
== null) return false;
3755 for (TreePath path1
: paths
) {
3756 if (path
.isDescendant(path1
)) return true;
3761 public boolean isInStructure(@Nullable Object element
) {
3762 Object eachParent
= element
;
3763 while (eachParent
!= null) {
3764 if (getTreeStructure().getRootElement().equals(eachParent
)) return true;
3765 eachParent
= getTreeStructure().getParentElement(eachParent
);
3771 interface NodeAction
{
3772 void onReady(DefaultMutableTreeNode node
);
3775 public void setCanYield(final boolean canYield
) {
3776 myCanYield
= canYield
;
3779 public Collection
<TreeUpdatePass
> getYeildingPasses() {
3780 return myYeildingPasses
;
3783 public boolean isBuilt(Object element
) {
3784 if (!myElementToNodeMap
.containsKey(element
)) return false;
3785 final Object node
= myElementToNodeMap
.get(element
);
3786 return !myUnbuiltNodes
.contains(node
);
3789 static class LoadedChildren
{
3791 private List myElements
;
3792 private Map
<Object
, NodeDescriptor
> myDescriptors
= new HashMap
<Object
, NodeDescriptor
>();
3793 private Map
<NodeDescriptor
, Boolean
> myChanges
= new HashMap
<NodeDescriptor
, Boolean
>();
3795 LoadedChildren(Object
[] elements
) {
3796 myElements
= Arrays
.asList(elements
!= null ? elements
: new Object
[0]);
3799 void putDescriptor(Object element
, NodeDescriptor descriptor
, boolean isChanged
) {
3800 assert myElements
.contains(element
);
3801 myDescriptors
.put(element
, descriptor
);
3802 myChanges
.put(descriptor
, isChanged
);
3805 List
getElements() {
3809 NodeDescriptor
getDescriptor(Object element
) {
3810 return myDescriptors
.get(element
);
3814 public String
toString() {
3815 return Arrays
.asList(myElements
) + "->" + myChanges
;
3818 public boolean isUpdated(Object element
) {
3819 NodeDescriptor desc
= getDescriptor(element
);
3820 return myChanges
.get(desc
);
3824 UpdaterTreeState
getUpdaterState() {
3825 return myUpdaterState
;
3828 private ActionCallback
addReadyCallback(Object requestor
) {
3829 synchronized (myReadyCallbacks
) {
3830 ActionCallback cb
= myReadyCallbacks
.get(requestor
);
3832 cb
= new ActionCallback();
3833 myReadyCallbacks
.put(requestor
, cb
);
3840 private ActionCallback
[] getReadyCallbacks(boolean clear
) {
3841 synchronized (myReadyCallbacks
) {
3842 ActionCallback
[] result
= myReadyCallbacks
.values().toArray(new ActionCallback
[myReadyCallbacks
.size()]);
3844 myReadyCallbacks
.clear();
3850 private long getComparatorStamp() {
3851 if (myNodeDescriptorComparator
instanceof NodeDescriptor
.NodeComparator
) {
3852 long currentComparatorStamp
= ((NodeDescriptor
.NodeComparator
)myNodeDescriptorComparator
).getStamp();
3853 if (currentComparatorStamp
> myLastComparatorStamp
) {
3854 myOwnComparatorStamp
= Math
.max(myOwnComparatorStamp
, currentComparatorStamp
) + 1;
3856 myLastComparatorStamp
= currentComparatorStamp
;
3858 return Math
.max(currentComparatorStamp
, myOwnComparatorStamp
);
3861 return myOwnComparatorStamp
;
3865 public void incComparatorStamp() {
3866 myOwnComparatorStamp
= getComparatorStamp() + 1;
3869 public static class UpdateInfo
{
3870 NodeDescriptor myDescriptor
;
3871 TreeUpdatePass myPass
;
3872 boolean myCanSmartExpand
;
3873 boolean myWasExpanded
;
3874 boolean myForceUpdate
;
3875 boolean myDescriptorIsUpToDate
;
3877 public UpdateInfo(NodeDescriptor descriptor
,
3878 TreeUpdatePass pass
,
3879 boolean canSmartExpand
,
3880 boolean wasExpanded
,
3881 boolean forceUpdate
,
3882 boolean descriptorIsUpToDate
) {
3883 myDescriptor
= descriptor
;
3885 myCanSmartExpand
= canSmartExpand
;
3886 myWasExpanded
= wasExpanded
;
3887 myForceUpdate
= forceUpdate
;
3888 myDescriptorIsUpToDate
= descriptorIsUpToDate
;
3891 synchronized NodeDescriptor
getDescriptor() {
3892 return myDescriptor
;
3895 synchronized TreeUpdatePass
getPass() {
3899 synchronized boolean isCanSmartExpand() {
3900 return myCanSmartExpand
;
3903 synchronized boolean isWasExpanded() {
3904 return myWasExpanded
;
3907 synchronized boolean isForceUpdate() {
3908 return myForceUpdate
;
3911 synchronized boolean isDescriptorIsUpToDate() {
3912 return myDescriptorIsUpToDate
;
3915 public synchronized void apply(UpdateInfo updateInfo
) {
3916 myDescriptor
= updateInfo
.myDescriptor
;
3917 myPass
= updateInfo
.myPass
;
3918 myCanSmartExpand
= updateInfo
.myCanSmartExpand
;
3919 myWasExpanded
= updateInfo
.myWasExpanded
;
3920 myForceUpdate
= updateInfo
.myForceUpdate
;
3921 myDescriptorIsUpToDate
= updateInfo
.myDescriptorIsUpToDate
;
3924 public String
toString() {
3925 return "UpdateInfo: desc=" + myDescriptor
+ " pass=" + myPass
+ " canSmartExpand=" + myCanSmartExpand
+ " wasExpanded=" + myWasExpanded
+ " forceUpdate=" + myForceUpdate
+ " descriptorUpToDate=" + myDescriptorIsUpToDate
;
3930 public void setPassthroughMode(boolean passthrough
) {
3931 myPassthroughMode
= passthrough
;
3932 AbstractTreeUpdater updater
= getUpdater();
3934 if (updater
!= null) {
3935 updater
.setPassThroughMode(myPassthroughMode
);
3938 if (!isUnitTestingMode() && passthrough
) {
3939 // TODO: this assertion should be restored back as soon as possible [JamTreeTableView should be rewritten, etc]
3940 //LOG.error("Pass-through mode for TreeUi is allowed only for unit test mode");
3944 public boolean isPassthroughMode() {
3945 return myPassthroughMode
;
3948 private boolean isUnitTestingMode() {
3949 Application app
= ApplicationManager
.getApplication();
3950 return app
!= null && app
.isUnitTestMode();