2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.ide
.util
.treeView
;
18 import com
.intellij
.ide
.IdeBundle
;
19 import com
.intellij
.openapi
.application
.Application
;
20 import com
.intellij
.openapi
.application
.ApplicationManager
;
21 import com
.intellij
.openapi
.diagnostic
.Logger
;
22 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
23 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
24 import com
.intellij
.openapi
.progress
.ProgressManager
;
25 import com
.intellij
.openapi
.project
.IndexNotReadyException
;
26 import com
.intellij
.openapi
.util
.*;
27 import com
.intellij
.openapi
.util
.registry
.Registry
;
28 import com
.intellij
.openapi
.util
.registry
.RegistryValue
;
29 import com
.intellij
.ui
.LoadingNode
;
30 import com
.intellij
.ui
.treeStructure
.Tree
;
31 import com
.intellij
.util
.Alarm
;
32 import com
.intellij
.util
.ArrayUtil
;
33 import com
.intellij
.util
.ConcurrencyUtil
;
34 import com
.intellij
.util
.Time
;
35 import com
.intellij
.util
.concurrency
.WorkerThread
;
36 import com
.intellij
.util
.containers
.HashSet
;
37 import com
.intellij
.util
.enumeration
.EnumerationCopy
;
38 import com
.intellij
.util
.ui
.UIUtil
;
39 import com
.intellij
.util
.ui
.tree
.TreeUtil
;
40 import com
.intellij
.util
.ui
.update
.Activatable
;
41 import com
.intellij
.util
.ui
.update
.UiNotifyConnector
;
42 import org
.jetbrains
.annotations
.NotNull
;
43 import org
.jetbrains
.annotations
.Nullable
;
46 import javax
.swing
.event
.TreeExpansionEvent
;
47 import javax
.swing
.event
.TreeExpansionListener
;
48 import javax
.swing
.event
.TreeSelectionEvent
;
49 import javax
.swing
.event
.TreeSelectionListener
;
50 import javax
.swing
.tree
.*;
52 import java
.awt
.event
.FocusAdapter
;
53 import java
.awt
.event
.FocusEvent
;
54 import java
.security
.AccessControlException
;
56 import java
.util
.List
;
57 import java
.util
.concurrent
.ScheduledExecutorService
;
58 import java
.util
.concurrent
.TimeUnit
;
60 public class AbstractTreeUi
{
61 private static final Logger LOG
= Logger
.getInstance("#com.intellij.ide.util.treeView.AbstractTreeBuilder");
62 protected JTree myTree
;// protected for TestNG
63 @SuppressWarnings({"WeakerAccess"}) protected DefaultTreeModel myTreeModel
;
64 private AbstractTreeStructure myTreeStructure
;
65 private AbstractTreeUpdater myUpdater
;
67 private Comparator
<NodeDescriptor
> myNodeDescriptorComparator
;
68 private final Comparator
<TreeNode
> myNodeComparator
= new Comparator
<TreeNode
>() {
69 public int compare(TreeNode n1
, TreeNode n2
) {
70 if (isLoadingNode(n1
) || isLoadingNode(n2
)) return 0;
71 NodeDescriptor nodeDescriptor1
= getDescriptorFrom(((DefaultMutableTreeNode
)n1
));
72 NodeDescriptor nodeDescriptor2
= getDescriptorFrom(((DefaultMutableTreeNode
)n2
));
73 return myNodeDescriptorComparator
!= null
74 ? myNodeDescriptorComparator
.compare(nodeDescriptor1
, nodeDescriptor2
)
75 : nodeDescriptor1
.getIndex() - nodeDescriptor2
.getIndex();
78 long myOwnComparatorStamp
;
79 long myLastComparatorStamp
;
81 private DefaultMutableTreeNode myRootNode
;
82 private final HashMap
<Object
, Object
> myElementToNodeMap
= new HashMap
<Object
, Object
>();
83 private final HashSet
<DefaultMutableTreeNode
> myUnbuiltNodes
= new HashSet
<DefaultMutableTreeNode
>();
84 private TreeExpansionListener myExpansionListener
;
85 private MySelectionListener mySelectionListener
;
87 private WorkerThread myWorker
= null;
88 private final Set
<Runnable
> myActiveWorkerTasks
= new HashSet
<Runnable
>();
90 private ProgressIndicator myProgress
;
91 private static final int WAIT_CURSOR_DELAY
= 100;
92 private AbstractTreeNode
<Object
> TREE_NODE_WRAPPER
;
93 private boolean myRootNodeWasInitialized
= false;
94 private final Map
<Object
, List
<NodeAction
>> myNodeActions
= new HashMap
<Object
, List
<NodeAction
>>();
95 private boolean myUpdateFromRootRequested
;
96 private boolean myWasEverShown
;
97 private boolean myUpdateIfInactive
;
99 private final Map
<Object
, UpdateInfo
> myLoadedInBackground
= new HashMap
<Object
, UpdateInfo
>();
100 private final Map
<Object
, List
<NodeAction
>> myNodeChildrenActions
= new HashMap
<Object
, List
<NodeAction
>>();
102 private long myClearOnHideDelay
= -1;
103 private ScheduledExecutorService ourClearanceService
;
104 private final Map
<AbstractTreeUi
, Long
> ourUi2Countdown
= Collections
.synchronizedMap(new WeakHashMap
<AbstractTreeUi
, Long
>());
106 private final Set
<Runnable
> myDeferredSelections
= new HashSet
<Runnable
>();
107 private final Set
<Runnable
> myDeferredExpansions
= new HashSet
<Runnable
>();
109 private boolean myCanProcessDeferredSelections
;
111 private UpdaterTreeState myUpdaterState
;
112 private AbstractTreeBuilder myBuilder
;
114 private final Set
<DefaultMutableTreeNode
> myUpdatingChildren
= new HashSet
<DefaultMutableTreeNode
>();
115 private long myJanitorPollPeriod
= Time
.SECOND
* 10;
116 private boolean myCheckStructure
= false;
119 private boolean myCanYield
= false;
121 private final List
<TreeUpdatePass
> myYeildingPasses
= new ArrayList
<TreeUpdatePass
>();
123 private boolean myYeildingNow
;
125 private final Set
<DefaultMutableTreeNode
> myPendingNodeActions
= new HashSet
<DefaultMutableTreeNode
>();
126 private final Set
<Runnable
> myYeildingDoneRunnables
= new HashSet
<Runnable
>();
128 private final Alarm myBusyAlarm
= new Alarm();
129 private final Runnable myWaiterForReady
= new Runnable() {
131 maybeSetBusyAndScheduleWaiterForReady(false);
135 private final RegistryValue myYeildingUpdate
= Registry
.get("ide.tree.yeildingUiUpdate");
136 private final RegistryValue myShowBusyIndicator
= Registry
.get("ide.tree.showBusyIndicator");
137 private final RegistryValue myWaitForReadyTime
= Registry
.get("ide.tree.waitForReadyTimout");
139 private boolean myWasEverIndexNotReady
;
140 private boolean myShowing
;
141 private FocusAdapter myFocusListener
= new FocusAdapter() {
143 public void focusGained(FocusEvent e
) {
147 private Set
<DefaultMutableTreeNode
> myNotForSmartExpand
= new HashSet
<DefaultMutableTreeNode
>();
148 private TreePath myRequestedExpand
;
149 private ActionCallback myInitialized
= new ActionCallback();
150 private Map
<Object
, ActionCallback
> myReadyCallbacks
= new WeakHashMap
<Object
, ActionCallback
>();
152 private boolean myPassthroughMode
= false;
155 protected void init(AbstractTreeBuilder builder
,
157 DefaultTreeModel treeModel
,
158 AbstractTreeStructure treeStructure
,
159 @Nullable Comparator
<NodeDescriptor
> comparator
,
160 boolean updateIfInactive
) {
163 myTreeModel
= treeModel
;
164 TREE_NODE_WRAPPER
= getBuilder().createSearchingTreeNodeWrapper();
165 myTree
.setModel(myTreeModel
);
166 setRootNode((DefaultMutableTreeNode
)treeModel
.getRoot());
167 setTreeStructure(treeStructure
);
168 myNodeDescriptorComparator
= comparator
;
169 myUpdateIfInactive
= updateIfInactive
;
171 myExpansionListener
= new MyExpansionListener();
172 myTree
.addTreeExpansionListener(myExpansionListener
);
174 mySelectionListener
= new MySelectionListener();
175 myTree
.addTreeSelectionListener(mySelectionListener
);
177 setUpdater(getBuilder().createUpdater());
178 myProgress
= getBuilder().createProgressIndicator();
179 Disposer
.register(getBuilder(), getUpdater());
181 final UiNotifyConnector uiNotify
= new UiNotifyConnector(tree
, new Activatable() {
182 public void showNotify() {
184 myWasEverShown
= true;
190 public void hideNotify() {
197 Disposer
.register(getBuilder(), uiNotify
);
199 myTree
.addFocusListener(myFocusListener
);
203 boolean isNodeActionsPending() {
204 return !myNodeActions
.isEmpty() || !myNodeChildrenActions
.isEmpty();
207 private void clearNodeActions() {
208 myNodeActions
.clear();
209 myNodeChildrenActions
.clear();
212 private void maybeSetBusyAndScheduleWaiterForReady(boolean forcedBusy
) {
213 if (!myShowBusyIndicator
.asBoolean() || !canYield()) return;
215 if (myTree
instanceof com
.intellij
.ui
.treeStructure
.Tree
) {
216 final com
.intellij
.ui
.treeStructure
.Tree tree
= (Tree
)myTree
;
217 final boolean isBusy
= !isReady() || forcedBusy
;
218 if (isBusy
&& tree
.isShowing()) {
219 tree
.setPaintBusy(true);
220 myBusyAlarm
.cancelAllRequests();
221 myBusyAlarm
.addRequest(myWaiterForReady
, myWaitForReadyTime
.asInteger());
224 tree
.setPaintBusy(false);
229 private void initClearanceServiceIfNeeded() {
230 if (ourClearanceService
!= null) return;
232 ourClearanceService
= ConcurrencyUtil
.newSingleScheduledThreadExecutor("AbstractTreeBuilder's janitor");
233 ourClearanceService
.scheduleWithFixedDelay(new Runnable() {
237 }, myJanitorPollPeriod
, myJanitorPollPeriod
, TimeUnit
.MILLISECONDS
);
240 private void cleanUpAll() {
241 final long now
= System
.currentTimeMillis();
242 final AbstractTreeUi
[] uis
= ourUi2Countdown
.keySet().toArray(new AbstractTreeUi
[ourUi2Countdown
.size()]);
243 for (AbstractTreeUi eachUi
: uis
) {
244 if (eachUi
== null) continue;
245 final Long timeToCleanup
= ourUi2Countdown
.get(eachUi
);
246 if (timeToCleanup
== null) continue;
247 if (now
>= timeToCleanup
.longValue()) {
248 ourUi2Countdown
.remove(eachUi
);
249 Runnable runnable
= new Runnable() {
251 getBuilder().cleanUp();
254 if (isPassthroughMode()) {
257 UIUtil
.invokeAndWaitIfNeeded(runnable
);
263 protected void doCleanUp() {
264 Runnable cleanup
= new Runnable() {
272 if (isPassthroughMode()) {
275 UIUtil
.invokeLaterIfNeeded(cleanup
);
279 private void disposeClearanceService() {
281 if (ourClearanceService
!= null) {
282 ourClearanceService
.shutdown();
283 ourClearanceService
= null;
286 catch (AccessControlException e
) {
291 public void activate(boolean byShowing
) {
292 myCanProcessDeferredSelections
= true;
293 ourUi2Countdown
.remove(this);
295 if (!myWasEverShown
|| myUpdateFromRootRequested
|| myUpdateIfInactive
) {
296 getBuilder().updateFromRoot();
299 getUpdater().showNotify();
301 myWasEverShown
|= byShowing
;
304 public void deactivate() {
305 getUpdater().hideNotify();
306 myBusyAlarm
.cancelAllRequests();
308 if (!myWasEverShown
) return;
310 if (isNodeActionsPending()) {
311 cancelBackgroundLoading();
312 myUpdateFromRootRequested
= true;
315 if (getClearOnHideDelay() >= 0) {
316 ourUi2Countdown
.put(this, System
.currentTimeMillis() + getClearOnHideDelay());
317 initClearanceServiceIfNeeded();
322 public void release() {
323 if (isReleased()) return;
325 myTree
.removeTreeExpansionListener(myExpansionListener
);
326 myTree
.removeTreeSelectionListener(mySelectionListener
);
327 myTree
.removeFocusListener(myFocusListener
);
329 disposeNode(getRootNode());
330 myElementToNodeMap
.clear();
331 getUpdater().cancelAllRequests();
332 if (myWorker
!= null) {
333 myWorker
.dispose(true);
336 TREE_NODE_WRAPPER
.setValue(null);
337 if (myProgress
!= null) {
340 disposeClearanceService();
345 //todo [kirillk] afraid to do so just in release day, to uncomment
346 // myTreeStructure = null;
351 myDeferredSelections
.clear();
352 myDeferredExpansions
.clear();
353 myYeildingDoneRunnables
.clear();
356 public boolean isReleased() {
357 return myBuilder
== null;
360 protected void doExpandNodeChildren(final DefaultMutableTreeNode node
) {
361 getTreeStructure().commit();
362 addSubtreeToUpdate(node
);
363 getUpdater().performUpdate();
366 public final AbstractTreeStructure
getTreeStructure() {
367 return myTreeStructure
;
370 public final JTree
getTree() {
375 private NodeDescriptor
getDescriptorFrom(DefaultMutableTreeNode node
) {
376 return (NodeDescriptor
)node
.getUserObject();
380 public final DefaultMutableTreeNode
getNodeForElement(Object element
, final boolean validateAgainstStructure
) {
381 DefaultMutableTreeNode result
= null;
382 if (validateAgainstStructure
) {
385 final DefaultMutableTreeNode node
= findNode(element
, index
);
386 if (node
== null) break;
388 if (isNodeValidForElement(element
, node
)) {
397 result
= getFirstNode(element
);
401 if (result
!= null && !isNodeInStructure(result
)) {
409 private boolean isNodeInStructure(DefaultMutableTreeNode node
) {
410 return TreeUtil
.isAncestor(getRootNode(), node
) && getRootNode() == myTreeModel
.getRoot();
413 private boolean isNodeValidForElement(final Object element
, final DefaultMutableTreeNode node
) {
414 return isSameHierarchy(element
, node
) || isValidChildOfParent(element
, node
);
417 private boolean isValidChildOfParent(final Object element
, final DefaultMutableTreeNode node
) {
418 final DefaultMutableTreeNode parent
= (DefaultMutableTreeNode
)node
.getParent();
419 final Object parentElement
= getElementFor(parent
);
420 if (!isInStructure(parentElement
)) return false;
422 if (parent
instanceof ElementNode
) {
423 return ((ElementNode
)parent
).isValidChild(element
);
426 for (int i
= 0; i
< parent
.getChildCount(); i
++) {
427 final TreeNode child
= parent
.getChildAt(i
);
428 final Object eachElement
= getElementFor(child
);
429 if (element
.equals(eachElement
)) return true;
436 private boolean isSameHierarchy(Object eachParent
, DefaultMutableTreeNode eachParentNode
) {
437 boolean valid
= true;
439 if (eachParent
== null) {
440 valid
= eachParentNode
== null;
444 if (!eachParent
.equals(getElementFor(eachParentNode
))) {
449 eachParent
= getTreeStructure().getParentElement(eachParent
);
450 eachParentNode
= (DefaultMutableTreeNode
)eachParentNode
.getParent();
455 public final DefaultMutableTreeNode
getNodeForPath(Object
[] path
) {
456 DefaultMutableTreeNode node
= null;
457 for (final Object pathElement
: path
) {
458 node
= node
== null ?
getFirstNode(pathElement
) : findNodeForChildElement(node
, pathElement
);
466 public final void buildNodeForElement(Object element
) {
467 getUpdater().performUpdate();
468 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
470 final java
.util
.List
<Object
> elements
= new ArrayList
<Object
>();
472 element
= getTreeStructure().getParentElement(element
);
473 if (element
== null) {
476 elements
.add(0, element
);
479 for (final Object element1
: elements
) {
480 node
= getNodeForElement(element1
, false);
488 public final void buildNodeForPath(Object
[] path
) {
489 getUpdater().performUpdate();
490 DefaultMutableTreeNode node
= null;
491 for (final Object pathElement
: path
) {
492 node
= node
== null ?
getFirstNode(pathElement
) : findNodeForChildElement(node
, pathElement
);
493 if (node
!= null && node
!= path
[path
.length
- 1]) {
499 public final void setNodeDescriptorComparator(Comparator
<NodeDescriptor
> nodeDescriptorComparator
) {
500 myNodeDescriptorComparator
= nodeDescriptorComparator
;
501 myLastComparatorStamp
= -1;
502 getBuilder().queueUpdateFrom(getTreeStructure().getRootElement(), true);
505 protected AbstractTreeBuilder
getBuilder() {
509 protected final void initRootNode() {
510 if (myUpdateIfInactive
) {
514 myUpdateFromRootRequested
= true;
518 private void initRootNodeNowIfNeeded(final TreeUpdatePass pass
) {
519 if (myRootNodeWasInitialized
) return;
521 myRootNodeWasInitialized
= true;
523 final Object rootElement
= getTreeStructure().getRootElement();
524 addNodeAction(rootElement
, new NodeAction() {
525 public void onReady(final DefaultMutableTreeNode node
) {
526 processDeferredActions();
531 final Ref
<NodeDescriptor
> rootDescriptor
= new Ref
<NodeDescriptor
>(null);
532 final boolean bgLoading
= getTreeStructure().isToBuildChildrenInBackground(rootElement
);
534 Runnable build
= new Runnable() {
536 rootDescriptor
.set(getTreeStructure().createDescriptor(rootElement
, null));
537 getRootNode().setUserObject(rootDescriptor
.get());
538 update(rootDescriptor
.get(), true);
543 Runnable update
= new Runnable() {
545 if (getElementFromDescriptor(rootDescriptor
.get()) != null) {
546 createMapping(getElementFromDescriptor(rootDescriptor
.get()), getRootNode());
550 insertLoadingNode(getRootNode(), true);
552 boolean willUpdate
= false;
553 if (isAutoExpand(rootDescriptor
.get())) {
554 willUpdate
= myUnbuiltNodes
.contains(getRootNode());
555 expand(getRootNode(), true);
558 updateNodeChildren(getRootNode(), pass
, null, false, false, false, true);
560 if (getRootNode().getChildCount() == 0) {
561 myTreeModel
.nodeChanged(getRootNode());
567 queueToBackground(build
, update
, null);
575 private boolean isAutoExpand(NodeDescriptor descriptor
) {
576 boolean autoExpand
= false;
578 if (descriptor
!= null) {
579 autoExpand
= getBuilder().isAutoExpandNode(descriptor
);
582 if (!autoExpand
&& !myTree
.isRootVisible()) {
583 Object element
= getElementFromDescriptor(descriptor
);
584 if (element
!= null && element
.equals(getTreeStructure().getRootElement())) return true;
590 private boolean isAutoExpand(DefaultMutableTreeNode node
) {
591 return isAutoExpand(getDescriptorFrom(node
));
594 private AsyncResult
<Boolean
> update(final NodeDescriptor nodeDescriptor
, boolean now
) {
595 final AsyncResult
<Boolean
> result
= new AsyncResult
<Boolean
>();
597 if (now
|| isPassthroughMode()) {
598 return new AsyncResult
<Boolean
>().setDone(_update(nodeDescriptor
));
601 Object element
= getElementFromDescriptor(nodeDescriptor
);
602 boolean bgLoading
= getTreeStructure().isToBuildChildrenInBackground(element
);
604 boolean edt
= isEdt();
607 final Ref
<Boolean
> changes
= new Ref
<Boolean
>(false);
608 queueToBackground(new Runnable() {
610 changes
.set(_update(nodeDescriptor
));
614 result
.setDone(changes
.get());
619 result
.setDone(_update(nodeDescriptor
));
623 if (edt
|| !myWasEverShown
) {
624 result
.setDone(_update(nodeDescriptor
));
627 UIUtil
.invokeLaterIfNeeded(new Runnable() {
630 result
.setDone(_update(nodeDescriptor
));
633 result
.setRejected();
640 result
.doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
641 public void run(Boolean changes
) {
643 final long updateStamp
= nodeDescriptor
.getUpdateCount();
644 UIUtil
.invokeLaterIfNeeded(new Runnable() {
646 Object element
= nodeDescriptor
.getElement();
647 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
649 TreePath path
= getPathFor(node
);
650 if (path
!= null && myTree
.isVisible(path
)) {
651 updateNodeImageAndPosition(node
, false);
664 private boolean _update(NodeDescriptor nodeDescriptor
) {
665 nodeDescriptor
.setUpdateCount(nodeDescriptor
.getUpdateCount() + 1);
666 return getBuilder().updateNodeDescriptor(nodeDescriptor
);
669 private void assertIsDispatchThread() {
670 if (isPassthroughMode()) return;
672 if (isTreeShowing() && !isEdt()) {
673 LOG
.error("Must be in event-dispatch thread");
677 private boolean isEdt() {
678 return SwingUtilities
.isEventDispatchThread();
681 private boolean isTreeShowing() {
685 private void assertNotDispatchThread() {
686 if (isPassthroughMode()) return;
689 LOG
.error("Must not be in event-dispatch thread");
693 private void processDeferredActions() {
694 processDeferredActions(myDeferredSelections
);
695 processDeferredActions(myDeferredExpansions
);
698 private void processDeferredActions(Set
<Runnable
> actions
) {
699 final Runnable
[] runnables
= actions
.toArray(new Runnable
[actions
.size()]);
701 for (Runnable runnable
: runnables
) {
706 //todo: to make real callback
707 public ActionCallback
queueUpdate(Object element
) {
708 AbstractTreeUpdater updater
= getUpdater();
709 if (updater
== null) {
710 return new ActionCallback
.Rejected();
713 final ActionCallback result
= new ActionCallback();
714 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
716 addSubtreeToUpdate(node
);
719 addSubtreeToUpdate(getRootNode());
722 updater
.runAfterUpdate(new Runnable() {
730 public void doUpdateFromRoot() {
731 updateSubtree(getRootNode(), false);
734 public ActionCallback
doUpdateFromRootCB() {
735 final ActionCallback cb
= new ActionCallback();
736 getUpdater().runAfterUpdate(new Runnable() {
741 updateSubtree(getRootNode(), false);
745 public final void updateSubtree(DefaultMutableTreeNode node
, boolean canSmartExpand
) {
746 updateSubtree(new TreeUpdatePass(node
), canSmartExpand
);
749 public final void updateSubtree(TreeUpdatePass pass
, boolean canSmartExpand
) {
750 if (getUpdater() != null) {
751 getUpdater().addSubtreeToUpdate(pass
);
754 updateSubtreeNow(pass
, canSmartExpand
);
758 final void updateSubtreeNow(TreeUpdatePass pass
, boolean canSmartExpand
) {
759 maybeSetBusyAndScheduleWaiterForReady(true);
761 initRootNodeNowIfNeeded(pass
);
763 final DefaultMutableTreeNode node
= pass
.getNode();
765 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
767 setUpdaterState(new UpdaterTreeState(this)).beforeSubtreeUpdate();
769 boolean forceUpdate
= true;
770 TreePath path
= getPathFor(node
);
771 boolean invisible
= !myTree
.isExpanded(path
) && (path
.getParentPath() == null || !myTree
.isExpanded(path
.getParentPath()));
773 if (invisible
&& myUnbuiltNodes
.contains(node
)) {
777 updateNodeChildren(node
, pass
, null, false, canSmartExpand
, forceUpdate
, false);
780 private boolean isToBuildInBackground(NodeDescriptor descriptor
) {
781 return getTreeStructure().isToBuildChildrenInBackground(getBuilder().getTreeStructureElement(descriptor
));
785 UpdaterTreeState
setUpdaterState(UpdaterTreeState state
) {
786 final UpdaterTreeState oldState
= myUpdaterState
;
787 if (oldState
== null) {
788 myUpdaterState
= state
;
792 oldState
.addAll(state
);
797 protected void doUpdateNode(final DefaultMutableTreeNode node
) {
798 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
799 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
800 final Object prevElement
= getElementFromDescriptor(descriptor
);
801 if (prevElement
== null) return;
802 update(descriptor
, false).doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
803 public void run(Boolean changes
) {
804 if (!isValid(descriptor
)) {
805 if (isInStructure(prevElement
)) {
806 getUpdater().addSubtreeToUpdateByElement(getTreeStructure().getParentElement(prevElement
));
811 updateNodeImageAndPosition(node
, true);
817 public Object
getElementFromDescriptor(NodeDescriptor descriptor
) {
818 return getBuilder().getTreeStructureElement(descriptor
);
821 private void updateNodeChildren(final DefaultMutableTreeNode node
,
822 final TreeUpdatePass pass
,
823 @Nullable LoadedChildren loadedChildren
,
825 final boolean toSmartExpand
,
827 final boolean descriptorIsUpToDate
) {
828 getTreeStructure().commit();
829 final boolean wasExpanded
= myTree
.isExpanded(new TreePath(node
.getPath())) || isAutoExpand(node
);
830 final boolean wasLeaf
= node
.getChildCount() == 0;
833 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
834 if (descriptor
== null) {
835 removeLoading(node
, true);
839 boolean bgBuild
= isToBuildInBackground(descriptor
);
840 boolean notRequiredToUpdateChildren
= !forcedNow
&& !wasExpanded
;
842 if (notRequiredToUpdateChildren
&& forceUpdate
&& !wasExpanded
) {
843 boolean alwaysPlus
= getBuilder().isAlwaysShowPlus(descriptor
);
844 if (alwaysPlus
&& wasLeaf
) {
845 notRequiredToUpdateChildren
= false;
847 notRequiredToUpdateChildren
= alwaysPlus
;
851 final Ref
<LoadedChildren
> preloaded
= new Ref
<LoadedChildren
>(loadedChildren
);
852 boolean descriptorWasUpdated
= descriptorIsUpToDate
;
854 if (notRequiredToUpdateChildren
) {
855 if (myUnbuiltNodes
.contains(node
) && node
.getChildCount() == 0) {
856 insertLoadingNode(node
, true);
863 if (myUnbuiltNodes
.contains(node
)) {
864 if (!descriptorWasUpdated
) {
865 update(descriptor
, true);
866 descriptorWasUpdated
= true;
869 if (processAlwaysLeaf(node
)) return;
871 Pair
<Boolean
, LoadedChildren
> unbuilt
= processUnbuilt(node
, descriptor
, pass
, wasExpanded
, null);
872 if (unbuilt
.getFirst()) return;
873 preloaded
.set(unbuilt
.getSecond());
879 final boolean childForceUpdate
= isChildNodeForceUpdate(node
, forceUpdate
, wasExpanded
);
881 if (!forcedNow
&& isToBuildInBackground(descriptor
)) {
882 if (processAlwaysLeaf(node
)) return;
884 queueBackgroundUpdate(
885 new UpdateInfo(descriptor
, pass
, canSmartExpand(node
, toSmartExpand
), wasExpanded
, childForceUpdate
, descriptorWasUpdated
), node
);
889 if (!descriptorWasUpdated
) {
890 update(descriptor
, false).doWhenDone(new Runnable() {
892 if (processAlwaysLeaf(node
)) return;
893 updateNodeChildrenNow(node
, pass
, preloaded
.get(), toSmartExpand
, wasExpanded
, wasLeaf
, childForceUpdate
);
898 if (processAlwaysLeaf(node
)) return;
900 updateNodeChildrenNow(node
, pass
, preloaded
.get(), toSmartExpand
, wasExpanded
, wasLeaf
, childForceUpdate
);
905 processNodeActionsIfReady(node
);
909 private boolean processAlwaysLeaf(DefaultMutableTreeNode node
) {
910 Object element
= getElementFor(node
);
911 if (getTreeStructure().isAlwaysLeaf(element
)) {
912 removeLoading(node
, true);
913 processNodeActionsIfReady(node
);
920 private boolean isChildNodeForceUpdate(DefaultMutableTreeNode node
, boolean parentForceUpdate
, boolean parentExpanded
) {
921 TreePath path
= getPathFor(node
);
922 return parentForceUpdate
&& (parentExpanded
|| myTree
.isExpanded(path
));
925 private void updateNodeChildrenNow(final DefaultMutableTreeNode node
,
926 final TreeUpdatePass pass
,
927 final LoadedChildren preloadedChildren
,
928 final boolean toSmartExpand
,
929 final boolean wasExpanded
,
930 final boolean wasLeaf
,
931 final boolean forceUpdate
) {
932 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
934 final MutualMap
<Object
, Integer
> elementToIndexMap
= loadElementsFromStructure(descriptor
, preloadedChildren
);
935 final LoadedChildren loadedChildren
=
936 preloadedChildren
!= null ? preloadedChildren
: new LoadedChildren(elementToIndexMap
.getKeys().toArray());
940 pass
.setCurrentNode(node
);
942 final boolean canSmartExpand
= canSmartExpand(node
, toSmartExpand
);
944 processExistingNodes(node
, elementToIndexMap
, pass
, canSmartExpand(node
, toSmartExpand
), forceUpdate
, wasExpanded
, preloadedChildren
)
945 .doWhenDone(new Runnable() {
947 if (isDisposed(node
)) {
951 removeLoading(node
, false);
953 final boolean expanded
= isExpanded(node
, wasExpanded
);
955 collectNodesToInsert(descriptor
, elementToIndexMap
, node
, expanded
, loadedChildren
)
956 .doWhenDone(new AsyncResult
.Handler
<ArrayList
<TreeNode
>>() {
957 public void run(ArrayList
<TreeNode
> nodesToInsert
) {
958 insertNodesInto(nodesToInsert
, node
);
959 updateNodesToInsert(nodesToInsert
, pass
, canSmartExpand
, isChildNodeForceUpdate(node
, forceUpdate
, expanded
));
960 removeLoading(node
, true);
962 if (node
.getChildCount() > 0) {
964 expand(node
, canSmartExpand
);
968 removeFromUpdating(node
);
970 final Object element
= getElementFor(node
);
971 addNodeAction(element
, new NodeAction() {
972 public void onReady(final DefaultMutableTreeNode node
) {
973 removeLoading(node
, false);
977 processNodeActionsIfReady(node
);
984 private boolean isDisposed(DefaultMutableTreeNode node
) {
985 return !node
.isNodeAncestor((DefaultMutableTreeNode
)myTree
.getModel().getRoot());
988 private void expand(DefaultMutableTreeNode node
, boolean canSmartExpand
) {
989 expand(new TreePath(node
.getPath()), canSmartExpand
);
992 private void expand(final TreePath path
, boolean canSmartExpand
) {
993 if (path
== null) return;
996 final Object last
= path
.getLastPathComponent();
997 boolean isLeaf
= myTree
.getModel().isLeaf(path
.getLastPathComponent());
998 final boolean isRoot
= last
== myTree
.getModel().getRoot();
999 final TreePath parent
= path
.getParentPath();
1000 if (isRoot
&& !myTree
.isExpanded(path
)) {
1001 if (myTree
.isRootVisible() || myUnbuiltNodes
.contains(last
)) {
1002 insertLoadingNode((DefaultMutableTreeNode
)last
, false);
1004 expandPath(path
, canSmartExpand
);
1006 else if (myTree
.isExpanded(path
) || (isLeaf
&& parent
!= null && myTree
.isExpanded(parent
) && !myUnbuiltNodes
.contains(last
))) {
1007 if (last
instanceof DefaultMutableTreeNode
) {
1008 processNodeActionsIfReady((DefaultMutableTreeNode
)last
);
1012 if (isLeaf
&& myUnbuiltNodes
.contains(last
)) {
1013 insertLoadingNode((DefaultMutableTreeNode
)last
, true);
1014 expandPath(path
, canSmartExpand
);
1016 else if (isLeaf
&& parent
!= null) {
1017 final DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)parent
.getLastPathComponent();
1018 if (parentNode
!= null) {
1019 addToUnbuilt(parentNode
);
1021 expandPath(parent
, canSmartExpand
);
1024 expandPath(path
, canSmartExpand
);
1029 private void addToUnbuilt(DefaultMutableTreeNode node
) {
1030 myUnbuiltNodes
.add(node
);
1033 private void removeFromUnbuilt(DefaultMutableTreeNode node
) {
1034 myUnbuiltNodes
.remove(node
);
1037 private Pair
<Boolean
, LoadedChildren
> processUnbuilt(final DefaultMutableTreeNode node
,
1038 final NodeDescriptor descriptor
,
1039 final TreeUpdatePass pass
,
1041 final LoadedChildren loadedChildren
) {
1042 if (!isExpanded
&& getBuilder().isAlwaysShowPlus(descriptor
)) {
1043 return new Pair
<Boolean
, LoadedChildren
>(true, null);
1046 final Object element
= getElementFor(node
);
1048 final LoadedChildren children
= loadedChildren
!= null ? loadedChildren
: new LoadedChildren(getChildrenFor(element
));
1052 if (children
.getElements().size() == 0) {
1053 removeLoading(node
, true);
1057 if (isAutoExpand(node
)) {
1058 addNodeAction(getElementFor(node
), new NodeAction() {
1059 public void onReady(final DefaultMutableTreeNode node
) {
1060 final TreePath path
= new TreePath(node
.getPath());
1061 if (getTree().isExpanded(path
) || children
.getElements().size() == 0) {
1062 removeLoading(node
, false);
1065 maybeYeild(new ActiveRunnable() {
1066 public ActionCallback
run() {
1067 expand(element
, null);
1068 return new ActionCallback
.Done();
1078 processNodeActionsIfReady(node
);
1080 return new Pair
<Boolean
, LoadedChildren
>(processed
, children
);
1083 private boolean removeIfLoading(TreeNode node
) {
1084 if (isLoadingNode(node
)) {
1085 moveSelectionToParentIfNeeded(node
);
1086 removeNodeFromParent((MutableTreeNode
)node
, false);
1093 private void moveSelectionToParentIfNeeded(TreeNode node
) {
1094 TreePath path
= getPathFor(node
);
1095 if (myTree
.getSelectionModel().isPathSelected(path
)) {
1096 TreePath parentPath
= path
.getParentPath();
1097 myTree
.getSelectionModel().removeSelectionPath(path
);
1098 if (parentPath
!= null) {
1099 myTree
.getSelectionModel().addSelectionPath(parentPath
);
1104 //todo [kirillk] temporary consistency check
1105 private Object
[] getChildrenFor(final Object element
) {
1106 final Object
[] passOne
;
1108 passOne
= getTreeStructure().getChildElements(element
);
1110 catch (IndexNotReadyException e
) {
1111 if (!myWasEverIndexNotReady
) {
1112 myWasEverIndexNotReady
= true;
1113 LOG
.warn("Tree is not dumb-mode-aware; treeBuilder=" + getBuilder() + " treeStructure=" + getTreeStructure());
1115 return ArrayUtil
.EMPTY_OBJECT_ARRAY
;
1118 if (!myCheckStructure
) return passOne
;
1120 final Object
[] passTwo
= getTreeStructure().getChildElements(element
);
1122 final HashSet two
= new HashSet(Arrays
.asList(passTwo
));
1124 if (passOne
.length
!= passTwo
.length
) {
1126 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
1130 for (Object eachInOne
: passOne
) {
1131 if (!two
.contains(eachInOne
)) {
1133 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
1143 private void updateNodesToInsert(final ArrayList
<TreeNode
> nodesToInsert
,
1144 TreeUpdatePass pass
,
1145 boolean canSmartExpand
,
1146 boolean forceUpdate
) {
1147 for (TreeNode aNodesToInsert
: nodesToInsert
) {
1148 DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)aNodesToInsert
;
1149 updateNodeChildren(childNode
, pass
, null, false, canSmartExpand
, forceUpdate
, true);
1153 private ActionCallback
processExistingNodes(final DefaultMutableTreeNode node
,
1154 final MutualMap
<Object
, Integer
> elementToIndexMap
,
1155 final TreeUpdatePass pass
,
1156 final boolean canSmartExpand
,
1157 final boolean forceUpdate
,
1158 final boolean wasExpaned
,
1159 final LoadedChildren preloaded
) {
1161 final ArrayList
<TreeNode
> childNodes
= TreeUtil
.childrenToArray(node
);
1162 return maybeYeild(new ActiveRunnable() {
1163 public ActionCallback
run() {
1164 if (pass
.isExpired()) return new ActionCallback
.Rejected();
1165 if (childNodes
.size() == 0) return new ActionCallback
.Done();
1168 final ActionCallback result
= new ActionCallback(childNodes
.size());
1170 for (TreeNode each
: childNodes
) {
1171 final DefaultMutableTreeNode eachChild
= (DefaultMutableTreeNode
)each
;
1172 if (isLoadingNode(eachChild
)) {
1177 final boolean childForceUpdate
= isChildNodeForceUpdate(eachChild
, forceUpdate
, wasExpaned
);
1179 maybeYeild(new ActiveRunnable() {
1181 public ActionCallback
run() {
1182 return processExistingNode(eachChild
, getDescriptorFrom(eachChild
), node
, elementToIndexMap
, pass
, canSmartExpand
,
1183 childForceUpdate
, preloaded
);
1185 }, pass
, node
).notify(result
);
1187 if (result
.isRejected()) {
1197 private boolean isRerunNeeded(TreeUpdatePass pass
) {
1198 if (pass
.isExpired()) return false;
1200 final boolean rerunBecauseTreeIsHidden
= !pass
.isExpired() && !isTreeShowing() && getUpdater().isInPostponeMode();
1202 return rerunBecauseTreeIsHidden
|| getUpdater().isRerunNeededFor(pass
);
1205 private ActionCallback
maybeYeild(final ActiveRunnable processRunnable
, final TreeUpdatePass pass
, final DefaultMutableTreeNode node
) {
1206 final ActionCallback result
= new ActionCallback();
1208 if (isRerunNeeded(pass
)) {
1209 getUpdater().addSubtreeToUpdate(pass
);
1210 result
.setRejected();
1213 if (isToYieldUpdateFor(node
)) {
1214 pass
.setCurrentNode(node
);
1215 yieldAndRun(new Runnable() {
1217 if (pass
.isExpired()) return;
1219 if (isRerunNeeded(pass
)) {
1220 runDone(new Runnable() {
1222 if (!pass
.isExpired()) {
1223 getUpdater().addSubtreeToUpdate(pass
);
1227 result
.setRejected();
1230 processRunnable
.run().notify(result
);
1236 processRunnable
.run().notify(result
);
1243 private void yieldAndRun(final Runnable runnable
, final TreeUpdatePass pass
) {
1244 myYeildingPasses
.add(pass
);
1245 myYeildingNow
= true;
1246 yield(new Runnable() {
1252 runOnYieldingDone(new Runnable() {
1257 executeYieldingRequest(runnable
, pass
);
1264 public boolean isYeildingNow() {
1265 return myYeildingNow
;
1268 private boolean hasSheduledUpdates() {
1269 return getUpdater().hasNodesToUpdate() || isLoadingInBackgroundNow();
1272 public boolean isReady() {
1273 return isIdle() && !hasPendingWork() && !isNodeActionsPending();
1276 public boolean hasPendingWork() {
1277 return hasNodesToUpdate() || (myUpdaterState
!= null && myUpdaterState
.isProcessingNow());
1280 public boolean isIdle() {
1281 return !isYeildingNow() && !isWorkerBusy() && (!hasSheduledUpdates() || getUpdater().isInPostponeMode());
1284 private void executeYieldingRequest(Runnable runnable
, TreeUpdatePass pass
) {
1286 myYeildingPasses
.remove(pass
);
1290 maybeYeildingFinished();
1294 private void maybeYeildingFinished() {
1295 if (myYeildingPasses
.size() == 0) {
1296 myYeildingNow
= false;
1297 flushPendingNodeActions();
1302 if (isReleased()) return;
1305 if (myTree
.isShowing() || myUpdateIfInactive
) {
1306 myInitialized
.setDone();
1309 if (myTree
.isShowing()) {
1310 if (getBuilder().isToEnsureSelectionOnFocusGained() && Registry
.is("ide.tree.ensureSelectionOnFocusGained")) {
1311 TreeUtil
.ensureSelection(myTree
);
1315 if (myInitialized
.isDone()) {
1316 for (ActionCallback each
: getReadyCallbacks(true)) {
1323 private void flushPendingNodeActions() {
1324 final DefaultMutableTreeNode
[] nodes
= myPendingNodeActions
.toArray(new DefaultMutableTreeNode
[myPendingNodeActions
.size()]);
1325 myPendingNodeActions
.clear();
1327 for (DefaultMutableTreeNode each
: nodes
) {
1328 processNodeActionsIfReady(each
);
1331 final Runnable
[] actions
= myYeildingDoneRunnables
.toArray(new Runnable
[myYeildingDoneRunnables
.size()]);
1332 for (Runnable each
: actions
) {
1333 if (!isYeildingNow()) {
1334 myYeildingDoneRunnables
.remove(each
);
1342 protected void runOnYieldingDone(Runnable onDone
) {
1343 getBuilder().runOnYeildingDone(onDone
);
1346 protected void yield(Runnable runnable
) {
1347 getBuilder().yield(runnable
);
1350 private boolean isToYieldUpdateFor(final DefaultMutableTreeNode node
) {
1351 if (!canYield()) return false;
1352 return getBuilder().isToYieldUpdateFor(node
);
1355 private MutualMap
<Object
, Integer
> loadElementsFromStructure(final NodeDescriptor descriptor
,
1356 @Nullable LoadedChildren preloadedChildren
) {
1357 MutualMap
<Object
, Integer
> elementToIndexMap
= new MutualMap
<Object
, Integer
>(true);
1358 List children
= preloadedChildren
!= null
1359 ? preloadedChildren
.getElements()
1360 : Arrays
.asList(getChildrenFor(getBuilder().getTreeStructureElement(descriptor
)));
1362 for (Object child
: children
) {
1363 if (!isValid(child
)) continue;
1364 elementToIndexMap
.put(child
, Integer
.valueOf(index
));
1367 return elementToIndexMap
;
1370 private void expand(final DefaultMutableTreeNode node
,
1371 final NodeDescriptor descriptor
,
1372 final boolean wasLeaf
,
1373 final boolean canSmartExpand
) {
1374 final Alarm alarm
= new Alarm(Alarm
.ThreadToUse
.SHARED_THREAD
);
1375 alarm
.addRequest(new Runnable() {
1377 myTree
.setCursor(Cursor
.getPredefinedCursor(Cursor
.WAIT_CURSOR
));
1379 }, WAIT_CURSOR_DELAY
);
1381 if (wasLeaf
&& isAutoExpand(descriptor
)) {
1382 expand(node
, canSmartExpand
);
1385 ArrayList
<TreeNode
> nodes
= TreeUtil
.childrenToArray(node
);
1386 for (TreeNode node1
: nodes
) {
1387 final DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)node1
;
1388 if (isLoadingNode(childNode
)) continue;
1389 NodeDescriptor childDescr
= getDescriptorFrom(childNode
);
1390 if (isAutoExpand(childDescr
)) {
1391 addNodeAction(getElementFor(childNode
), new NodeAction() {
1392 public void onReady(DefaultMutableTreeNode node
) {
1393 expand(childNode
, canSmartExpand
);
1396 addSubtreeToUpdate(childNode
);
1400 int n
= alarm
.cancelAllRequests();
1402 myTree
.setCursor(Cursor
.getDefaultCursor());
1406 public static boolean isLoadingNode(final Object node
) {
1407 return node
instanceof LoadingNode
;
1410 private AsyncResult
<ArrayList
<TreeNode
>> collectNodesToInsert(final NodeDescriptor descriptor
,
1411 final MutualMap
<Object
, Integer
> elementToIndexMap
,
1412 final DefaultMutableTreeNode parent
,
1413 final boolean addLoadingNode
,
1414 @NotNull final LoadedChildren loadedChildren
) {
1415 final AsyncResult
<ArrayList
<TreeNode
>> result
= new AsyncResult
<ArrayList
<TreeNode
>>();
1417 final ArrayList
<TreeNode
> nodesToInsert
= new ArrayList
<TreeNode
>();
1418 final Collection
<Object
> allElements
= elementToIndexMap
.getKeys();
1420 final ActionCallback processingDone
= new ActionCallback(allElements
.size());
1422 for (final Object child
: allElements
) {
1423 Integer index
= elementToIndexMap
.getValue(child
);
1424 final Ref
<NodeDescriptor
> childDescr
= new Ref
<NodeDescriptor
>(loadedChildren
.getDescriptor(child
));
1425 boolean needToUpdate
= false;
1426 if (childDescr
.get() == null) {
1427 childDescr
.set(getTreeStructure().createDescriptor(child
, descriptor
));
1428 needToUpdate
= true;
1431 //noinspection ConstantConditions
1432 if (childDescr
.get() == null) {
1433 LOG
.error("childDescr == null, treeStructure = " + getTreeStructure() + ", child = " + child
);
1434 processingDone
.setDone();
1437 childDescr
.get().setIndex(index
.intValue());
1439 final ActionCallback update
= new ActionCallback();
1441 update(childDescr
.get(), false).doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
1442 public void run(Boolean changes
) {
1443 loadedChildren
.putDescriptor(child
, childDescr
.get(), changes
);
1452 update
.doWhenDone(new Runnable() {
1454 Object element
= getElementFromDescriptor(childDescr
.get());
1455 if (element
== null) {
1456 processingDone
.setDone();
1457 LOG
.error("childDescr.getElement() == null, child = " + child
+ ", builder = " + this);
1460 DefaultMutableTreeNode node
= getNodeForElement(element
, false);
1461 if (node
== null || node
.getParent() != parent
) {
1462 final DefaultMutableTreeNode childNode
= createChildNode(childDescr
.get());
1463 if (addLoadingNode
|| getBuilder().isAlwaysShowPlus(childDescr
.get())) {
1464 insertLoadingNode(childNode
, true);
1467 addToUnbuilt(childNode
);
1469 nodesToInsert
.add(childNode
);
1470 createMapping(element
, childNode
);
1472 processingDone
.setDone();
1478 processingDone
.doWhenDone(new Runnable() {
1480 result
.setDone(nodesToInsert
);
1487 protected DefaultMutableTreeNode
createChildNode(final NodeDescriptor descriptor
) {
1488 return new ElementNode(this, descriptor
);
1491 protected boolean canYield() {
1492 return myCanYield
&& myYeildingUpdate
.asBoolean();
1495 public long getClearOnHideDelay() {
1496 return myClearOnHideDelay
> 0 ? myClearOnHideDelay
: Registry
.intValue("ide.tree.clearOnHideTime");
1499 public ActionCallback
getInitialized() {
1500 return myInitialized
;
1503 public ActionCallback
getReady(Object requestor
) {
1505 return new ActionCallback
.Done();
1508 return addReadyCallback(requestor
);
1512 private void addToUpdating(DefaultMutableTreeNode node
) {
1513 synchronized (myUpdatingChildren
) {
1514 myUpdatingChildren
.add(node
);
1518 private void removeFromUpdating(DefaultMutableTreeNode node
) {
1519 synchronized (myUpdatingChildren
) {
1520 myUpdatingChildren
.remove(node
);
1524 public boolean isUpdatingNow(DefaultMutableTreeNode node
) {
1525 synchronized (myUpdatingChildren
) {
1526 return myUpdatingChildren
.contains(node
);
1530 boolean hasUpdatingNow() {
1531 synchronized (myUpdatingChildren
) {
1532 return myUpdatingChildren
.size() > 0;
1536 public Map
getNodeActions() {
1537 return myNodeActions
;
1540 public List
<Object
> getLoadedChildrenFor(Object element
) {
1541 List
<Object
> result
= new ArrayList
<Object
>();
1543 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)findNodeByElement(element
);
1545 for (int i
= 0; i
< node
.getChildCount(); i
++) {
1546 TreeNode each
= node
.getChildAt(i
);
1547 if (isLoadingNode(each
)) continue;
1549 result
.add(getElementFor(each
));
1556 public boolean hasNodesToUpdate() {
1557 return getUpdater().hasNodesToUpdate() || hasUpdatingNow() || isLoadingInBackgroundNow();
1560 public List
<Object
> getExpandedElements() {
1561 List
<Object
> result
= new ArrayList
<Object
>();
1562 Enumeration
<TreePath
> enumeration
= myTree
.getExpandedDescendants(getPathFor(getRootNode()));
1563 while (enumeration
.hasMoreElements()) {
1564 TreePath each
= enumeration
.nextElement();
1565 Object eachElement
= getElementFor(each
.getLastPathComponent());
1566 if (eachElement
!= null) {
1567 result
.add(eachElement
);
1574 static class ElementNode
extends DefaultMutableTreeNode
{
1576 Set
<Object
> myElements
= new HashSet
<Object
>();
1577 AbstractTreeUi myUi
;
1579 ElementNode(AbstractTreeUi ui
, NodeDescriptor descriptor
) {
1585 public void insert(final MutableTreeNode newChild
, final int childIndex
) {
1586 super.insert(newChild
, childIndex
);
1587 final Object element
= myUi
.getElementFor(newChild
);
1588 if (element
!= null) {
1589 myElements
.add(element
);
1594 public void remove(final int childIndex
) {
1595 final TreeNode node
= getChildAt(childIndex
);
1596 super.remove(childIndex
);
1597 final Object element
= myUi
.getElementFor(node
);
1598 if (element
!= null) {
1599 myElements
.remove(element
);
1603 boolean isValidChild(Object childElement
) {
1604 return myElements
.contains(childElement
);
1608 public String
toString() {
1609 return String
.valueOf(getUserObject());
1613 private boolean isUpdatingParent(DefaultMutableTreeNode kid
) {
1614 DefaultMutableTreeNode eachParent
= kid
;
1615 while (eachParent
!= null) {
1616 if (isUpdatingNow(eachParent
)) return true;
1617 eachParent
= (DefaultMutableTreeNode
)eachParent
.getParent();
1623 private boolean isLoadedInBackground(Object element
) {
1624 return getLoadedInBackground(element
) != null;
1627 private UpdateInfo
getLoadedInBackground(Object element
) {
1628 synchronized (myLoadedInBackground
) {
1629 return myLoadedInBackground
.get(element
);
1633 private void addToLoadedInBackground(Object element
, UpdateInfo info
) {
1634 synchronized (myLoadedInBackground
) {
1635 myLoadedInBackground
.put(element
, info
);
1639 private void removeFromLoadedInBackground(final Object element
) {
1640 synchronized (myLoadedInBackground
) {
1641 myLoadedInBackground
.remove(element
);
1645 private boolean isLoadingInBackgroundNow() {
1646 synchronized (myLoadedInBackground
) {
1647 return myLoadedInBackground
.size() > 0;
1651 private boolean queueBackgroundUpdate(final UpdateInfo updateInfo
, final DefaultMutableTreeNode node
) {
1652 assertIsDispatchThread();
1654 final Object oldElementFromDescriptor
= getElementFromDescriptor(updateInfo
.getDescriptor());
1656 UpdateInfo loaded
= getLoadedInBackground(oldElementFromDescriptor
);
1657 if (loaded
!= null) {
1658 loaded
.apply(updateInfo
);
1662 addToLoadedInBackground(oldElementFromDescriptor
, updateInfo
);
1664 if (!isNodeBeingBuilt(node
)) {
1665 LoadingNode loadingNode
= new LoadingNode(getLoadingNodeText());
1666 myTreeModel
.insertNodeInto(loadingNode
, node
, node
.getChildCount());
1669 final Ref
<LoadedChildren
> children
= new Ref
<LoadedChildren
>();
1670 final Ref
<Object
> elementFromDescriptor
= new Ref
<Object
>();
1671 Runnable buildRunnable
= new Runnable() {
1677 if (!updateInfo
.isDescriptorIsUpToDate()) {
1678 update(updateInfo
.getDescriptor(), true);
1681 Object element
= getElementFromDescriptor(updateInfo
.getDescriptor());
1682 if (element
== null) {
1683 removeFromLoadedInBackground(oldElementFromDescriptor
);
1687 elementFromDescriptor
.set(element
);
1689 Object
[] loadedElements
= getChildrenFor(getBuilder().getTreeStructureElement(updateInfo
.getDescriptor()));
1690 LoadedChildren loaded
= new LoadedChildren(loadedElements
);
1691 for (Object each
: loadedElements
) {
1692 NodeDescriptor eachChildDescriptor
= getTreeStructure().createDescriptor(each
, updateInfo
.getDescriptor());
1693 loaded
.putDescriptor(each
, eachChildDescriptor
, update(eachChildDescriptor
, true).getResult());
1696 children
.set(loaded
);
1700 final DefaultMutableTreeNode
[] nodeToProcessActions
= new DefaultMutableTreeNode
[1];
1701 Runnable updateRunnable
= new Runnable() {
1703 if (isReleased()) return;
1704 if (children
.get() == null) return;
1706 if (isRerunNeeded(updateInfo
.getPass())) {
1707 removeFromLoadedInBackground(elementFromDescriptor
.get());
1708 getUpdater().addSubtreeToUpdate(updateInfo
.getPass());
1712 removeFromLoadedInBackground(elementFromDescriptor
.get());
1714 if (myUnbuiltNodes
.contains(node
)) {
1715 Pair
<Boolean
, LoadedChildren
> unbuilt
=
1716 processUnbuilt(node
, updateInfo
.getDescriptor(), updateInfo
.getPass(), isExpanded(node
, updateInfo
.isWasExpanded()),
1718 if (unbuilt
.getFirst()) {
1719 nodeToProcessActions
[0] = node
;
1724 updateNodeChildren(node
, updateInfo
.getPass(), children
.get(), true, updateInfo
.isCanSmartExpand(), updateInfo
.isForceUpdate(),
1728 if (isRerunNeeded(updateInfo
.getPass())) {
1729 getUpdater().addSubtreeToUpdate(updateInfo
.getPass());
1733 Object element
= elementFromDescriptor
.get();
1735 if (element
!= null) {
1736 removeLoading(node
, true);
1737 nodeToProcessActions
[0] = node
;
1741 queueToBackground(buildRunnable
, updateRunnable
, new Runnable() {
1743 if (nodeToProcessActions
[0] != null) {
1744 processNodeActionsIfReady(nodeToProcessActions
[0]);
1751 private boolean isExpanded(DefaultMutableTreeNode node
, boolean isExpanded
) {
1752 return isExpanded
|| myTree
.isExpanded(getPathFor(node
));
1755 private void removeLoading(DefaultMutableTreeNode parent
, boolean removeFromUnbuilt
) {
1756 for (int i
= 0; i
< parent
.getChildCount(); i
++) {
1757 TreeNode child
= parent
.getChildAt(i
);
1758 if (removeIfLoading(child
)) {
1763 if (removeFromUnbuilt
) {
1764 removeFromUnbuilt(parent
);
1767 if (parent
== getRootNode() && !myTree
.isRootVisible() && parent
.getChildCount() == 0) {
1768 insertLoadingNode(parent
, false);
1774 private void processNodeActionsIfReady(final DefaultMutableTreeNode node
) {
1775 if (isNodeBeingBuilt(node
)) return;
1777 final Object o
= node
.getUserObject();
1778 if (!(o
instanceof NodeDescriptor
)) return;
1781 if (isYeildingNow()) {
1782 myPendingNodeActions
.add(node
);
1786 final Object element
= getBuilder().getTreeStructureElement((NodeDescriptor
)o
);
1788 boolean childrenReady
= !isLoadedInBackground(element
);
1790 processActions(node
, element
, myNodeActions
, childrenReady ? myNodeChildrenActions
: null);
1791 if (childrenReady
) {
1792 processActions(node
, element
, myNodeChildrenActions
, null);
1795 if (!isUpdatingParent(node
) && !isWorkerBusy()) {
1796 final UpdaterTreeState state
= myUpdaterState
;
1797 if (myNodeActions
.size() == 0 && state
!= null && !state
.isProcessingNow()) {
1798 if (!state
.restore(childrenReady ? node
: null)) {
1799 setUpdaterState(state
);
1808 private void processActions(DefaultMutableTreeNode node
, Object element
, final Map
<Object
, List
<NodeAction
>> nodeActions
, @Nullable final Map
<Object
, List
<NodeAction
>> secondaryNodeAction
) {
1809 final List
<NodeAction
> actions
= nodeActions
.get(element
);
1810 if (actions
!= null) {
1811 nodeActions
.remove(element
);
1813 List
<NodeAction
> secondary
= secondaryNodeAction
!= null ? secondaryNodeAction
.get(element
) : null;
1814 for (NodeAction each
: actions
) {
1815 if (secondary
!= null && secondary
.contains(each
)) {
1816 secondary
.remove(each
);
1824 private boolean canSmartExpand(DefaultMutableTreeNode node
, boolean canSmartExpand
) {
1825 return !myNotForSmartExpand
.contains(node
) && canSmartExpand
;
1828 private void processSmartExpand(final DefaultMutableTreeNode node
, final boolean canSmartExpand
) {
1829 if (!getBuilder().isSmartExpand() || !canSmartExpand(node
, canSmartExpand
)) return;
1831 if (isNodeBeingBuilt(node
)) {
1832 addNodeAction(getElementFor(node
), new NodeAction() {
1833 public void onReady(DefaultMutableTreeNode node
) {
1834 processSmartExpand(node
, canSmartExpand
);
1839 TreeNode child
= getChildForSmartExpand(node
);
1840 if (child
!= null) {
1841 final TreePath childPath
= new TreePath(node
.getPath()).pathByAddingChild(child
);
1842 myTree
.expandPath(childPath
);
1848 private TreeNode
getChildForSmartExpand(DefaultMutableTreeNode node
) {
1849 int realChildCount
= 0;
1850 TreeNode nodeToExpand
= null;
1852 for (int i
= 0; i
< node
.getChildCount(); i
++) {
1853 TreeNode eachChild
= node
.getChildAt(i
);
1855 if (!isLoadingNode(eachChild
)) {
1857 if (nodeToExpand
== null) {
1858 nodeToExpand
= eachChild
;
1862 if (realChildCount
> 1) {
1863 nodeToExpand
= null;
1868 return nodeToExpand
;
1871 public boolean isLoadingChildrenFor(final Object nodeObject
) {
1872 if (!(nodeObject
instanceof DefaultMutableTreeNode
)) return false;
1874 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)nodeObject
;
1876 int loadingNodes
= 0;
1877 for (int i
= 0; i
< Math
.min(node
.getChildCount(), 2); i
++) {
1878 TreeNode child
= node
.getChildAt(i
);
1879 if (isLoadingNode(child
)) {
1883 return loadingNodes
> 0 && loadingNodes
== node
.getChildCount();
1886 private boolean isParentLoading(Object nodeObject
) {
1887 if (!(nodeObject
instanceof DefaultMutableTreeNode
)) return false;
1889 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)nodeObject
;
1891 TreeNode eachParent
= node
.getParent();
1893 while (eachParent
!= null) {
1894 eachParent
= eachParent
.getParent();
1895 if (eachParent
instanceof DefaultMutableTreeNode
) {
1896 final Object eachElement
= getElementFor((DefaultMutableTreeNode
)eachParent
);
1897 if (isLoadedInBackground(eachElement
)) return true;
1904 protected String
getLoadingNodeText() {
1905 return IdeBundle
.message("progress.searching");
1908 private ActionCallback
processExistingNode(final DefaultMutableTreeNode childNode
,
1909 final NodeDescriptor childDescriptor
,
1910 final DefaultMutableTreeNode parentNode
,
1911 final MutualMap
<Object
, Integer
> elementToIndexMap
,
1912 final TreeUpdatePass pass
,
1913 final boolean canSmartExpand
,
1914 final boolean forceUpdate
,
1915 LoadedChildren parentPreloadedChildren
) {
1917 final ActionCallback result
= new ActionCallback();
1919 if (pass
.isExpired()) {
1920 return new ActionCallback
.Rejected();
1923 final Ref
<NodeDescriptor
> childDesc
= new Ref
<NodeDescriptor
>(childDescriptor
);
1925 if (childDesc
.get() == null) {
1927 return new ActionCallback
.Rejected();
1929 final Object oldElement
= getElementFromDescriptor(childDesc
.get());
1930 if (oldElement
== null) {
1932 return new ActionCallback
.Rejected();
1935 AsyncResult
<Boolean
> update
= new AsyncResult
<Boolean
>();
1936 if (parentPreloadedChildren
!= null && parentPreloadedChildren
.getDescriptor(oldElement
) != null) {
1937 update
.setDone(parentPreloadedChildren
.isUpdated(oldElement
));
1940 update
= update(childDesc
.get(), false);
1943 update
.doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
1944 public void run(Boolean isChanged
) {
1945 final Ref
<Boolean
> changes
= new Ref
<Boolean
>(isChanged
);
1947 final Ref
<Boolean
> forceRemapping
= new Ref
<Boolean
>(false);
1948 final Ref
<Object
> newElement
= new Ref
<Object
>(getElementFromDescriptor(childDesc
.get()));
1950 final Integer index
= newElement
.get() != null ? elementToIndexMap
.getValue(getBuilder().getTreeStructureElement(childDesc
.get())) : null;
1951 final AsyncResult
<Boolean
> updateIndexDone
= new AsyncResult
<Boolean
>();
1952 final ActionCallback indexReady
= new ActionCallback();
1953 if (index
!= null) {
1954 final Object elementFromMap
= elementToIndexMap
.getKey(index
);
1955 if (elementFromMap
!= newElement
.get() && elementFromMap
.equals(newElement
.get())) {
1956 if (isInStructure(elementFromMap
) && isInStructure(newElement
.get())) {
1957 if (parentNode
.getUserObject() instanceof NodeDescriptor
) {
1958 final NodeDescriptor parentDescriptor
= getDescriptorFrom(parentNode
);
1959 childDesc
.set(getTreeStructure().createDescriptor(elementFromMap
, parentDescriptor
));
1960 childNode
.setUserObject(childDesc
.get());
1961 newElement
.set(elementFromMap
);
1962 forceRemapping
.set(true);
1963 update(childDesc
.get(), false).doWhenDone(new AsyncResult
.Handler
<Boolean
>() {
1964 public void run(Boolean isChanged
) {
1965 changes
.set(isChanged
);
1966 updateIndexDone
.setDone(isChanged
);
1972 updateIndexDone
.setDone(changes
.get());
1975 updateIndexDone
.setDone(changes
.get());
1978 updateIndexDone
.doWhenDone(new Runnable() {
1980 if (childDesc
.get().getIndex() != index
.intValue()) {
1983 childDesc
.get().setIndex(index
.intValue());
1984 indexReady
.setDone();
1989 updateIndexDone
.setDone();
1992 updateIndexDone
.doWhenDone(new Runnable() {
1994 if (index
!= null && changes
.get()) {
1995 updateNodeImageAndPosition(childNode
, false);
1997 if (!oldElement
.equals(newElement
.get()) | forceRemapping
.get()) {
1998 removeMapping(oldElement
, childNode
, newElement
.get());
1999 if (newElement
.get() != null) {
2000 createMapping(newElement
.get(), childNode
);
2004 if (index
== null) {
2005 int selectedIndex
= -1;
2006 if (TreeBuilderUtil
.isNodeOrChildSelected(myTree
, childNode
)) {
2007 selectedIndex
= parentNode
.getIndex(childNode
);
2010 if (childNode
.getParent() instanceof DefaultMutableTreeNode
) {
2011 final DefaultMutableTreeNode parent
= (DefaultMutableTreeNode
)childNode
.getParent();
2012 if (myTree
.isExpanded(new TreePath(parent
.getPath()))) {
2013 if (parent
.getChildCount() == 1 && parent
.getChildAt(0) == childNode
) {
2014 insertLoadingNode(parent
, false);
2019 Object disposedElement
= getElementFor(childNode
);
2021 removeNodeFromParent(childNode
, selectedIndex
>= 0);
2022 disposeNode(childNode
);
2024 adjustSelectionOnChildRemove(parentNode
, selectedIndex
, disposedElement
);
2027 elementToIndexMap
.remove(getBuilder().getTreeStructureElement(childDesc
.get()));
2028 updateNodeChildren(childNode
, pass
, null, false, canSmartExpand
, forceUpdate
, true);
2031 if (parentNode
.equals(getRootNode())) {
2032 myTreeModel
.nodeChanged(getRootNode());
2045 private void adjustSelectionOnChildRemove(DefaultMutableTreeNode parentNode
, int selectedIndex
, Object disposedElement
) {
2046 DefaultMutableTreeNode node
= getNodeForElement(disposedElement
, false);
2047 if (node
!= null && isValidForSelectionAdjusting(node
)) {
2048 Object newElement
= getElementFor(node
);
2049 addSelectionPath(getPathFor(node
), true, getExpiredElementCondition(newElement
));
2054 if (selectedIndex
>= 0) {
2055 if (parentNode
.getChildCount() > 0) {
2056 if (parentNode
.getChildCount() > selectedIndex
) {
2057 TreeNode newChildNode
= parentNode
.getChildAt(selectedIndex
);
2058 if (isValidForSelectionAdjusting(newChildNode
)) {
2059 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(newChildNode
)), true, getExpiredElementCondition(disposedElement
));
2063 TreeNode newChild
= parentNode
.getChildAt(parentNode
.getChildCount() - 1);
2064 if (isValidForSelectionAdjusting(newChild
)) {
2065 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(newChild
)), true, getExpiredElementCondition(disposedElement
));
2070 addSelectionPath(new TreePath(myTreeModel
.getPathToRoot(parentNode
)), true, getExpiredElementCondition(disposedElement
));
2075 private boolean isValidForSelectionAdjusting(TreeNode node
) {
2076 if (!myTree
.isRootVisible() && getRootNode() == node
) return false;
2078 if (isLoadingNode(node
)) return true;
2080 final Object elementInTree
= getElementFor(node
);
2081 if (elementInTree
== null) return false;
2083 final TreeNode parentNode
= node
.getParent();
2084 final Object parentElementInTree
= getElementFor(parentNode
);
2085 if (parentElementInTree
== null) return false;
2087 final Object parentElement
= getTreeStructure().getParentElement(elementInTree
);
2089 return parentElementInTree
.equals(parentElement
);
2092 public Condition
getExpiredElementCondition(final Object element
) {
2093 return new Condition() {
2094 public boolean value(final Object o
) {
2095 return isInStructure(element
);
2100 private void addSelectionPath(final TreePath path
, final boolean isAdjustedSelection
, final Condition isExpiredAdjustement
) {
2101 doWithUpdaterState(new Runnable() {
2103 TreePath toSelect
= null;
2105 if (isLoadingNode(path
.getLastPathComponent())) {
2106 final TreePath parentPath
= path
.getParentPath();
2107 if (parentPath
!= null) {
2108 if (isValidForSelectionAdjusting((TreeNode
)parentPath
.getLastPathComponent())) {
2109 toSelect
= parentPath
;
2120 if (toSelect
!= null) {
2121 myTree
.addSelectionPath(toSelect
);
2123 if (isAdjustedSelection
&& myUpdaterState
!= null) {
2124 final Object toSelectElement
= getElementFor(toSelect
.getLastPathComponent());
2125 myUpdaterState
.addAdjustedSelection(toSelectElement
, isExpiredAdjustement
);
2132 private static TreePath
getPathFor(TreeNode node
) {
2133 if (node
instanceof DefaultMutableTreeNode
) {
2134 return new TreePath(((DefaultMutableTreeNode
)node
).getPath());
2137 ArrayList nodes
= new ArrayList();
2138 TreeNode eachParent
= node
;
2139 while (eachParent
!= null) {
2140 nodes
.add(eachParent
);
2141 eachParent
= eachParent
.getParent();
2144 return new TreePath(ArrayUtil
.toObjectArray(nodes
));
2149 private void removeNodeFromParent(final MutableTreeNode node
, final boolean willAdjustSelection
) {
2150 doWithUpdaterState(new Runnable() {
2152 if (willAdjustSelection
) {
2153 final TreePath path
= getPathFor(node
);
2154 if (myTree
.isPathSelected(path
)) {
2155 myTree
.removeSelectionPath(path
);
2159 myTreeModel
.removeNodeFromParent(node
);
2164 private void expandPath(final TreePath path
, final boolean canSmartExpand
) {
2165 doWithUpdaterState(new Runnable() {
2167 if (path
.getLastPathComponent() instanceof DefaultMutableTreeNode
) {
2168 DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
2169 if (node
.getChildCount() > 0 && !myTree
.isExpanded(path
)) {
2170 if (!canSmartExpand
) {
2171 myNotForSmartExpand
.add(node
);
2174 myRequestedExpand
= path
;
2175 myTree
.expandPath(path
);
2176 processSmartExpand(node
, canSmartExpand
);
2179 myNotForSmartExpand
.remove(node
);
2180 myRequestedExpand
= null;
2184 processNodeActionsIfReady(node
);
2191 private void doWithUpdaterState(Runnable runnable
) {
2192 if (myUpdaterState
!= null) {
2193 myUpdaterState
.process(runnable
);
2200 protected boolean doUpdateNodeDescriptor(final NodeDescriptor descriptor
) {
2201 return descriptor
.update();
2204 private void makeLoadingOrLeafIfNoChildren(final DefaultMutableTreeNode node
) {
2205 TreePath path
= getPathFor(node
);
2206 if (path
== null) return;
2208 insertLoadingNode(node
, true);
2210 final NodeDescriptor descriptor
= getDescriptorFrom(node
);
2211 if (descriptor
== null) return;
2213 descriptor
.setChildrenSortingStamp(-1);
2215 if (getBuilder().isAlwaysShowPlus(descriptor
)) return;
2218 TreePath parentPath
= path
.getParentPath();
2219 if (myTree
.isVisible(path
) || (parentPath
!= null && myTree
.isExpanded(parentPath
))) {
2220 if (myTree
.isExpanded(path
)) {
2221 addSubtreeToUpdate(node
);
2224 insertLoadingNode(node
, false);
2230 private boolean isValid(DefaultMutableTreeNode node
) {
2231 if (node
== null) return false;
2232 final Object object
= node
.getUserObject();
2233 if (object
instanceof NodeDescriptor
) {
2234 return isValid((NodeDescriptor
)object
);
2240 private boolean isValid(NodeDescriptor descriptor
) {
2241 if (descriptor
== null) return false;
2242 return isValid(getElementFromDescriptor(descriptor
));
2245 private boolean isValid(Object element
) {
2246 if (element
instanceof ValidateableNode
) {
2247 if (!((ValidateableNode
)element
).isValid()) return false;
2249 return getBuilder().validateNode(element
);
2252 private void insertLoadingNode(final DefaultMutableTreeNode node
, boolean addToUnbuilt
) {
2253 if (!isLoadingChildrenFor(node
)) {
2254 myTreeModel
.insertNodeInto(new LoadingNode(), node
, 0);
2263 protected void queueToBackground(@NotNull final Runnable bgBuildAction
,
2264 @Nullable final Runnable edtPostRunnable
,
2265 @Nullable final Runnable finalizeEdtRunnable
) {
2266 registerWorkerTask(bgBuildAction
);
2268 final Runnable pooledThreadWithProgressRunnable
= new Runnable() {
2274 final AbstractTreeBuilder builder
= getBuilder();
2276 builder
.runBackgroundLoading(new Runnable() {
2278 assertNotDispatchThread();
2285 bgBuildAction
.run();
2287 if (edtPostRunnable
!= null && !isReleased()) {
2288 builder
.updateAfterLoadedInBackground(new Runnable() {
2291 assertIsDispatchThread();
2297 edtPostRunnable
.run();
2300 unregisterWorkerTask(bgBuildAction
, finalizeEdtRunnable
);
2306 unregisterWorkerTask(bgBuildAction
, finalizeEdtRunnable
);
2309 catch (ProcessCanceledException e
) {
2310 unregisterWorkerTask(bgBuildAction
, finalizeEdtRunnable
);
2312 catch (Throwable t
) {
2313 unregisterWorkerTask(bgBuildAction
, finalizeEdtRunnable
);
2314 throw new RuntimeException(t
);
2321 Runnable pooledThreadRunnable
= new Runnable() {
2323 if (isReleased()) return;
2326 if (myProgress
!= null) {
2327 ProgressManager
.getInstance().runProcess(pooledThreadWithProgressRunnable
, myProgress
);
2330 pooledThreadWithProgressRunnable
.run();
2333 catch (ProcessCanceledException e
) {
2339 if (isPassthroughMode()) {
2342 if (myWorker
== null || myWorker
.isDisposed()) {
2343 myWorker
= new WorkerThread("AbstractTreeBuilder.Worker", 1);
2345 myWorker
.addTaskFirst(pooledThreadRunnable
);
2346 myWorker
.dispose(false);
2349 myWorker
.addTaskFirst(pooledThreadRunnable
);
2354 private void registerWorkerTask(Runnable runnable
) {
2355 synchronized (myActiveWorkerTasks
) {
2356 myActiveWorkerTasks
.add(runnable
);
2360 private void unregisterWorkerTask(Runnable runnable
, @Nullable Runnable finalizeRunnable
) {
2362 synchronized (myActiveWorkerTasks
) {
2363 wasRemoved
= myActiveWorkerTasks
.remove(runnable
);
2366 if (wasRemoved
&& finalizeRunnable
!= null) {
2367 finalizeRunnable
.run();
2373 public boolean isWorkerBusy() {
2374 synchronized (myActiveWorkerTasks
) {
2375 return myActiveWorkerTasks
.size() > 0;
2379 private void clearWorkerTasks() {
2380 synchronized (myActiveWorkerTasks
) {
2381 myActiveWorkerTasks
.clear();
2385 private void updateNodeImageAndPosition(final DefaultMutableTreeNode node
, boolean updatePosition
) {
2386 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
2387 NodeDescriptor descriptor
= getDescriptorFrom(node
);
2388 if (getElementFromDescriptor(descriptor
) == null) return;
2390 boolean notified
= false;
2391 if (updatePosition
) {
2392 DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)node
.getParent();
2393 if (parentNode
!= null) {
2394 int oldIndex
= parentNode
.getIndex(node
);
2395 int newIndex
= oldIndex
;
2396 if (isLoadingChildrenFor(node
.getParent()) || getBuilder().isChildrenResortingNeeded(descriptor
)) {
2397 final ArrayList
<TreeNode
> children
= new ArrayList
<TreeNode
>(parentNode
.getChildCount());
2398 for (int i
= 0; i
< parentNode
.getChildCount(); i
++) {
2399 children
.add(parentNode
.getChildAt(i
));
2401 sortChildren(node
, children
, true, false);
2402 newIndex
= children
.indexOf(node
);
2405 if (oldIndex
!= newIndex
) {
2406 List
<Object
> pathsToExpand
= new ArrayList
<Object
>();
2407 List
<Object
> selectionPaths
= new ArrayList
<Object
>();
2408 TreeBuilderUtil
.storePaths(getBuilder(), node
, pathsToExpand
, selectionPaths
, false);
2409 removeNodeFromParent(node
, false);
2410 myTreeModel
.insertNodeInto(node
, parentNode
, newIndex
);
2411 TreeBuilderUtil
.restorePaths(getBuilder(), pathsToExpand
, selectionPaths
, false);
2415 myTreeModel
.nodeChanged(node
);
2420 myTreeModel
.nodeChanged(node
);
2426 myTreeModel
.nodeChanged(node
);
2431 public DefaultTreeModel
getTreeModel() {
2435 private void insertNodesInto(final ArrayList
<TreeNode
> toInsert
, final DefaultMutableTreeNode parentNode
) {
2436 sortChildren(parentNode
, toInsert
, false, true);
2437 final ArrayList
<TreeNode
> all
= new ArrayList
<TreeNode
>(toInsert
.size() + parentNode
.getChildCount());
2438 all
.addAll(toInsert
);
2439 all
.addAll(TreeUtil
.childrenToArray(parentNode
));
2441 if (toInsert
.size() > 0) {
2442 sortChildren(parentNode
, all
, true, true);
2444 int[] newNodeIndices
= new int[toInsert
.size()];
2445 int eachNewNodeIndex
= 0;
2446 TreeMap
<Integer
, TreeNode
> insertSet
= new TreeMap
<Integer
, TreeNode
>();
2447 for (int i
= 0; i
< toInsert
.size(); i
++) {
2448 TreeNode eachNewNode
= toInsert
.get(i
);
2449 while (all
.get(eachNewNodeIndex
) != eachNewNode
) {
2452 newNodeIndices
[i
] = eachNewNodeIndex
;
2453 insertSet
.put(eachNewNodeIndex
, eachNewNode
);
2456 Iterator
<Integer
> indices
= insertSet
.keySet().iterator();
2457 while (indices
.hasNext()) {
2458 Integer eachIndex
= indices
.next();
2459 TreeNode eachNode
= insertSet
.get(eachIndex
);
2460 parentNode
.insert((MutableTreeNode
)eachNode
, eachIndex
);
2463 myTreeModel
.nodesWereInserted(parentNode
, newNodeIndices
);
2466 ArrayList
<TreeNode
> before
= new ArrayList
<TreeNode
>();
2469 sortChildren(parentNode
, all
, true, false);
2470 if (!before
.equals(all
)) {
2471 doWithUpdaterState(new Runnable() {
2473 parentNode
.removeAllChildren();
2474 for (TreeNode each
: all
) {
2475 parentNode
.add((MutableTreeNode
)each
);
2477 myTreeModel
.nodeStructureChanged(parentNode
);
2484 private void sortChildren(DefaultMutableTreeNode node
, ArrayList
<TreeNode
> children
, boolean updateStamp
, boolean forceSort
) {
2485 NodeDescriptor descriptor
= getDescriptorFrom(node
);
2486 assert descriptor
!= null;
2488 if (descriptor
.getChildrenSortingStamp() >= getComparatorStamp() && !forceSort
) return;
2489 if (children
.size() > 0) {
2490 getBuilder().sortChildren(myNodeComparator
, node
, children
);
2494 descriptor
.setChildrenSortingStamp(getComparatorStamp());
2498 private void disposeNode(DefaultMutableTreeNode node
) {
2499 removeFromUpdating(node
);
2500 removeFromUnbuilt(node
);
2502 if (node
.getChildCount() > 0) {
2503 for (DefaultMutableTreeNode _node
= (DefaultMutableTreeNode
)node
.getFirstChild(); _node
!= null; _node
= _node
.getNextSibling()) {
2507 if (isLoadingNode(node
)) return;
2508 NodeDescriptor descriptor
= getDescriptorFrom(node
);
2509 if (descriptor
== null) return;
2510 final Object element
= getElementFromDescriptor(descriptor
);
2511 removeMapping(element
, node
, null);
2512 node
.setUserObject(null);
2513 node
.removeAllChildren();
2516 public boolean addSubtreeToUpdate(final DefaultMutableTreeNode root
) {
2517 return addSubtreeToUpdate(root
, null);
2520 public boolean addSubtreeToUpdate(final DefaultMutableTreeNode root
, Runnable runAfterUpdate
) {
2521 Object element
= getElementFor(root
);
2522 if (getTreeStructure().isAlwaysLeaf(element
)) {
2523 removeLoading(root
, true);
2525 if (runAfterUpdate
!= null) {
2526 getReady(this).doWhenDone(runAfterUpdate
);
2531 getUpdater().runAfterUpdate(runAfterUpdate
);
2532 getUpdater().addSubtreeToUpdate(root
);
2537 public boolean wasRootNodeInitialized() {
2538 return myRootNodeWasInitialized
;
2541 private boolean isRootNodeBuilt() {
2542 return myRootNodeWasInitialized
&& isNodeBeingBuilt(myRootNode
);
2545 public void select(final Object
[] elements
, @Nullable final Runnable onDone
) {
2546 select(elements
, onDone
, false);
2549 public void select(final Object
[] elements
, @Nullable final Runnable onDone
, boolean addToSelection
) {
2550 select(elements
, onDone
, addToSelection
, false);
2553 public void select(final Object
[] elements
, @Nullable final Runnable onDone
, boolean addToSelection
, boolean deferred
) {
2554 _select(elements
, onDone
, addToSelection
, true, false, true, deferred
, false, false);
2557 void _select(final Object
[] elements
,
2558 final Runnable onDone
,
2559 final boolean addToSelection
,
2560 final boolean checkCurrentSelection
,
2561 final boolean checkIfInStructure
) {
2563 _select(elements
, onDone
, addToSelection
, checkCurrentSelection
, checkIfInStructure
, true, false, false, false);
2566 void _select(final Object
[] elements
,
2567 final Runnable onDone
,
2568 final boolean addToSelection
,
2569 final boolean checkCurrentSelection
,
2570 final boolean checkIfInStructure
,
2571 final boolean scrollToVisible
) {
2573 _select(elements
, onDone
, addToSelection
, checkCurrentSelection
, checkIfInStructure
, scrollToVisible
, false, false, false);
2576 public void userSelect(final Object
[] elements
,
2577 final Runnable onDone
,
2578 final boolean addToSelection
,
2580 _select(elements
, onDone
, addToSelection
, true, false, scroll
, false, true, true);
2583 void _select(final Object
[] elements
,
2584 final Runnable onDone
,
2585 final boolean addToSelection
,
2586 final boolean checkCurrentSelection
,
2587 final boolean checkIfInStructure
,
2588 final boolean scrollToVisible
,
2589 final boolean deferred
,
2590 final boolean canSmartExpand
,
2591 final boolean mayQueue
) {
2593 AbstractTreeUpdater updater
= getUpdater();
2594 if (mayQueue
&& updater
!= null) {
2595 updater
.queueSelection(new SelectionRequest(elements
, onDone
, addToSelection
, checkCurrentSelection
, checkIfInStructure
, scrollToVisible
, deferred
, canSmartExpand
));
2599 boolean willAffectSelection
= elements
.length
> 0 || (elements
.length
== 0 && addToSelection
);
2600 if (!willAffectSelection
) {
2605 final boolean oldCanProcessDeferredSelection
= myCanProcessDeferredSelections
;
2607 if (!deferred
&& wasRootNodeInitialized() && willAffectSelection
) {
2608 myCanProcessDeferredSelections
= false;
2611 if (!checkDeferred(deferred
, onDone
)) return;
2613 if (!deferred
&& oldCanProcessDeferredSelection
&& !myCanProcessDeferredSelections
) {
2614 getTree().clearSelection();
2618 runDone(new Runnable() {
2620 if (!checkDeferred(deferred
, onDone
)) return;
2622 final Set
<Object
> currentElements
= getSelectedElements();
2624 if (checkCurrentSelection
&& currentElements
.size() > 0 && elements
.length
== currentElements
.size()) {
2625 boolean runSelection
= false;
2626 for (Object eachToSelect
: elements
) {
2627 if (!currentElements
.contains(eachToSelect
)) {
2628 runSelection
= true;
2633 if (!runSelection
) {
2634 if (elements
.length
> 0) {
2635 selectVisible(elements
[0], onDone
, true, true, scrollToVisible
);
2641 Set
<Object
> toSelect
= new HashSet
<Object
>();
2642 myTree
.clearSelection();
2643 toSelect
.addAll(Arrays
.asList(elements
));
2644 if (addToSelection
) {
2645 toSelect
.addAll(currentElements
);
2648 if (checkIfInStructure
) {
2649 final Iterator
<Object
> allToSelect
= toSelect
.iterator();
2650 while (allToSelect
.hasNext()) {
2651 Object each
= allToSelect
.next();
2652 if (!isInStructure(each
)) {
2653 allToSelect
.remove();
2658 final Object
[] elementsToSelect
= ArrayUtil
.toObjectArray(toSelect
);
2660 if (wasRootNodeInitialized()) {
2661 final int[] originalRows
= myTree
.getSelectionRows();
2662 if (!addToSelection
) {
2663 myTree
.clearSelection();
2665 addNext(elementsToSelect
, 0, new Runnable() {
2667 if (getTree().isSelectionEmpty()) {
2668 restoreSelection(currentElements
);
2672 }, originalRows
, deferred
, scrollToVisible
, canSmartExpand
);
2675 addToDeferred(elementsToSelect
, onDone
);
2681 private void restoreSelection(Set
<Object
> selection
) {
2682 for (Object each
: selection
) {
2683 DefaultMutableTreeNode node
= getNodeForElement(each
, false);
2684 if (node
!= null && isValidForSelectionAdjusting(node
)) {
2685 addSelectionPath(getPathFor(node
), false, null);
2691 private void addToDeferred(final Object
[] elementsToSelect
, final Runnable onDone
) {
2692 myDeferredSelections
.clear();
2693 myDeferredSelections
.add(new Runnable() {
2695 select(elementsToSelect
, onDone
, false, true);
2700 private boolean checkDeferred(boolean isDeferred
, @Nullable Runnable onDone
) {
2701 if (!isDeferred
|| myCanProcessDeferredSelections
|| !wasRootNodeInitialized()) {
2711 final Set
<Object
> getSelectedElements() {
2712 final TreePath
[] paths
= myTree
.getSelectionPaths();
2714 Set
<Object
> result
= new HashSet
<Object
>();
2715 if (paths
!= null) {
2716 for (TreePath eachPath
: paths
) {
2717 if (eachPath
.getLastPathComponent() instanceof DefaultMutableTreeNode
) {
2718 final DefaultMutableTreeNode eachNode
= (DefaultMutableTreeNode
)eachPath
.getLastPathComponent();
2719 final Object eachElement
= getElementFor(eachNode
);
2720 if (eachElement
!= null) {
2721 result
.add(eachElement
);
2730 private void addNext(final Object
[] elements
,
2732 @Nullable final Runnable onDone
,
2733 final int[] originalRows
,
2734 final boolean deferred
,
2735 final boolean scrollToVisible
,
2736 final boolean canSmartExpand
) {
2737 if (i
>= elements
.length
) {
2738 if (myTree
.isSelectionEmpty()) {
2739 myTree
.setSelectionRows(originalRows
);
2744 if (!checkDeferred(deferred
, onDone
)) {
2748 doSelect(elements
[i
], new Runnable() {
2750 if (!checkDeferred(deferred
, onDone
)) return;
2752 addNext(elements
, i
+ 1, onDone
, originalRows
, deferred
, scrollToVisible
, canSmartExpand
);
2754 }, true, deferred
, i
== 0, scrollToVisible
, canSmartExpand
);
2758 public void select(final Object element
, @Nullable final Runnable onDone
) {
2759 select(element
, onDone
, false);
2762 public void select(final Object element
, @Nullable final Runnable onDone
, boolean addToSelection
) {
2763 _select(new Object
[]{element
}, onDone
, addToSelection
, true, false);
2766 private void doSelect(final Object element
,
2767 final Runnable onDone
,
2768 final boolean addToSelection
,
2769 final boolean deferred
,
2770 final boolean canBeCentered
,
2771 final boolean scrollToVisible
,
2772 boolean canSmartExpand
) {
2773 final Runnable _onDone
= new Runnable() {
2775 if (!checkDeferred(deferred
, onDone
)) return;
2776 selectVisible(element
, onDone
, addToSelection
, canBeCentered
, scrollToVisible
);
2779 _expand(element
, _onDone
, true, false, canSmartExpand
);
2782 public void scrollSelectionToVisible(@Nullable Runnable onDone
, boolean shouldBeCentered
) {
2783 int[] rows
= myTree
.getSelectionRows();
2784 if (rows
== null || rows
.length
== 0) {
2790 Object toSelect
= null;
2791 for (int eachRow
: rows
) {
2792 TreePath path
= myTree
.getPathForRow(eachRow
);
2793 toSelect
= getElementFor(path
.getLastPathComponent());
2794 if (toSelect
!= null) break;
2797 if (toSelect
!= null) {
2798 selectVisible(toSelect
, onDone
, true, shouldBeCentered
, true);
2802 private void selectVisible(Object element
, final Runnable onDone
, boolean addToSelection
, boolean canBeCentered
, final boolean scroll
) {
2803 final DefaultMutableTreeNode toSelect
= getNodeForElement(element
, false);
2805 if (toSelect
== null) {
2810 if (getRootNode() == toSelect
&& !myTree
.isRootVisible()) {
2815 final int row
= myTree
.getRowForPath(new TreePath(toSelect
.getPath()));
2817 if (myUpdaterState
!= null) {
2818 myUpdaterState
.addSelection(element
);
2821 if (Registry
.is("ide.tree.autoscrollToVCenter") && canBeCentered
) {
2822 runDone(new Runnable() {
2824 TreeUtil
.showRowCentered(myTree
, row
, false, scroll
).doWhenDone(new Runnable() {
2833 TreeUtil
.showAndSelect(myTree
, row
- 2, row
+ 2, row
, -1, addToSelection
, scroll
).doWhenDone(new Runnable() {
2841 public void expand(final Object element
, @Nullable final Runnable onDone
) {
2842 expand(new Object
[]{element
}, onDone
);
2845 public void expand(final Object
[] element
, @Nullable final Runnable onDone
) {
2846 expand(element
, onDone
, false);
2850 void expand(final Object element
, @Nullable final Runnable onDone
, boolean checkIfInStructure
) {
2851 _expand(new Object
[]{element
}, onDone
== null ?
new EmptyRunnable() : onDone
, false, checkIfInStructure
, false);
2854 void expand(final Object
[] element
, @Nullable final Runnable onDone
, boolean checkIfInStructure
) {
2855 _expand(element
, onDone
== null ?
new EmptyRunnable() : onDone
, false, checkIfInStructure
, false);
2858 void _expand(final Object
[] element
,
2859 @NotNull final Runnable onDone
,
2860 final boolean parentsOnly
,
2861 final boolean checkIfInStructure
,
2862 final boolean canSmartExpand
) {
2864 runDone(new Runnable() {
2866 if (element
.length
== 0) {
2871 if (myUpdaterState
!= null) {
2872 myUpdaterState
.clearExpansion();
2876 final ActionCallback done
= new ActionCallback(element
.length
);
2877 done
.doWhenDone(new Runnable() {
2883 expandNext(element
, 0, parentsOnly
, checkIfInStructure
, canSmartExpand
, done
);
2888 private void expandNext(final Object
[] elements
, final int index
, final boolean parentsOnly
, final boolean checkIfInStricture
, final boolean canSmartExpand
, final ActionCallback done
) {
2889 if (elements
.length
<= 0) {
2894 if (index
>= elements
.length
) {
2898 _expand(elements
[index
], new Runnable() {
2901 expandNext(elements
, index
+ 1, parentsOnly
, checkIfInStricture
, canSmartExpand
, done
);
2903 }, parentsOnly
, checkIfInStricture
, canSmartExpand
);
2906 public void collapseChildren(final Object element
, @Nullable final Runnable onDone
) {
2907 runDone(new Runnable() {
2909 final DefaultMutableTreeNode node
= getNodeForElement(element
, false);
2911 getTree().collapsePath(new TreePath(node
.getPath()));
2918 private void runDone(@Nullable Runnable done
) {
2919 if (isReleased()) return;
2920 if (done
== null) return;
2922 if (isYeildingNow()) {
2923 if (!myYeildingDoneRunnables
.contains(done
)) {
2924 myYeildingDoneRunnables
.add(done
);
2932 private void _expand(final Object element
,
2933 @NotNull final Runnable onDone
,
2934 final boolean parentsOnly
,
2935 boolean checkIfInStructure
,
2936 boolean canSmartExpand
) {
2938 if (checkIfInStructure
&& !isInStructure(element
)) {
2943 if (wasRootNodeInitialized()) {
2944 List
<Object
> kidsToExpand
= new ArrayList
<Object
>();
2945 Object eachElement
= element
;
2946 DefaultMutableTreeNode firstVisible
= null;
2948 if (!isValid(eachElement
)) break;
2950 firstVisible
= getNodeForElement(eachElement
, true);
2951 if (eachElement
!= element
|| !parentsOnly
) {
2952 assert !kidsToExpand
.contains(eachElement
) :
2953 "Not a valid tree structure, walking up the structure gives many entries for element=" +
2956 getTreeStructure().getRootElement();
2957 kidsToExpand
.add(eachElement
);
2959 if (firstVisible
!= null) break;
2960 eachElement
= getTreeStructure().getParentElement(eachElement
);
2961 if (eachElement
== null) {
2962 firstVisible
= null;
2968 if (firstVisible
== null) {
2971 else if (kidsToExpand
.size() == 0) {
2972 final DefaultMutableTreeNode parentNode
= (DefaultMutableTreeNode
)firstVisible
.getParent();
2973 if (parentNode
!= null) {
2974 final TreePath parentPath
= new TreePath(parentNode
.getPath());
2975 if (!myTree
.isExpanded(parentPath
)) {
2976 expand(parentPath
, canSmartExpand
);
2982 processExpand(firstVisible
, kidsToExpand
, kidsToExpand
.size() - 1, onDone
, canSmartExpand
);
2986 deferExpansion(element
, onDone
, parentsOnly
, canSmartExpand
);
2990 private void deferExpansion(final Object element
, final Runnable onDone
, final boolean parentsOnly
, final boolean canSmartExpand
) {
2991 myDeferredExpansions
.add(new Runnable() {
2993 _expand(element
, onDone
, parentsOnly
, false, canSmartExpand
);
2998 private void processExpand(final DefaultMutableTreeNode toExpand
,
2999 final List kidsToExpand
,
3000 final int expandIndex
,
3001 @NotNull final Runnable onDone
,
3002 final boolean canSmartExpand
) {
3004 final Object element
= getElementFor(toExpand
);
3005 if (element
== null) {
3010 addNodeAction(element
, new NodeAction() {
3011 public void onReady(final DefaultMutableTreeNode node
) {
3013 if (node
.getChildCount() > 0 && !myTree
.isExpanded(new TreePath(node
.getPath()))) {
3014 if (!isAutoExpand(node
)) {
3015 expand(node
, canSmartExpand
);
3019 if (expandIndex
<= 0) {
3024 final DefaultMutableTreeNode nextNode
= getNodeForElement(kidsToExpand
.get(expandIndex
- 1), false);
3025 if (nextNode
!= null) {
3026 processExpand(nextNode
, kidsToExpand
, expandIndex
- 1, onDone
, canSmartExpand
);
3035 if (myTree
.isExpanded(getPathFor(toExpand
)) && !myUnbuiltNodes
.contains(toExpand
)) {
3036 if (!areChildrenToBeUpdated(toExpand
)) {
3037 processNodeActionsIfReady(toExpand
);
3041 if (!myUnbuiltNodes
.contains(toExpand
)) {
3042 addSubtreeToUpdate(toExpand
);
3045 expand(toExpand
, canSmartExpand
);
3050 private boolean areChildrenToBeUpdated(DefaultMutableTreeNode node
) {
3051 return getUpdater().isEnqueuedToUpdate(node
) || isUpdatingParent(node
);
3054 private String
asString(DefaultMutableTreeNode node
) {
3055 if (node
== null) return null;
3057 StringBuffer children
= new StringBuffer(node
.toString());
3058 children
.append(" [");
3059 for (int i
= 0; i
< node
.getChildCount(); i
++) {
3060 children
.append(node
.getChildAt(i
));
3061 if (i
< node
.getChildCount() - 1) {
3062 children
.append(",");
3065 children
.append("]");
3067 return children
.toString();
3071 public Object
getElementFor(Object node
) {
3072 if (!(node
instanceof DefaultMutableTreeNode
)) return null;
3073 return getElementFor((DefaultMutableTreeNode
)node
);
3077 Object
getElementFor(DefaultMutableTreeNode node
) {
3079 final Object o
= node
.getUserObject();
3080 if (o
instanceof NodeDescriptor
) {
3081 return getElementFromDescriptor(((NodeDescriptor
)o
));
3088 public final boolean isNodeBeingBuilt(final TreePath path
) {
3089 return isNodeBeingBuilt(path
.getLastPathComponent());
3092 public final boolean isNodeBeingBuilt(Object node
) {
3093 if (isParentLoading(node
) || isLoadingParent(node
)) return true;
3095 final boolean childrenAreNoLoadedYet
= myUnbuiltNodes
.contains(node
);
3096 if (childrenAreNoLoadedYet
) {
3097 if (node
instanceof DefaultMutableTreeNode
) {
3098 final TreePath nodePath
= new TreePath(((DefaultMutableTreeNode
)node
).getPath());
3099 if (!myTree
.isExpanded(nodePath
)) return false;
3109 private boolean isLoadingParent(Object node
) {
3110 if (!(node
instanceof DefaultMutableTreeNode
)) return false;
3111 return isLoadedInBackground(getElementFor((DefaultMutableTreeNode
)node
));
3114 public void setTreeStructure(final AbstractTreeStructure treeStructure
) {
3115 myTreeStructure
= treeStructure
;
3116 clearUpdaterState();
3119 public AbstractTreeUpdater
getUpdater() {
3123 public void setUpdater(final AbstractTreeUpdater updater
) {
3124 myUpdater
= updater
;
3125 if (updater
!= null && myUpdateIfInactive
) {
3126 updater
.showNotify();
3129 if (myUpdater
!= null) {
3130 myUpdater
.setPassThroughMode(myPassthroughMode
);
3134 public DefaultMutableTreeNode
getRootNode() {
3138 public void setRootNode(@NotNull final DefaultMutableTreeNode rootNode
) {
3139 myRootNode
= rootNode
;
3142 private void dropUpdaterStateIfExternalChange() {
3143 if (myUpdaterState
!= null && !myUpdaterState
.isProcessingNow()) {
3144 clearUpdaterState();
3148 void clearUpdaterState() {
3149 myUpdaterState
= null;
3152 private void createMapping(Object element
, DefaultMutableTreeNode node
) {
3153 if (!myElementToNodeMap
.containsKey(element
)) {
3154 myElementToNodeMap
.put(element
, node
);
3157 final Object value
= myElementToNodeMap
.get(element
);
3158 final List
<DefaultMutableTreeNode
> nodes
;
3159 if (value
instanceof DefaultMutableTreeNode
) {
3160 nodes
= new ArrayList
<DefaultMutableTreeNode
>();
3161 nodes
.add((DefaultMutableTreeNode
)value
);
3162 myElementToNodeMap
.put(element
, nodes
);
3165 nodes
= (List
<DefaultMutableTreeNode
>)value
;
3171 private void removeMapping(Object element
, DefaultMutableTreeNode node
, @Nullable Object elementToPutNodeActionsFor
) {
3172 final Object value
= myElementToNodeMap
.get(element
);
3173 if (value
!= null) {
3174 if (value
instanceof DefaultMutableTreeNode
) {
3175 if (value
.equals(node
)) {
3176 myElementToNodeMap
.remove(element
);
3180 List
<DefaultMutableTreeNode
> nodes
= (List
<DefaultMutableTreeNode
>)value
;
3181 final boolean reallyRemoved
= nodes
.remove(node
);
3182 if (reallyRemoved
) {
3183 if (nodes
.isEmpty()) {
3184 myElementToNodeMap
.remove(element
);
3190 remapNodeActions(element
, elementToPutNodeActionsFor
);
3193 private void remapNodeActions(Object element
, Object elementToPutNodeActionsFor
) {
3194 _remapNodeActions(element
, elementToPutNodeActionsFor
, myNodeActions
);
3195 _remapNodeActions(element
, elementToPutNodeActionsFor
, myNodeChildrenActions
);
3198 private void _remapNodeActions(Object element
, Object elementToPutNodeActionsFor
, final Map
<Object
, List
<NodeAction
>> nodeActions
) {
3199 final List
<NodeAction
> actions
= nodeActions
.get(element
);
3200 nodeActions
.remove(element
);
3202 if (elementToPutNodeActionsFor
!= null && actions
!= null) {
3203 nodeActions
.put(elementToPutNodeActionsFor
, actions
);
3207 private DefaultMutableTreeNode
getFirstNode(Object element
) {
3208 return findNode(element
, 0);
3211 private DefaultMutableTreeNode
findNode(final Object element
, int startIndex
) {
3212 final Object value
= getBuilder().findNodeByElement(element
);
3213 if (value
== null) {
3216 if (value
instanceof DefaultMutableTreeNode
) {
3217 return startIndex
== 0 ?
(DefaultMutableTreeNode
)value
: null;
3219 final List
<DefaultMutableTreeNode
> nodes
= (List
<DefaultMutableTreeNode
>)value
;
3220 return startIndex
< nodes
.size() ? nodes
.get(startIndex
) : null;
3223 protected Object
findNodeByElement(Object element
) {
3224 if (myElementToNodeMap
.containsKey(element
)) {
3225 return myElementToNodeMap
.get(element
);
3229 TREE_NODE_WRAPPER
.setValue(element
);
3230 return myElementToNodeMap
.get(TREE_NODE_WRAPPER
);
3233 TREE_NODE_WRAPPER
.setValue(null);
3237 private DefaultMutableTreeNode
findNodeForChildElement(DefaultMutableTreeNode parentNode
, Object element
) {
3238 final Object value
= myElementToNodeMap
.get(element
);
3239 if (value
== null) {
3243 if (value
instanceof DefaultMutableTreeNode
) {
3244 final DefaultMutableTreeNode elementNode
= (DefaultMutableTreeNode
)value
;
3245 return parentNode
.equals(elementNode
.getParent()) ? elementNode
: null;
3248 final List
<DefaultMutableTreeNode
> allNodesForElement
= (List
<DefaultMutableTreeNode
>)value
;
3249 for (final DefaultMutableTreeNode elementNode
: allNodesForElement
) {
3250 if (parentNode
.equals(elementNode
.getParent())) {
3258 public void cancelBackgroundLoading() {
3259 if (myWorker
!= null) {
3260 myWorker
.cancelTasks();
3267 private void addNodeAction(Object element
, NodeAction action
, boolean shouldChildrenBeReady
) {
3268 _addNodeAction(element
, action
, myNodeActions
);
3269 if (shouldChildrenBeReady
) {
3270 _addNodeAction(element
, action
, myNodeChildrenActions
);
3275 private void _addNodeAction(Object element
, NodeAction action
, Map
<Object
, List
<NodeAction
>> map
) {
3276 maybeSetBusyAndScheduleWaiterForReady(true);
3277 List
<NodeAction
> list
= map
.get(element
);
3279 list
= new ArrayList
<NodeAction
>();
3280 map
.put(element
, list
);
3286 private void cleanUpNow() {
3287 if (isReleased()) return;
3289 final UpdaterTreeState state
= new UpdaterTreeState(this);
3291 myTree
.collapsePath(new TreePath(myTree
.getModel().getRoot()));
3292 myTree
.clearSelection();
3293 getRootNode().removeAllChildren();
3295 myRootNodeWasInitialized
= false;
3297 myElementToNodeMap
.clear();
3298 myDeferredSelections
.clear();
3299 myDeferredExpansions
.clear();
3300 myLoadedInBackground
.clear();
3301 myUnbuiltNodes
.clear();
3302 myUpdateFromRootRequested
= true;
3304 if (myWorker
!= null) {
3305 Disposer
.dispose(myWorker
);
3309 myTree
.invalidate();
3311 state
.restore(null);
3314 public AbstractTreeUi
setClearOnHideDelay(final long clearOnHideDelay
) {
3315 myClearOnHideDelay
= clearOnHideDelay
;
3319 public void setJantorPollPeriod(final long time
) {
3320 myJanitorPollPeriod
= time
;
3323 public void setCheckStructure(final boolean checkStructure
) {
3324 myCheckStructure
= checkStructure
;
3327 private class MySelectionListener
implements TreeSelectionListener
{
3328 public void valueChanged(final TreeSelectionEvent e
) {
3329 dropUpdaterStateIfExternalChange();
3334 private class MyExpansionListener
implements TreeExpansionListener
{
3335 public void treeExpanded(TreeExpansionEvent event
) {
3336 dropUpdaterStateIfExternalChange();
3338 TreePath path
= event
.getPath();
3340 if (myRequestedExpand
!= null && !myRequestedExpand
.equals(path
)) return;
3342 final DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
3344 if (!myUnbuiltNodes
.contains(node
)) {
3345 removeLoading(node
, false);
3347 boolean hasUnbuiltChildren
= false;
3348 for (int i
= 0; i
< node
.getChildCount(); i
++) {
3349 DefaultMutableTreeNode each
= (DefaultMutableTreeNode
)node
.getChildAt(i
);
3350 if (myUnbuiltNodes
.contains(each
)) {
3351 makeLoadingOrLeafIfNoChildren(each
);
3352 hasUnbuiltChildren
= true;
3356 if (hasUnbuiltChildren
) {
3357 addSubtreeToUpdate(node
);
3361 getBuilder().expandNodeChildren(node
);
3364 processSmartExpand(node
, canSmartExpand(node
, true));
3365 processNodeActionsIfReady(node
);
3368 public void treeCollapsed(TreeExpansionEvent e
) {
3369 dropUpdaterStateIfExternalChange();
3371 final TreePath path
= e
.getPath();
3372 final DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)path
.getLastPathComponent();
3373 if (!(node
.getUserObject() instanceof NodeDescriptor
)) return;
3376 TreePath pathToSelect
= null;
3377 if (isSelectionInside(node
)) {
3378 pathToSelect
= new TreePath(node
.getPath());
3382 NodeDescriptor descriptor
= getDescriptorFrom(node
);
3383 if (getBuilder().isDisposeOnCollapsing(descriptor
)) {
3384 runDone(new Runnable() {
3386 if (isDisposed(node
)) return;
3388 TreePath nodePath
= new TreePath(node
.getPath());
3389 if (myTree
.isExpanded(nodePath
)) return;
3391 removeChildren(node
);
3392 makeLoadingOrLeafIfNoChildren(node
);
3395 if (node
.equals(getRootNode())) {
3396 if (myTree
.isRootVisible()) {
3397 //todo kirillk to investigate -- should be done by standard selction move
3398 //addSelectionPath(new TreePath(getRootNode().getPath()), true, Condition.FALSE);
3402 myTreeModel
.reload(node
);
3406 if (pathToSelect
!= null && myTree
.isSelectionEmpty()) {
3407 addSelectionPath(pathToSelect
, true, Condition
.FALSE
);
3411 private void removeChildren(DefaultMutableTreeNode node
) {
3412 EnumerationCopy copy
= new EnumerationCopy(node
.children());
3413 while (copy
.hasMoreElements()) {
3414 disposeNode((DefaultMutableTreeNode
)copy
.nextElement());
3416 node
.removeAllChildren();
3417 myTreeModel
.nodeStructureChanged(node
);
3420 private boolean isSelectionInside(DefaultMutableTreeNode parent
) {
3421 TreePath path
= new TreePath(myTreeModel
.getPathToRoot(parent
));
3422 TreePath
[] paths
= myTree
.getSelectionPaths();
3423 if (paths
== null) return false;
3424 for (TreePath path1
: paths
) {
3425 if (path
.isDescendant(path1
)) return true;
3431 public boolean isInStructure(@Nullable Object element
) {
3432 Object eachParent
= element
;
3433 while (eachParent
!= null) {
3434 if (getTreeStructure().getRootElement().equals(eachParent
)) return true;
3435 eachParent
= getTreeStructure().getParentElement(eachParent
);
3441 interface NodeAction
{
3442 void onReady(DefaultMutableTreeNode node
);
3445 public void setCanYield(final boolean canYield
) {
3446 myCanYield
= canYield
;
3449 public Collection
<TreeUpdatePass
> getYeildingPasses() {
3450 return myYeildingPasses
;
3453 public boolean isBuilt(Object element
) {
3454 if (!myElementToNodeMap
.containsKey(element
)) return false;
3455 final Object node
= myElementToNodeMap
.get(element
);
3456 return !myUnbuiltNodes
.contains(node
);
3459 static class LoadedChildren
{
3461 private List myElements
;
3462 private Map
<Object
, NodeDescriptor
> myDescriptors
= new HashMap
<Object
, NodeDescriptor
>();
3463 private Map
<NodeDescriptor
, Boolean
> myChanges
= new HashMap
<NodeDescriptor
, Boolean
>();
3465 LoadedChildren(Object
[] elements
) {
3466 myElements
= Arrays
.asList(elements
!= null ? elements
: new Object
[0]);
3469 void putDescriptor(Object element
, NodeDescriptor descriptor
, boolean isChanged
) {
3470 assert myElements
.contains(element
);
3471 myDescriptors
.put(element
, descriptor
);
3472 myChanges
.put(descriptor
, isChanged
);
3475 List
getElements() {
3479 NodeDescriptor
getDescriptor(Object element
) {
3480 return myDescriptors
.get(element
);
3484 public String
toString() {
3485 return Arrays
.asList(myElements
) + "->" + myChanges
;
3488 public boolean isUpdated(Object element
) {
3489 NodeDescriptor desc
= getDescriptor(element
);
3490 return myChanges
.get(desc
);
3494 UpdaterTreeState
getUpdaterState() {
3495 return myUpdaterState
;
3498 private ActionCallback
addReadyCallback(Object requestor
) {
3499 synchronized (myReadyCallbacks
) {
3500 ActionCallback cb
= myReadyCallbacks
.get(requestor
);
3502 cb
= new ActionCallback();
3503 myReadyCallbacks
.put(requestor
, cb
);
3510 private ActionCallback
[] getReadyCallbacks(boolean clear
) {
3511 synchronized (myReadyCallbacks
) {
3512 ActionCallback
[] result
= myReadyCallbacks
.values().toArray(new ActionCallback
[myReadyCallbacks
.size()]);
3514 myReadyCallbacks
.clear();
3520 private long getComparatorStamp() {
3521 if (myNodeDescriptorComparator
instanceof NodeDescriptor
.NodeComparator
) {
3522 long currentComparatorStamp
= ((NodeDescriptor
.NodeComparator
)myNodeDescriptorComparator
).getStamp();
3523 if (currentComparatorStamp
> myLastComparatorStamp
) {
3524 myOwnComparatorStamp
= Math
.max(myOwnComparatorStamp
, currentComparatorStamp
) + 1;
3526 myLastComparatorStamp
= currentComparatorStamp
;
3528 return Math
.max(currentComparatorStamp
, myOwnComparatorStamp
);
3531 return myOwnComparatorStamp
;
3535 public void incComparatorStamp() {
3536 myOwnComparatorStamp
= getComparatorStamp() + 1;
3539 public static class UpdateInfo
{
3540 NodeDescriptor myDescriptor
;
3541 TreeUpdatePass myPass
;
3542 boolean myCanSmartExpand
;
3543 boolean myWasExpanded
;
3544 boolean myForceUpdate
;
3545 boolean myDescriptorIsUpToDate
;
3547 public UpdateInfo(NodeDescriptor descriptor
,
3548 TreeUpdatePass pass
,
3549 boolean canSmartExpand
,
3550 boolean wasExpanded
,
3551 boolean forceUpdate
,
3552 boolean descriptorIsUpToDate
) {
3553 myDescriptor
= descriptor
;
3555 myCanSmartExpand
= canSmartExpand
;
3556 myWasExpanded
= wasExpanded
;
3557 myForceUpdate
= forceUpdate
;
3558 myDescriptorIsUpToDate
= descriptorIsUpToDate
;
3561 synchronized NodeDescriptor
getDescriptor() {
3562 return myDescriptor
;
3565 synchronized TreeUpdatePass
getPass() {
3569 synchronized boolean isCanSmartExpand() {
3570 return myCanSmartExpand
;
3573 synchronized boolean isWasExpanded() {
3574 return myWasExpanded
;
3577 synchronized boolean isForceUpdate() {
3578 return myForceUpdate
;
3581 synchronized boolean isDescriptorIsUpToDate() {
3582 return myDescriptorIsUpToDate
;
3585 public synchronized void apply(UpdateInfo updateInfo
) {
3586 myDescriptor
= updateInfo
.myDescriptor
;
3587 myPass
= updateInfo
.myPass
;
3588 myCanSmartExpand
= updateInfo
.myCanSmartExpand
;
3589 myWasExpanded
= updateInfo
.myWasExpanded
;
3590 myForceUpdate
= updateInfo
.myForceUpdate
;
3591 myDescriptorIsUpToDate
= updateInfo
.myDescriptorIsUpToDate
;
3596 public void setPassthroughMode(boolean passthrough
) {
3597 myPassthroughMode
= passthrough
;
3598 AbstractTreeUpdater updater
= getUpdater();
3600 if (updater
!= null) {
3601 updater
.setPassThroughMode(myPassthroughMode
);
3604 if (!isUnitTestingMode() && passthrough
) {
3605 LOG
.error("Pass-through mode for TreeUi is allowed only for unit test mode");
3609 public boolean isPassthroughMode() {
3610 return myPassthroughMode
;
3613 private boolean isUnitTestingMode() {
3614 Application app
= ApplicationManager
.getApplication();
3615 return app
!= null && app
.isUnitTestMode();