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.*;
23 import com.intellij.openapi.project.IndexNotReadyException;
24 import com.intellij.openapi.util.*;
25 import com.intellij.openapi.util.registry.Registry;
26 import com.intellij.openapi.util.registry.RegistryValue;
27 import com.intellij.ui.LoadingNode;
28 import com.intellij.ui.treeStructure.Tree;
29 import com.intellij.util.Alarm;
30 import com.intellij.util.ArrayUtil;
31 import com.intellij.util.Time;
32 import com.intellij.util.concurrency.WorkerThread;
33 import com.intellij.util.containers.ContainerUtil;
34 import com.intellij.util.containers.HashSet;
35 import com.intellij.util.enumeration.EnumerationCopy;
36 import com.intellij.util.ui.UIUtil;
37 import com.intellij.util.ui.tree.TreeUtil;
38 import com.intellij.util.ui.update.Activatable;
39 import com.intellij.util.ui.update.UiNotifyConnector;
40 import org.jetbrains.annotations.NotNull;
41 import org.jetbrains.annotations.Nullable;
44 import javax.swing.event.*;
45 import javax.swing.tree.*;
47 import java.awt.event.FocusAdapter;
48 import java.awt.event.FocusEvent;
50 import java.util.List;
51 import java.util.concurrent.TimeUnit;
52 import java.util.concurrent.atomic.AtomicBoolean;
53 import java.util.concurrent.locks.Lock;
54 import java.util.concurrent.locks.ReentrantLock;
56 public class AbstractTreeUi {
57 private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.treeView.AbstractTreeBuilder");
58 protected JTree myTree;// protected for TestNG
59 @SuppressWarnings({"WeakerAccess"}) protected DefaultTreeModel myTreeModel;
60 private AbstractTreeStructure myTreeStructure;
61 private AbstractTreeUpdater myUpdater;
63 private Comparator<NodeDescriptor> myNodeDescriptorComparator;
64 private final Comparator<TreeNode> myNodeComparator = new Comparator<TreeNode>() {
65 public int compare(TreeNode n1, TreeNode n2) {
66 if (isLoadingNode(n1) || isLoadingNode(n2)) return 0;
67 NodeDescriptor nodeDescriptor1 = getDescriptorFrom(((DefaultMutableTreeNode)n1));
68 NodeDescriptor nodeDescriptor2 = getDescriptorFrom(((DefaultMutableTreeNode)n2));
69 return myNodeDescriptorComparator != null
70 ? myNodeDescriptorComparator.compare(nodeDescriptor1, nodeDescriptor2)
71 : nodeDescriptor1.getIndex() - nodeDescriptor2.getIndex();
74 long myOwnComparatorStamp;
75 long myLastComparatorStamp;
77 private DefaultMutableTreeNode myRootNode;
78 private final HashMap<Object, Object> myElementToNodeMap = new HashMap<Object, Object>();
79 private final HashSet<DefaultMutableTreeNode> myUnbuiltNodes = new HashSet<DefaultMutableTreeNode>();
80 private TreeExpansionListener myExpansionListener;
81 private MySelectionListener mySelectionListener;
83 private WorkerThread myWorker = null;
84 private final Set<Runnable> myActiveWorkerTasks = new HashSet<Runnable>();
86 private ProgressIndicator myProgress;
87 private static final int WAIT_CURSOR_DELAY = 100;
88 private AbstractTreeNode<Object> TREE_NODE_WRAPPER;
90 private boolean myRootNodeWasQueuedToInitialize = false;
91 private boolean myRootNodeInitialized = false;
93 private final Map<Object, List<NodeAction>> myNodeActions = new HashMap<Object, List<NodeAction>>();
94 private boolean myUpdateFromRootRequested;
95 private boolean myWasEverShown;
96 private boolean myUpdateIfInactive;
98 private final Map<Object, UpdateInfo> myLoadedInBackground = new HashMap<Object, UpdateInfo>();
99 private final Map<Object, List<NodeAction>> myNodeChildrenActions = new HashMap<Object, List<NodeAction>>();
101 private long myClearOnHideDelay = -1;
102 private final Map<AbstractTreeUi, Long> ourUi2Countdown = Collections.synchronizedMap(new WeakHashMap<AbstractTreeUi, Long>());
104 private final Set<Runnable> myDeferredSelections = new HashSet<Runnable>();
105 private final Set<Runnable> myDeferredExpansions = new HashSet<Runnable>();
107 private boolean myCanProcessDeferredSelections;
109 private UpdaterTreeState myUpdaterState;
110 private AbstractTreeBuilder myBuilder;
112 private final Set<DefaultMutableTreeNode> myUpdatingChildren = new HashSet<DefaultMutableTreeNode>();
113 private long myJanitorPollPeriod = Time.SECOND * 10;
115 private boolean myCanYield = false;
117 private final List<TreeUpdatePass> myYeildingPasses = new ArrayList<TreeUpdatePass>();
119 private boolean myYeildingNow;
121 private final Set<DefaultMutableTreeNode> myPendingNodeActions = new HashSet<DefaultMutableTreeNode>();
122 private final Set<Runnable> myYeildingDoneRunnables = new HashSet<Runnable>();
124 private final Alarm myBusyAlarm = new Alarm();
125 private final Runnable myWaiterForReady = new Runnable() {
127 maybeSetBusyAndScheduleWaiterForReady(false);
131 private final RegistryValue myYeildingUpdate = Registry.get("ide.tree.yeildingUiUpdate");
132 private final RegistryValue myShowBusyIndicator = Registry.get("ide.tree.showBusyIndicator");
133 private final RegistryValue myWaitForReadyTime = Registry.get("ide.tree.waitForReadyTimeout");
135 private boolean myWasEverIndexNotReady;
136 private boolean myShowing;
137 private final FocusAdapter myFocusListener = new FocusAdapter() {
139 public void focusGained(FocusEvent e) {
143 private final Set<DefaultMutableTreeNode> myNotForSmartExpand = new HashSet<DefaultMutableTreeNode>();
144 private TreePath myRequestedExpand;
146 private TreePath mySilentExpand;
147 private TreePath mySilentSelect;
149 private final ActionCallback myInitialized = new ActionCallback();
150 private BusyObject.Impl myBusyObject = new BusyObject.Impl() {
152 protected boolean isReady() {
153 return AbstractTreeUi.this.isReady(true);
157 private boolean myPassthroughMode = false;
160 private final Set<Object> myAutoExpandRoots = new HashSet<Object>();
161 private final RegistryValue myAutoExpandDepth = Registry.get("ide.tree.autoExpandMaxDepth");
163 private final Set<DefaultMutableTreeNode> myWillBeExpaned = new HashSet<DefaultMutableTreeNode>();
164 private SimpleTimerTask myCleanupTask;
166 private AtomicBoolean myCancelRequest = new AtomicBoolean();
167 private Lock myStateLock = new ReentrantLock();
169 private AtomicBoolean myResettingToReadyNow = new AtomicBoolean();
171 private Map<Progressive, ProgressIndicator> myBatchIndicators = new HashMap<Progressive, ProgressIndicator>();
172 private Map<Progressive, ActionCallback> myBatchCallbacks = new HashMap<Progressive, ActionCallback>();
174 private Map<DefaultMutableTreeNode, DefaultMutableTreeNode> myCancelledBuild = new WeakHashMap<DefaultMutableTreeNode, DefaultMutableTreeNode>();
176 private boolean mySelectionIsAdjusted;
177 private boolean myReleaseRequested;
179 private Set<Object> myRevalidatedObjects = new HashSet<Object>();
181 private Alarm myMaybeReady = new Alarm();
182 private Runnable myMaybeReadyRunnable = new Runnable() {
189 protected void init(AbstractTreeBuilder builder,
191 DefaultTreeModel treeModel,
192 AbstractTreeStructure treeStructure,
193 @Nullable Comparator<NodeDescriptor> comparator,
194 boolean updateIfInactive) {
197 myTreeModel = treeModel;
198 addModelListenerToDianoseAccessOutsideEdt();
199 TREE_NODE_WRAPPER = getBuilder().createSearchingTreeNodeWrapper();
200 myTree.setModel(myTreeModel);
201 setRootNode((DefaultMutableTreeNode)treeModel.getRoot());
202 setTreeStructure(treeStructure);
203 myNodeDescriptorComparator = comparator;
204 myUpdateIfInactive = updateIfInactive;
206 UIUtil.invokeLaterIfNeeded(new Runnable() {
208 if (!wasRootNodeInitialized()) {
209 if (myRootNode.getChildCount() == 0) {
210 insertLoadingNode(myRootNode, true);
216 myExpansionListener = new MyExpansionListener();
217 myTree.addTreeExpansionListener(myExpansionListener);
219 mySelectionListener = new MySelectionListener();
220 myTree.addTreeSelectionListener(mySelectionListener);
222 setUpdater(getBuilder().createUpdater());
223 myProgress = getBuilder().createProgressIndicator();
224 Disposer.register(getBuilder(), getUpdater());
226 final UiNotifyConnector uiNotify = new UiNotifyConnector(tree, new Activatable() {
227 public void showNotify() {
229 myWasEverShown = true;
230 if (canInitiateNewActivity()) {
235 public void hideNotify() {
237 if (canInitiateNewActivity()) {
242 Disposer.register(getBuilder(), uiNotify);
244 myTree.addFocusListener(myFocusListener);
248 boolean isNodeActionsPending() {
249 return !myNodeActions.isEmpty() || !myNodeChildrenActions.isEmpty();
252 private void clearNodeActions() {
253 myNodeActions.clear();
254 myNodeChildrenActions.clear();
257 private void maybeSetBusyAndScheduleWaiterForReady(boolean forcedBusy) {
258 if (!myShowBusyIndicator.asBoolean() || !canYield()) return;
260 if (myTree instanceof com.intellij.ui.treeStructure.Tree) {
261 final com.intellij.ui.treeStructure.Tree tree = (Tree)myTree;
262 final boolean isBusy = !isReady(true) || forcedBusy;
263 if (isBusy && tree.isShowing()) {
264 tree.setPaintBusy(true);
265 myBusyAlarm.cancelAllRequests();
266 myBusyAlarm.addRequest(myWaiterForReady, myWaitForReadyTime.asInteger());
269 tree.setPaintBusy(false);
274 private void setHoldSize(boolean holdSize) {
275 if (myTree instanceof com.intellij.ui.treeStructure.Tree) {
276 final com.intellij.ui.treeStructure.Tree tree = (Tree)myTree;
277 tree.setHoldSize(holdSize);
281 private void cleanUpAll() {
282 final long now = System.currentTimeMillis();
283 final AbstractTreeUi[] uis = ourUi2Countdown.keySet().toArray(new AbstractTreeUi[ourUi2Countdown.size()]);
284 for (AbstractTreeUi eachUi : uis) {
285 if (eachUi == null) continue;
286 final Long timeToCleanup = ourUi2Countdown.get(eachUi);
287 if (timeToCleanup == null) continue;
288 if (now >= timeToCleanup.longValue()) {
289 ourUi2Countdown.remove(eachUi);
290 Runnable runnable = new Runnable() {
292 if (!canInitiateNewActivity()) return;
294 myCleanupTask = null;
295 getBuilder().cleanUp();
298 if (isPassthroughMode()) {
302 UIUtil.invokeLaterIfNeeded(runnable);
308 protected void doCleanUp() {
309 Runnable cleanup = new Runnable() {
311 if (canInitiateNewActivity()) {
317 if (isPassthroughMode()) {
321 UIUtil.invokeLaterIfNeeded(cleanup);
325 private ActionCallback invokeLaterIfNeeded(@NotNull final Runnable runnable) {
326 final ActionCallback result = new ActionCallback();
328 Runnable actual = new Runnable() {
335 if (isPassthroughMode() || (!isEdt() && (!isTreeShowing() && !myWasEverShown))) {
339 UIUtil.invokeLaterIfNeeded(actual);
345 public void activate(boolean byShowing) {
346 cancelCurrentCleanupTask();
348 myCanProcessDeferredSelections = true;
349 ourUi2Countdown.remove(this);
351 if (!myWasEverShown || myUpdateFromRootRequested || myUpdateIfInactive) {
352 getBuilder().updateFromRoot();
355 getUpdater().showNotify();
357 myWasEverShown |= byShowing;
360 private void cancelCurrentCleanupTask() {
361 if (myCleanupTask != null) {
362 myCleanupTask.cancel();
363 myCleanupTask = null;
367 public void deactivate() {
368 getUpdater().hideNotify();
369 myBusyAlarm.cancelAllRequests();
371 if (!myWasEverShown) return;
375 myUpdateFromRootRequested = true;
378 if (getClearOnHideDelay() >= 0) {
379 ourUi2Countdown.put(this, System.currentTimeMillis() + getClearOnHideDelay());
384 private void sheduleCleanUpAll() {
385 cancelCurrentCleanupTask();
387 myCleanupTask = SimpleTimer.getInstance().setUp(new Runnable() {
391 }, getClearOnHideDelay());
394 public void requestRelease() {
395 myReleaseRequested = true;
396 cancelUpdate().doWhenDone(new Runnable() {
403 private void releaseNow() {
407 myTree.removeTreeExpansionListener(myExpansionListener);
408 myTree.removeTreeSelectionListener(mySelectionListener);
409 myTree.removeFocusListener(myFocusListener);
411 disposeNode(getRootNode());
412 myElementToNodeMap.clear();
413 getUpdater().cancelAllRequests();
414 if (myWorker != null) {
415 myWorker.dispose(true);
418 TREE_NODE_WRAPPER.setValue(null);
419 if (myProgress != null) {
423 cancelCurrentCleanupTask();
428 myTreeStructure = null;
429 myBuilder.releaseUi();
434 myDeferredSelections.clear();
435 myDeferredExpansions.clear();
436 myYeildingDoneRunnables.clear();
438 catch (InterruptedException e) {
445 public boolean isReleased() {
446 return myBuilder == null;
449 protected void doExpandNodeChildren(final DefaultMutableTreeNode node) {
450 if (!myUnbuiltNodes.contains(node)) return;
451 if (isLoadedInBackground(getElementFor(node))) return;
453 getTreeStructure().commit();
454 addSubtreeToUpdate(node);
455 getUpdater().performUpdate();
458 public final AbstractTreeStructure getTreeStructure() {
459 return myTreeStructure;
462 public final JTree getTree() {
467 private NodeDescriptor getDescriptorFrom(DefaultMutableTreeNode node) {
468 return (NodeDescriptor)node.getUserObject();
472 public final DefaultMutableTreeNode getNodeForElement(Object element, final boolean validateAgainstStructure) {
473 DefaultMutableTreeNode result = null;
474 if (validateAgainstStructure) {
477 final DefaultMutableTreeNode node = findNode(element, index);
478 if (node == null) break;
480 if (isNodeValidForElement(element, node)) {
489 result = getFirstNode(element);
493 if (result != null && !isNodeInStructure(result)) {
501 private boolean isNodeInStructure(DefaultMutableTreeNode node) {
502 return TreeUtil.isAncestor(getRootNode(), node) && getRootNode() == myTreeModel.getRoot();
505 private boolean isNodeValidForElement(final Object element, final DefaultMutableTreeNode node) {
506 return isSameHierarchy(element, node) || isValidChildOfParent(element, node);
509 private boolean isValidChildOfParent(final Object element, final DefaultMutableTreeNode node) {
510 final DefaultMutableTreeNode parent = (DefaultMutableTreeNode)node.getParent();
511 final Object parentElement = getElementFor(parent);
512 if (!isInStructure(parentElement)) return false;
514 if (parent instanceof ElementNode) {
515 return ((ElementNode)parent).isValidChild(element);
518 for (int i = 0; i < parent.getChildCount(); i++) {
519 final TreeNode child = parent.getChildAt(i);
520 final Object eachElement = getElementFor(child);
521 if (element.equals(eachElement)) return true;
528 private boolean isSameHierarchy(Object eachParent, DefaultMutableTreeNode eachParentNode) {
529 boolean valid = true;
531 if (eachParent == null) {
532 valid = eachParentNode == null;
536 if (!eachParent.equals(getElementFor(eachParentNode))) {
541 eachParent = getTreeStructure().getParentElement(eachParent);
542 eachParentNode = (DefaultMutableTreeNode)eachParentNode.getParent();
547 public final DefaultMutableTreeNode getNodeForPath(Object[] path) {
548 DefaultMutableTreeNode node = null;
549 for (final Object pathElement : path) {
550 node = node == null ? getFirstNode(pathElement) : findNodeForChildElement(node, pathElement);
558 public final void buildNodeForElement(Object element) {
559 getUpdater().performUpdate();
560 DefaultMutableTreeNode node = getNodeForElement(element, false);
562 final java.util.List<Object> elements = new ArrayList<Object>();
564 element = getTreeStructure().getParentElement(element);
565 if (element == null) {
568 elements.add(0, element);
571 for (final Object element1 : elements) {
572 node = getNodeForElement(element1, false);
580 public final void buildNodeForPath(Object[] path) {
581 getUpdater().performUpdate();
582 DefaultMutableTreeNode node = null;
583 for (final Object pathElement : path) {
584 node = node == null ? getFirstNode(pathElement) : findNodeForChildElement(node, pathElement);
585 if (node != null && node != path[path.length - 1]) {
591 public final void setNodeDescriptorComparator(Comparator<NodeDescriptor> nodeDescriptorComparator) {
592 myNodeDescriptorComparator = nodeDescriptorComparator;
593 myLastComparatorStamp = -1;
594 getBuilder().queueUpdateFrom(getTreeStructure().getRootElement(), true);
597 protected AbstractTreeBuilder getBuilder() {
601 protected final void initRootNode() {
602 if (myUpdateIfInactive) {
606 myUpdateFromRootRequested = true;
610 private boolean initRootNodeNowIfNeeded(final TreeUpdatePass pass) {
611 boolean wasCleanedUp = false;
612 if (myRootNodeWasQueuedToInitialize) {
613 Object root = getTreeStructure().getRootElement();
614 assert root != null : "Root element cannot be null";
616 Object currentRoot = getElementFor(myRootNode);
618 if (Comparing.equal(root, currentRoot)) return false;
620 Object rootAgain = getTreeStructure().getRootElement();
621 if (root != rootAgain && !root.equals(rootAgain)) {
622 assert false : "getRootElement() if called twice must return either root1 == root2 or root1.equals(root2)";
629 if (myRootNodeWasQueuedToInitialize) return wasCleanedUp;
631 myRootNodeWasQueuedToInitialize = true;
633 final Object rootElement = getTreeStructure().getRootElement();
634 addNodeAction(rootElement, new NodeAction() {
635 public void onReady(final DefaultMutableTreeNode node) {
636 processDeferredActions();
641 final Ref<NodeDescriptor> rootDescriptor = new Ref<NodeDescriptor>(null);
642 final boolean bgLoading = getTreeStructure().isToBuildChildrenInBackground(rootElement);
644 Runnable build = new Runnable() {
646 rootDescriptor.set(getTreeStructure().createDescriptor(rootElement, null));
647 getRootNode().setUserObject(rootDescriptor.get());
648 update(rootDescriptor.get(), true);
653 Runnable update = new Runnable() {
655 if (getElementFromDescriptor(rootDescriptor.get()) != null) {
656 createMapping(getElementFromDescriptor(rootDescriptor.get()), getRootNode());
660 insertLoadingNode(getRootNode(), true);
662 boolean willUpdate = false;
663 if (isAutoExpand(rootDescriptor.get())) {
664 willUpdate = myUnbuiltNodes.contains(getRootNode());
665 expand(getRootNode(), true);
668 updateNodeChildren(getRootNode(), pass, null, false, false, false, true);
670 if (getRootNode().getChildCount() == 0) {
671 myTreeModel.nodeChanged(getRootNode());
677 queueToBackground(build, update, rootDescriptor).doWhenProcessed(new Runnable() {
680 myRootNodeInitialized = true;
681 processNodeActionsIfReady(myRootNode);
688 myRootNodeInitialized = true;
689 processNodeActionsIfReady(myRootNode);
695 private boolean isAutoExpand(NodeDescriptor descriptor) {
696 return isAutoExpand(descriptor, true);
699 private boolean isAutoExpand(NodeDescriptor descriptor, boolean validate) {
700 if (descriptor == null) return false;
702 boolean autoExpand = getBuilder().isAutoExpandNode(descriptor);
704 Object element = getElementFromDescriptor(descriptor);
706 autoExpand = validateAutoExpand(autoExpand, element);
709 if (!autoExpand && !myTree.isRootVisible()) {
710 if (element != null && element.equals(getTreeStructure().getRootElement())) return true;
716 private boolean validateAutoExpand(boolean autoExpand, Object element) {
718 int distance = getDistanceToAutoExpandRoot(element);
720 myAutoExpandRoots.add(element);
723 if (distance >= myAutoExpandDepth.asInteger() - 1) {
729 DefaultMutableTreeNode node = getNodeForElement(element, false);
730 if (isInVisibleAutoExpandChain(node)) {
741 private boolean isInVisibleAutoExpandChain(DefaultMutableTreeNode child) {
742 TreeNode eachParent = child;
743 while (eachParent != null) {
745 if (myRootNode == eachParent) return true;
747 NodeDescriptor eachDescriptor = getDescriptorFrom((DefaultMutableTreeNode)eachParent);
748 if (!isAutoExpand(eachDescriptor, false)) {
749 TreePath path = getPathFor(eachParent);
750 if (myWillBeExpaned.contains(path.getLastPathComponent()) || (myTree.isExpanded(path) && myTree.isVisible(path))) {
757 eachParent = eachParent.getParent();
763 private int getDistanceToAutoExpandRoot(Object element) {
766 Object eachParent = element;
767 while (eachParent != null) {
768 if (myAutoExpandRoots.contains(eachParent)) break;
769 eachParent = getTreeStructure().getParentElement(eachParent);
773 return eachParent != null ? distance : -1;
776 private boolean isAutoExpand(DefaultMutableTreeNode node) {
777 return isAutoExpand(getDescriptorFrom(node));
780 private AsyncResult<Boolean> update(final NodeDescriptor nodeDescriptor, boolean now) {
781 final AsyncResult<Boolean> result = new AsyncResult<Boolean>();
783 if (now || isPassthroughMode()) {
784 return new AsyncResult<Boolean>().setDone(_update(nodeDescriptor));
787 Object element = getElementFromDescriptor(nodeDescriptor);
788 boolean bgLoading = getTreeStructure().isToBuildChildrenInBackground(element);
790 boolean edt = isEdt();
793 final Ref<Boolean> changes = new Ref<Boolean>(false);
794 queueToBackground(new Runnable() {
796 changes.set(_update(nodeDescriptor));
800 result.setDone(changes.get());
805 result.setDone(_update(nodeDescriptor));
809 if (edt || !myWasEverShown) {
810 result.setDone(_update(nodeDescriptor));
813 UIUtil.invokeLaterIfNeeded(new Runnable() {
815 execute(new Runnable() {
817 result.setDone(_update(nodeDescriptor));
825 result.doWhenDone(new AsyncResult.Handler<Boolean>() {
826 public void run(Boolean changes) {
828 final long updateStamp = nodeDescriptor.getUpdateCount();
829 UIUtil.invokeLaterIfNeeded(new Runnable() {
831 Object element = nodeDescriptor.getElement();
832 DefaultMutableTreeNode node = getNodeForElement(element, false);
834 TreePath path = getPathFor(node);
835 if (path != null && myTree.isVisible(path)) {
836 updateNodeImageAndPosition(node, false);
849 private boolean _update(final NodeDescriptor nodeDescriptor) {
851 final Ref<Boolean> update = new Ref<Boolean>();
854 execute(new Runnable() {
856 nodeDescriptor.setUpdateCount(nodeDescriptor.getUpdateCount() + 1);
857 update.set(getBuilder().updateNodeDescriptor(nodeDescriptor));
861 catch (InterruptedException e) {
862 throw new ProcessCanceledException();
863 } catch (ProcessCanceledException e) {
870 catch (IndexNotReadyException e) {
871 warnOnIndexNotReady();
876 private void assertIsDispatchThread() {
877 if (isPassthroughMode()) return;
879 if ((isTreeShowing() || myWasEverShown) && !isEdt()) {
880 LOG.error("Must be in event-dispatch thread");
884 private boolean isEdt() {
885 return SwingUtilities.isEventDispatchThread();
888 private boolean isTreeShowing() {
892 private void assertNotDispatchThread() {
893 if (isPassthroughMode()) return;
896 LOG.error("Must not be in event-dispatch thread");
900 private void processDeferredActions() {
901 processDeferredActions(myDeferredSelections);
902 processDeferredActions(myDeferredExpansions);
905 private void processDeferredActions(Set<Runnable> actions) {
906 final Runnable[] runnables = actions.toArray(new Runnable[actions.size()]);
908 for (Runnable runnable : runnables) {
913 //todo: to make real callback
914 public ActionCallback queueUpdate(Object element) {
916 AbstractTreeUpdater updater = getUpdater();
917 if (updater == null) {
918 return new ActionCallback.Rejected();
921 final ActionCallback result = new ActionCallback();
922 DefaultMutableTreeNode node = getNodeForElement(element, false);
924 addSubtreeToUpdate(node);
927 addSubtreeToUpdate(getRootNode());
930 updater.runAfterUpdate(new Runnable() {
937 catch (ProcessCanceledException e) {
938 return new ActionCallback.Rejected();
942 public void doUpdateFromRoot() {
943 updateSubtree(getRootNode(), false);
946 public ActionCallback doUpdateFromRootCB() {
947 final ActionCallback cb = new ActionCallback();
948 getUpdater().runAfterUpdate(new Runnable() {
953 updateSubtree(getRootNode(), false);
957 public final void updateSubtree(DefaultMutableTreeNode node, boolean canSmartExpand) {
958 updateSubtree(new TreeUpdatePass(node), canSmartExpand);
961 public final void updateSubtree(TreeUpdatePass pass, boolean canSmartExpand) {
962 if (getUpdater() != null) {
963 getUpdater().addSubtreeToUpdate(pass);
966 updateSubtreeNow(pass, canSmartExpand);
970 final void updateSubtreeNow(TreeUpdatePass pass, boolean canSmartExpand) {
973 maybeSetBusyAndScheduleWaiterForReady(true);
976 boolean consumed = initRootNodeNowIfNeeded(pass);
977 if (consumed) return;
979 final DefaultMutableTreeNode node = pass.getNode();
981 if (!(node.getUserObject() instanceof NodeDescriptor)) return;
983 setUpdaterState(new UpdaterTreeState(this)).beforeSubtreeUpdate();
985 boolean forceUpdate = true;
986 TreePath path = getPathFor(node);
987 boolean invisible = !myTree.isExpanded(path) && (path.getParentPath() == null || !myTree.isExpanded(path.getParentPath()));
989 if (invisible && myUnbuiltNodes.contains(node)) {
993 updateNodeChildren(node, pass, null, false, canSmartExpand, forceUpdate, false);
996 private boolean isToBuildInBackground(NodeDescriptor descriptor) {
997 return getTreeStructure().isToBuildChildrenInBackground(getBuilder().getTreeStructureElement(descriptor));
1001 UpdaterTreeState setUpdaterState(UpdaterTreeState state) {
1002 if (myUpdaterState != null && myUpdaterState.equals(state)) return state;
1004 final UpdaterTreeState oldState = myUpdaterState;
1005 if (oldState == null) {
1006 myUpdaterState = state;
1010 oldState.addAll(state);
1015 protected void doUpdateNode(final DefaultMutableTreeNode node) {
1016 if (!(node.getUserObject() instanceof NodeDescriptor)) return;
1017 final NodeDescriptor descriptor = getDescriptorFrom(node);
1018 final Object prevElement = getElementFromDescriptor(descriptor);
1019 if (prevElement == null) return;
1020 update(descriptor, false).doWhenDone(new AsyncResult.Handler<Boolean>() {
1021 public void run(Boolean changes) {
1022 if (!isValid(descriptor)) {
1023 if (isInStructure(prevElement)) {
1024 getUpdater().addSubtreeToUpdateByElement(getTreeStructure().getParentElement(prevElement));
1029 updateNodeImageAndPosition(node, true);
1035 public Object getElementFromDescriptor(NodeDescriptor descriptor) {
1036 return getBuilder().getTreeStructureElement(descriptor);
1039 private void updateNodeChildren(final DefaultMutableTreeNode node,
1040 final TreeUpdatePass pass,
1041 @Nullable final LoadedChildren loadedChildren,
1042 final boolean forcedNow,
1043 final boolean toSmartExpand,
1044 final boolean forceUpdate,
1045 final boolean descriptorIsUpToDate) {
1047 removeFromCancelled(node);
1049 execute(new Runnable() {
1052 getTreeStructure().commit();
1055 final NodeDescriptor descriptor = getDescriptorFrom(node);
1056 if (descriptor == null) {
1057 removeLoading(node, true);
1061 final boolean wasExpanded = myTree.isExpanded(new TreePath(node.getPath())) || isAutoExpand(node);
1062 final boolean wasLeaf = node.getChildCount() == 0;
1065 boolean bgBuild = isToBuildInBackground(descriptor);
1066 boolean notRequiredToUpdateChildren = !forcedNow && !wasExpanded;
1068 if (notRequiredToUpdateChildren && forceUpdate && !wasExpanded) {
1069 boolean alwaysPlus = getBuilder().isAlwaysShowPlus(descriptor);
1070 if (alwaysPlus && wasLeaf) {
1071 notRequiredToUpdateChildren = false;
1074 notRequiredToUpdateChildren = alwaysPlus;
1078 final Ref<LoadedChildren> preloaded = new Ref<LoadedChildren>(loadedChildren);
1079 boolean descriptorWasUpdated = descriptorIsUpToDate;
1081 if (notRequiredToUpdateChildren) {
1082 if (myUnbuiltNodes.contains(node) && node.getChildCount() == 0) {
1083 insertLoadingNode(node, true);
1090 if (myUnbuiltNodes.contains(node)) {
1091 if (!descriptorWasUpdated) {
1092 update(descriptor, true);
1093 descriptorWasUpdated = true;
1096 if (processAlwaysLeaf(node)) return;
1098 Pair<Boolean, LoadedChildren> unbuilt = processUnbuilt(node, descriptor, pass, wasExpanded, null);
1100 if (unbuilt.getFirst()) return;
1101 preloaded.set(unbuilt.getSecond());
1107 final boolean childForceUpdate = isChildNodeForceUpdate(node, forceUpdate, wasExpanded);
1109 if (!forcedNow && isToBuildInBackground(descriptor)) {
1110 if (processAlwaysLeaf(node)) return;
1112 queueBackgroundUpdate(
1113 new UpdateInfo(descriptor, pass, canSmartExpand(node, toSmartExpand), wasExpanded, childForceUpdate, descriptorWasUpdated), node);
1117 if (!descriptorWasUpdated) {
1118 update(descriptor, false).doWhenDone(new Runnable() {
1120 if (processAlwaysLeaf(node)) return;
1121 updateNodeChildrenNow(node, pass, preloaded.get(), toSmartExpand, wasExpanded, wasLeaf, childForceUpdate);
1126 if (processAlwaysLeaf(node)) return;
1128 updateNodeChildrenNow(node, pass, preloaded.get(), toSmartExpand, wasExpanded, wasLeaf, childForceUpdate);
1133 if (isReleased()) return;
1135 processNodeActionsIfReady(node);
1141 private boolean processAlwaysLeaf(DefaultMutableTreeNode node) {
1142 Object element = getElementFor(node);
1143 NodeDescriptor desc = getDescriptorFrom(node);
1145 if (desc == null) return false;
1147 if (getTreeStructure().isAlwaysLeaf(element)) {
1148 removeLoading(node, true);
1150 if (node.getChildCount() > 0) {
1151 final TreeNode[] children = new TreeNode[node.getChildCount()];
1152 for (int i = 0; i < node.getChildCount(); i++) {
1153 children[i] = node.getChildAt(i);
1156 if (isSelectionInside(node)) {
1157 addSelectionPath(getPathFor(node), true, Condition.TRUE, null);
1160 processInnerChange(new Runnable() {
1162 for (TreeNode each : children) {
1163 removeNodeFromParent((MutableTreeNode)each, true);
1164 disposeNode((DefaultMutableTreeNode)each);
1170 removeFromUnbuilt(node);
1171 desc.setWasDeclaredAlwaysLeaf(true);
1172 processNodeActionsIfReady(node);
1176 boolean wasLeaf = desc.isWasDeclaredAlwaysLeaf();
1177 desc.setWasDeclaredAlwaysLeaf(false);
1180 insertLoadingNode(node, true);
1187 private boolean isChildNodeForceUpdate(DefaultMutableTreeNode node, boolean parentForceUpdate, boolean parentExpanded) {
1188 TreePath path = getPathFor(node);
1189 return parentForceUpdate && (parentExpanded || myTree.isExpanded(path));
1192 private void updateNodeChildrenNow(final DefaultMutableTreeNode node,
1193 final TreeUpdatePass pass,
1194 final LoadedChildren preloadedChildren,
1195 final boolean toSmartExpand,
1196 final boolean wasExpanded,
1197 final boolean wasLeaf,
1198 final boolean forceUpdate) {
1199 if (!canInitiateNewActivity()) {
1200 throw new ProcessCanceledException();
1203 final NodeDescriptor descriptor = getDescriptorFrom(node);
1205 final MutualMap<Object, Integer> elementToIndexMap = loadElementsFromStructure(descriptor, preloadedChildren);
1206 final LoadedChildren loadedChildren =
1207 preloadedChildren != null ? preloadedChildren : new LoadedChildren(elementToIndexMap.getKeys().toArray());
1210 addToUpdating(node);
1211 pass.setCurrentNode(node);
1213 final boolean canSmartExpand = canSmartExpand(node, toSmartExpand);
1215 processExistingNodes(node, elementToIndexMap, pass, canSmartExpand(node, toSmartExpand), forceUpdate, wasExpanded, preloadedChildren)
1216 .doWhenDone(new Runnable() {
1218 if (isDisposed(node)) {
1219 removeFromUpdating(node);
1223 removeLoading(node, false);
1225 final boolean expanded = isExpanded(node, wasExpanded);
1228 myWillBeExpaned.add(node);
1231 myWillBeExpaned.remove(node);
1234 collectNodesToInsert(descriptor, elementToIndexMap, node, expanded, loadedChildren)
1235 .doWhenDone(new AsyncResult.Handler<ArrayList<TreeNode>>() {
1236 public void run(ArrayList<TreeNode> nodesToInsert) {
1237 insertNodesInto(nodesToInsert, node);
1238 updateNodesToInsert(nodesToInsert, pass, canSmartExpand, isChildNodeForceUpdate(node, forceUpdate, expanded));
1239 removeLoading(node, true);
1240 removeFromUpdating(node);
1242 if (node.getChildCount() > 0) {
1244 expand(node, canSmartExpand);
1248 final Object element = getElementFor(node);
1249 addNodeAction(element, new NodeAction() {
1250 public void onReady(final DefaultMutableTreeNode node) {
1251 removeLoading(node, false);
1255 processNodeActionsIfReady(node);
1257 }).doWhenProcessed(new Runnable() {
1259 myWillBeExpaned.remove(node);
1260 removeFromUpdating(node);
1261 processNodeActionsIfReady(node);
1265 }).doWhenRejected(new Runnable() {
1267 removeFromUpdating(node);
1268 processNodeActionsIfReady(node);
1273 private boolean isDisposed(DefaultMutableTreeNode node) {
1274 return !node.isNodeAncestor((DefaultMutableTreeNode)myTree.getModel().getRoot());
1277 private void expandSilently(TreePath path) {
1278 assertIsDispatchThread();
1281 mySilentExpand = path;
1282 getTree().expandPath(path);
1285 mySilentExpand = null;
1289 private void addSelectionSilently(TreePath path) {
1290 assertIsDispatchThread();
1293 mySilentSelect = path;
1294 getTree().getSelectionModel().addSelectionPath(path);
1297 mySilentSelect = null;
1301 private void expand(DefaultMutableTreeNode node, boolean canSmartExpand) {
1302 expand(new TreePath(node.getPath()), canSmartExpand);
1305 private void expand(final TreePath path, boolean canSmartExpand) {
1306 if (path == null) return;
1309 final Object last = path.getLastPathComponent();
1310 boolean isLeaf = myTree.getModel().isLeaf(path.getLastPathComponent());
1311 final boolean isRoot = last == myTree.getModel().getRoot();
1312 final TreePath parent = path.getParentPath();
1313 if (isRoot && !myTree.isExpanded(path)) {
1314 if (myTree.isRootVisible() || myUnbuiltNodes.contains(last)) {
1315 insertLoadingNode((DefaultMutableTreeNode)last, false);
1317 expandPath(path, canSmartExpand);
1319 else if (myTree.isExpanded(path) || (isLeaf && parent != null && myTree.isExpanded(parent) && !myUnbuiltNodes.contains(last) && !isCancelled(last))) {
1320 if (last instanceof DefaultMutableTreeNode) {
1321 processNodeActionsIfReady((DefaultMutableTreeNode)last);
1325 if (isLeaf && (myUnbuiltNodes.contains(last) || isCancelled(last))) {
1326 insertLoadingNode((DefaultMutableTreeNode)last, true);
1327 expandPath(path, canSmartExpand);
1329 else if (isLeaf && parent != null) {
1330 final DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode)parent.getLastPathComponent();
1331 if (parentNode != null) {
1332 addToUnbuilt(parentNode);
1334 expandPath(parent, canSmartExpand);
1337 expandPath(path, canSmartExpand);
1342 private void addToUnbuilt(DefaultMutableTreeNode node) {
1343 myUnbuiltNodes.add(node);
1346 private void removeFromUnbuilt(DefaultMutableTreeNode node) {
1347 myUnbuiltNodes.remove(node);
1350 private Pair<Boolean, LoadedChildren> processUnbuilt(final DefaultMutableTreeNode node,
1351 final NodeDescriptor descriptor,
1352 final TreeUpdatePass pass,
1353 final boolean isExpanded,
1354 final LoadedChildren loadedChildren) {
1355 final Ref<Pair<Boolean, LoadedChildren>> result = new Ref<Pair<Boolean, LoadedChildren>>();
1357 execute(new Runnable() {
1359 if (!isExpanded && getBuilder().isAlwaysShowPlus(descriptor)) {
1360 result.set(new Pair<Boolean, LoadedChildren>(true, null));
1364 final Object element = getElementFor(node);
1366 addToUpdating(node);
1369 final LoadedChildren children = loadedChildren != null ? loadedChildren : new LoadedChildren(getChildrenFor(element));
1373 if (children.getElements().size() == 0) {
1374 removeLoading(node, true);
1378 if (isAutoExpand(node)) {
1379 addNodeAction(getElementFor(node), new NodeAction() {
1380 public void onReady(final DefaultMutableTreeNode node) {
1381 final TreePath path = new TreePath(node.getPath());
1382 if (getTree().isExpanded(path) || children.getElements().size() == 0) {
1383 removeLoading(node, false);
1386 maybeYeild(new ActiveRunnable() {
1387 public ActionCallback run() {
1388 expand(element, null);
1389 return new ActionCallback.Done();
1399 removeFromUpdating(node);
1401 processNodeActionsIfReady(node);
1403 result.set(new Pair<Boolean, LoadedChildren>(processed, children));
1406 removeFromUpdating(node);
1411 return result.get();
1414 private boolean removeIfLoading(TreeNode node) {
1415 if (isLoadingNode(node)) {
1416 moveSelectionToParentIfNeeded(node);
1417 removeNodeFromParent((MutableTreeNode)node, false);
1424 private void moveSelectionToParentIfNeeded(TreeNode node) {
1425 TreePath path = getPathFor(node);
1426 if (myTree.getSelectionModel().isPathSelected(path)) {
1427 TreePath parentPath = path.getParentPath();
1428 myTree.getSelectionModel().removeSelectionPath(path);
1429 if (parentPath != null) {
1430 myTree.getSelectionModel().addSelectionPath(parentPath);
1435 //todo [kirillk] temporary consistency check
1436 private Object[] getChildrenFor(final Object element) {
1437 final Ref<Object[]> passOne = new Ref<Object[]>();
1440 execute(new Runnable() {
1442 passOne.set(getTreeStructure().getChildElements(element));
1446 catch (IndexNotReadyException e) {
1447 warnOnIndexNotReady();
1448 return ArrayUtil.EMPTY_OBJECT_ARRAY;
1450 catch (InterruptedException e) {
1451 throw new ProcessCanceledException();
1456 if (!Registry.is("ide.tree.checkStructure")) return passOne.get();
1458 final Object[] passTwo = getTreeStructure().getChildElements(element);
1460 final HashSet two = new HashSet(Arrays.asList(passTwo));
1462 if (passOne.get().length != passTwo.length) {
1464 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
1468 for (Object eachInOne : passOne.get()) {
1469 if (!two.contains(eachInOne)) {
1471 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
1478 return passOne.get();
1481 private void warnOnIndexNotReady() {
1482 if (!myWasEverIndexNotReady) {
1483 myWasEverIndexNotReady = true;
1484 LOG.warn("Tree is not dumb-mode-aware; treeBuilder=" + getBuilder() + " treeStructure=" + getTreeStructure());
1488 private void updateNodesToInsert(final ArrayList<TreeNode> nodesToInsert,
1489 TreeUpdatePass pass,
1490 boolean canSmartExpand,
1491 boolean forceUpdate) {
1492 for (TreeNode aNodesToInsert : nodesToInsert) {
1493 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)aNodesToInsert;
1494 updateNodeChildren(childNode, pass, null, false, canSmartExpand, forceUpdate, true);
1498 private ActionCallback processExistingNodes(final DefaultMutableTreeNode node,
1499 final MutualMap<Object, Integer> elementToIndexMap,
1500 final TreeUpdatePass pass,
1501 final boolean canSmartExpand,
1502 final boolean forceUpdate,
1503 final boolean wasExpaned,
1504 final LoadedChildren preloaded) {
1506 final ArrayList<TreeNode> childNodes = TreeUtil.childrenToArray(node);
1507 return maybeYeild(new ActiveRunnable() {
1508 public ActionCallback run() {
1509 if (pass.isExpired()) return new ActionCallback.Rejected();
1510 if (childNodes.size() == 0) return new ActionCallback.Done();
1513 final ActionCallback result = new ActionCallback(childNodes.size());
1515 for (TreeNode each : childNodes) {
1516 final DefaultMutableTreeNode eachChild = (DefaultMutableTreeNode)each;
1517 if (isLoadingNode(eachChild)) {
1522 final boolean childForceUpdate = isChildNodeForceUpdate(eachChild, forceUpdate, wasExpaned);
1524 maybeYeild(new ActiveRunnable() {
1526 public ActionCallback run() {
1527 return processExistingNode(eachChild, getDescriptorFrom(eachChild), node, elementToIndexMap, pass, canSmartExpand,
1528 childForceUpdate, preloaded);
1530 }, pass, node).notify(result);
1532 if (result.isRejected()) {
1542 private boolean isRerunNeeded(TreeUpdatePass pass) {
1543 if (pass.isExpired() || !canInitiateNewActivity()) return false;
1545 final boolean rerunBecauseTreeIsHidden = !pass.isExpired() && !isTreeShowing() && getUpdater().isInPostponeMode();
1547 return rerunBecauseTreeIsHidden || getUpdater().isRerunNeededFor(pass);
1550 private ActionCallback maybeYeild(final ActiveRunnable processRunnable, final TreeUpdatePass pass, final DefaultMutableTreeNode node) {
1551 final ActionCallback result = new ActionCallback();
1553 if (isRerunNeeded(pass)) {
1554 getUpdater().addSubtreeToUpdate(pass);
1555 result.setRejected();
1558 if (isToYieldUpdateFor(node)) {
1559 pass.setCurrentNode(node);
1560 boolean wasRun = yieldAndRun(new Runnable() {
1562 if (pass.isExpired()) {
1563 result.setRejected();
1567 if (isRerunNeeded(pass)) {
1568 runDone(new Runnable() {
1570 if (!pass.isExpired()) {
1571 getUpdater().addSubtreeToUpdate(pass);
1575 result.setRejected();
1579 execute(processRunnable).notify(result);
1581 catch (ProcessCanceledException e) {
1583 result.setRejected();
1590 result.setRejected();
1595 execute(processRunnable).notify(result);
1597 catch (ProcessCanceledException e) {
1599 result.setRejected();
1608 private ActionCallback execute(final ActiveRunnable runnable) throws ProcessCanceledException {
1609 final ActionCallback result = new ActionCallback();
1610 execute(new Runnable() {
1612 runnable.run().notify(result);
1618 private void execute(Runnable runnable) {
1619 execute(runnable, null);
1622 private void execute(Runnable runnable, @Nullable DefaultMutableTreeNode node) throws ProcessCanceledException {
1624 if (!canInitiateNewActivity()) {
1625 throw new ProcessCanceledException();
1630 if (!canInitiateNewActivity()) {
1631 throw new ProcessCanceledException();
1634 catch (ProcessCanceledException e) {
1636 addToCancelled(node);
1638 if (!isReleased()) {
1639 setCancelRequested(true);
1646 private boolean canInitiateNewActivity() {
1647 return !isCancelProcessed() && !myReleaseRequested && !isReleased();
1650 private ActionCallback resetToReady() {
1651 final ActionCallback result = new ActionCallback();
1658 myResettingToReadyNow.set(true);
1660 invokeLaterIfNeeded(new Runnable() {
1662 Progressive[] progressives = myBatchIndicators.keySet().toArray(new Progressive[myBatchIndicators.size()]);
1663 for (Progressive each : progressives) {
1664 myBatchIndicators.remove(each).cancel();
1665 myBatchCallbacks.remove(each).setRejected();
1668 resetToReadyNow().notify(result);
1675 private ActionCallback resetToReadyNow() {
1676 if (isReleased()) return new ActionCallback.Rejected();
1678 assertIsDispatchThread();
1680 DefaultMutableTreeNode[] uc = myUpdatingChildren.toArray(new DefaultMutableTreeNode[myUpdatingChildren.size()]);
1681 for (DefaultMutableTreeNode each : uc) {
1682 resetIncompleteNode(each);
1686 Object[] bg = myLoadedInBackground.keySet().toArray(new Object[myLoadedInBackground.size()]);
1687 for (Object each : bg) {
1688 resetIncompleteNode(getNodeForElement(each, false));
1691 myUpdaterState = null;
1692 getUpdater().reset();
1695 myYeildingNow = false;
1696 myYeildingPasses.clear();
1697 myYeildingDoneRunnables.clear();
1699 myNodeActions.clear();
1700 myNodeChildrenActions.clear();
1702 myUpdatingChildren.clear();
1703 myLoadedInBackground.clear();
1705 myDeferredExpansions.clear();
1706 myDeferredSelections.clear();
1708 ActionCallback result = getReady(this);
1709 result.doWhenDone(new Runnable() {
1711 myResettingToReadyNow.set(false);
1712 setCancelRequested(false);
1721 public void addToCancelled(DefaultMutableTreeNode node) {
1722 myCancelledBuild.put(node, node);
1725 public void removeFromCancelled(DefaultMutableTreeNode node) {
1726 myCancelledBuild.remove(node);
1729 public boolean isCancelled(Object node) {
1730 if (node instanceof DefaultMutableTreeNode) {
1731 return myCancelledBuild.containsKey((DefaultMutableTreeNode)node);
1737 private void resetIncompleteNode(DefaultMutableTreeNode node) {
1738 if (myReleaseRequested) return;
1740 addToCancelled(node);
1742 if (!isExpanded(node, false)) {
1743 node.removeAllChildren();
1744 if (!getTreeStructure().isAlwaysLeaf(getElementFor(node))) {
1745 insertLoadingNode(node, true);
1749 removeLoading(node, true);
1753 private boolean yieldAndRun(final Runnable runnable, final TreeUpdatePass pass) {
1754 myYeildingPasses.add(pass);
1755 myYeildingNow = true;
1756 yield(new Runnable() {
1758 if (isReleased()) return;
1760 runOnYieldingDone(new Runnable() {
1762 if (isReleased()) return;
1764 executeYieldingRequest(runnable, pass);
1773 public boolean isYeildingNow() {
1774 return myYeildingNow;
1777 private boolean hasSheduledUpdates() {
1778 return getUpdater().hasNodesToUpdate();
1781 public boolean isReady() {
1782 return isReady(false);
1785 public boolean isReady(boolean attempt) {
1786 Boolean ready = _isReady(attempt);
1787 return ready != null && ready.booleanValue();
1791 public Boolean _isReady(boolean attempt) {
1792 Boolean ready = checkValue(new Computable<Boolean>() {
1794 public Boolean compute() {
1795 return Boolean.valueOf(isIdle() && !hasPendingWork() && !isNodeActionsPending());
1799 return ready != null && ready.booleanValue();
1802 private Boolean checkValue(Computable<Boolean> computable, boolean attempt, Boolean defaultValue) {
1803 boolean toRelease = true;
1806 if (!attemptLock()) {
1808 return defaultValue != null ? defaultValue : computable.compute();
1813 return computable.compute();
1815 catch (InterruptedException e) {
1817 return defaultValue;
1825 public String getStatus() {
1826 return "isReady=" + isReady() + "\n" +
1827 " isIdle=" + isIdle() + "\n" +
1828 " isYeildingNow=" + isYeildingNow() + "\n" +
1829 " isWorkerBusy=" + isWorkerBusy() + "\n" +
1830 " hasUpdatingNow=" + hasUpdatingNow() + "\n" +
1831 " isLoadingInBackgroundNow=" + isLoadingInBackgroundNow() + "\n" +
1832 " hasPendingWork=" + hasPendingWork() + "\n" +
1833 " hasNodesToUpdate=" + hasNodesToUpdate() + "\n" +
1834 " updaterState=" + myUpdaterState + "\n" +
1835 " hasScheduledUpdates=" + hasSheduledUpdates() + "\n" +
1836 " isPostponedMode=" + getUpdater().isInPostponeMode() + "\n" +
1837 " nodeActions=" + myNodeActions.keySet() + "\n" +
1838 " nodeChildrenActions=" + myNodeChildrenActions.keySet() + "\n" +
1839 "isReleased=" + isReleased() + "\n" +
1840 " isReleaseRequested=" + isReleaseRequested() + "\n" +
1841 "isCancelProcessed=" + isCancelProcessed() + "\n" +
1842 " isCancelRequested=" + myCancelRequest + "\n" +
1843 " isResettingToReadyNow=" + myResettingToReadyNow + "\n" +
1844 "canInitiateNewActivity=" + canInitiateNewActivity();
1847 public boolean hasPendingWork() {
1848 return hasNodesToUpdate() || (myUpdaterState != null && myUpdaterState.isProcessingNow()) || (hasSheduledUpdates() && !getUpdater().isInPostponeMode());
1851 public boolean isIdle() {
1852 return !isYeildingNow() && !isWorkerBusy() && !hasUpdatingNow() && !isLoadingInBackgroundNow();
1855 private void executeYieldingRequest(Runnable runnable, TreeUpdatePass pass) {
1858 myYeildingPasses.remove(pass);
1862 if (!isReleased()) {
1863 maybeYeildingFinished();
1867 catch (ProcessCanceledException e) {
1872 private void maybeYeildingFinished() {
1873 if (myYeildingPasses.size() == 0) {
1874 myYeildingNow = false;
1875 flushPendingNodeActions();
1880 assertIsDispatchThread();
1882 if (isReleased()) return;
1884 Boolean ready = _isReady(true);
1885 if (ready != null && ready.booleanValue()) {
1886 myRevalidatedObjects.clear();
1888 setCancelRequested(false);
1889 myResettingToReadyNow.set(false);
1891 myInitialized.setDone();
1893 if (canInitiateNewActivity()) {
1894 if (myUpdaterState != null && !myUpdaterState.isProcessingNow()) {
1895 UpdaterTreeState oldState = myUpdaterState;
1896 if (!myUpdaterState.restore(null)) {
1897 setUpdaterState(oldState);
1908 if (myTree.isShowing()) {
1909 if (getBuilder().isToEnsureSelectionOnFocusGained() && Registry.is("ide.tree.ensureSelectionOnFocusGained")) {
1910 TreeUtil.ensureSelection(myTree);
1914 if (myInitialized.isDone()) {
1915 myBusyObject.onReady();
1918 if (canInitiateNewActivity()) {
1919 TreePath[] selection = getTree().getSelectionPaths();
1920 Rectangle visible = getTree().getVisibleRect();
1921 if (selection != null) {
1922 for (TreePath each : selection) {
1923 Rectangle bounds = getTree().getPathBounds(each);
1924 if (bounds != null && (visible.contains(bounds) || visible.intersects(bounds))) {
1925 getTree().repaint(bounds);
1930 } else if (ready == null) {
1931 scheduleMaybeReady();
1935 private void scheduleMaybeReady() {
1936 myMaybeReady.cancelAllRequests();
1937 myMaybeReady.addRequest(myMaybeReadyRunnable, Registry.intValue("ide.tree.waitForReadySchedule"));
1940 private void flushPendingNodeActions() {
1941 final DefaultMutableTreeNode[] nodes = myPendingNodeActions.toArray(new DefaultMutableTreeNode[myPendingNodeActions.size()]);
1942 myPendingNodeActions.clear();
1944 for (DefaultMutableTreeNode each : nodes) {
1945 processNodeActionsIfReady(each);
1948 final Runnable[] actions = myYeildingDoneRunnables.toArray(new Runnable[myYeildingDoneRunnables.size()]);
1949 for (Runnable each : actions) {
1950 if (!isYeildingNow()) {
1951 myYeildingDoneRunnables.remove(each);
1959 protected void runOnYieldingDone(Runnable onDone) {
1960 getBuilder().runOnYeildingDone(onDone);
1963 protected void yield(Runnable runnable) {
1964 getBuilder().yield(runnable);
1967 private boolean isToYieldUpdateFor(final DefaultMutableTreeNode node) {
1968 if (!canYield()) return false;
1969 return getBuilder().isToYieldUpdateFor(node);
1972 private MutualMap<Object, Integer> loadElementsFromStructure(final NodeDescriptor descriptor,
1973 @Nullable LoadedChildren preloadedChildren) {
1974 MutualMap<Object, Integer> elementToIndexMap = new MutualMap<Object, Integer>(true);
1975 List children = preloadedChildren != null
1976 ? preloadedChildren.getElements()
1977 : Arrays.asList(getChildrenFor(getBuilder().getTreeStructureElement(descriptor)));
1979 for (Object child : children) {
1980 if (!isValid(child)) continue;
1981 elementToIndexMap.put(child, Integer.valueOf(index));
1984 return elementToIndexMap;
1987 private void expand(final DefaultMutableTreeNode node,
1988 final NodeDescriptor descriptor,
1989 final boolean wasLeaf,
1990 final boolean canSmartExpand) {
1991 final Alarm alarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
1992 alarm.addRequest(new Runnable() {
1994 myTree.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1996 }, WAIT_CURSOR_DELAY);
1998 if (wasLeaf && isAutoExpand(descriptor)) {
1999 expand(node, canSmartExpand);
2002 ArrayList<TreeNode> nodes = TreeUtil.childrenToArray(node);
2003 for (TreeNode node1 : nodes) {
2004 final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)node1;
2005 if (isLoadingNode(childNode)) continue;
2006 NodeDescriptor childDescr = getDescriptorFrom(childNode);
2007 if (isAutoExpand(childDescr)) {
2008 addNodeAction(getElementFor(childNode), new NodeAction() {
2009 public void onReady(DefaultMutableTreeNode node) {
2010 expand(childNode, canSmartExpand);
2013 addSubtreeToUpdate(childNode);
2017 int n = alarm.cancelAllRequests();
2019 myTree.setCursor(Cursor.getDefaultCursor());
2023 public static boolean isLoadingNode(final Object node) {
2024 return node instanceof LoadingNode;
2027 private AsyncResult<ArrayList<TreeNode>> collectNodesToInsert(final NodeDescriptor descriptor,
2028 final MutualMap<Object, Integer> elementToIndexMap,
2029 final DefaultMutableTreeNode parent,
2030 final boolean addLoadingNode,
2031 @NotNull final LoadedChildren loadedChildren) {
2032 final AsyncResult<ArrayList<TreeNode>> result = new AsyncResult<ArrayList<TreeNode>>();
2034 final ArrayList<TreeNode> nodesToInsert = new ArrayList<TreeNode>();
2035 final Collection<Object> allElements = elementToIndexMap.getKeys();
2037 final ActionCallback processingDone = new ActionCallback(allElements.size());
2039 for (final Object child : allElements) {
2040 Integer index = elementToIndexMap.getValue(child);
2041 final Ref<NodeDescriptor> childDescr = new Ref<NodeDescriptor>(loadedChildren.getDescriptor(child));
2042 boolean needToUpdate = false;
2043 if (childDescr.get() == null) {
2044 childDescr.set(getTreeStructure().createDescriptor(child, descriptor));
2045 needToUpdate = true;
2048 if (childDescr.get() == null) {
2049 processingDone.setDone();
2052 childDescr.get().setIndex(index.intValue());
2054 final ActionCallback update = new ActionCallback();
2056 update(childDescr.get(), false).doWhenDone(new AsyncResult.Handler<Boolean>() {
2057 public void run(Boolean changes) {
2058 loadedChildren.putDescriptor(child, childDescr.get(), changes);
2067 update.doWhenDone(new Runnable() {
2069 Object element = getElementFromDescriptor(childDescr.get());
2070 if (element == null) {
2071 processingDone.setDone();
2074 DefaultMutableTreeNode node = getNodeForElement(element, false);
2075 if (node == null || node.getParent() != parent) {
2076 final DefaultMutableTreeNode childNode = createChildNode(childDescr.get());
2077 if (addLoadingNode || getBuilder().isAlwaysShowPlus(childDescr.get())) {
2078 insertLoadingNode(childNode, true);
2081 addToUnbuilt(childNode);
2083 nodesToInsert.add(childNode);
2084 createMapping(element, childNode);
2086 processingDone.setDone();
2092 processingDone.doWhenDone(new Runnable() {
2094 result.setDone(nodesToInsert);
2101 protected DefaultMutableTreeNode createChildNode(final NodeDescriptor descriptor) {
2102 return new ElementNode(this, descriptor);
2105 protected boolean canYield() {
2106 return myCanYield && myYeildingUpdate.asBoolean();
2109 public long getClearOnHideDelay() {
2110 return myClearOnHideDelay > 0 ? myClearOnHideDelay : Registry.intValue("ide.tree.clearOnHideTime");
2113 public ActionCallback getInitialized() {
2114 return myInitialized;
2117 public ActionCallback getReady(Object requestor) {
2118 return myBusyObject.getReady(requestor);
2121 private void addToUpdating(DefaultMutableTreeNode node) {
2122 synchronized (myUpdatingChildren) {
2123 myUpdatingChildren.add(node);
2127 private void removeFromUpdating(DefaultMutableTreeNode node) {
2128 synchronized (myUpdatingChildren) {
2129 myUpdatingChildren.remove(node);
2133 public boolean isUpdatingNow(DefaultMutableTreeNode node) {
2134 synchronized (myUpdatingChildren) {
2135 return myUpdatingChildren.contains(node);
2139 boolean hasUpdatingNow() {
2140 synchronized (myUpdatingChildren) {
2141 return myUpdatingChildren.size() > 0;
2145 public Map getNodeActions() {
2146 return myNodeActions;
2149 public List<Object> getLoadedChildrenFor(Object element) {
2150 List<Object> result = new ArrayList<Object>();
2152 DefaultMutableTreeNode node = (DefaultMutableTreeNode)getNodeForElement(element, false);
2154 for (int i = 0; i < node.getChildCount(); i++) {
2155 TreeNode each = node.getChildAt(i);
2156 if (isLoadingNode(each)) continue;
2158 result.add(getElementFor(each));
2165 public boolean hasNodesToUpdate() {
2166 return getUpdater().hasNodesToUpdate();
2169 public List<Object> getExpandedElements() {
2170 List<Object> result = new ArrayList<Object>();
2171 Enumeration<TreePath> enumeration = myTree.getExpandedDescendants(getPathFor(getRootNode()));
2172 while (enumeration.hasMoreElements()) {
2173 TreePath each = enumeration.nextElement();
2174 Object eachElement = getElementFor(each.getLastPathComponent());
2175 if (eachElement != null) {
2176 result.add(eachElement);
2183 public ActionCallback cancelUpdate() {
2184 if (isReleased()) return new ActionCallback.Rejected();
2186 setCancelRequested(true);
2188 final ActionCallback done = new ActionCallback();
2190 invokeLaterIfNeeded(new Runnable() {
2197 if (myResettingToReadyNow.get()) {
2198 getReady(this).notify(done);
2199 } else if (isReady()) {
2203 if (isIdle() && hasPendingWork()) {
2207 getReady(this).notify(done);
2215 if (isEdt() || isPassthroughMode()) {
2222 private void setCancelRequested(boolean requested) {
2224 if (isUnitTestingMode()) {
2229 myCancelRequest.set(requested);
2231 catch (InterruptedException e) {
2239 private boolean attemptLock() throws InterruptedException {
2240 return myStateLock.tryLock(Registry.intValue("ide.tree.uiLockAttempt"), TimeUnit.MILLISECONDS);
2243 private void acquireLock() throws InterruptedException {
2247 private void releaseLock() {
2248 myStateLock.unlock();
2252 public ActionCallback batch(final Progressive progressive) {
2253 assertIsDispatchThread();
2255 EmptyProgressIndicator indicator = new EmptyProgressIndicator();
2256 final ActionCallback callback = new ActionCallback();
2258 myBatchIndicators.put(progressive, indicator);
2259 myBatchCallbacks.put(progressive, callback);
2262 progressive.run(indicator);
2263 } catch (ProcessCanceledException e) {
2264 resetToReadyNow().doWhenProcessed(new Runnable() {
2266 callback.setRejected();
2271 if (isReleased()) return new ActionCallback.Rejected();
2273 getReady(this).doWhenDone(new Runnable() {
2275 if (myBatchIndicators.containsKey(progressive)) {
2276 ProgressIndicator indicator = myBatchIndicators.remove(progressive);
2277 myBatchCallbacks.remove(progressive);
2279 if (indicator.isCanceled()) {
2280 callback.setRejected();
2285 callback.setRejected();
2297 public boolean isCancelProcessed() {
2298 Boolean processed = checkValue(new Computable<Boolean>() {
2300 public Boolean compute() {
2301 return Boolean.valueOf(myCancelRequest.get() || myResettingToReadyNow.get());
2305 return processed != null && processed.booleanValue();
2308 public boolean isToPaintSelection() {
2309 return isReady(true) || !mySelectionIsAdjusted;
2312 public boolean isReleaseRequested() {
2313 return myReleaseRequested;
2316 static class ElementNode extends DefaultMutableTreeNode {
2318 Set<Object> myElements = new HashSet<Object>();
2319 AbstractTreeUi myUi;
2321 ElementNode(AbstractTreeUi ui, NodeDescriptor descriptor) {
2327 public void insert(final MutableTreeNode newChild, final int childIndex) {
2328 super.insert(newChild, childIndex);
2329 final Object element = myUi.getElementFor(newChild);
2330 if (element != null) {
2331 myElements.add(element);
2336 public void remove(final int childIndex) {
2337 final TreeNode node = getChildAt(childIndex);
2338 super.remove(childIndex);
2339 final Object element = myUi.getElementFor(node);
2340 if (element != null) {
2341 myElements.remove(element);
2345 boolean isValidChild(Object childElement) {
2346 return myElements.contains(childElement);
2350 public String toString() {
2351 return String.valueOf(getUserObject());
2355 private boolean isUpdatingParent(DefaultMutableTreeNode kid) {
2356 return getUpdatingParent(kid) != null;
2359 private DefaultMutableTreeNode getUpdatingParent(DefaultMutableTreeNode kid) {
2360 DefaultMutableTreeNode eachParent = kid;
2361 while (eachParent != null) {
2362 if (isUpdatingNow(eachParent)) return eachParent;
2363 eachParent = (DefaultMutableTreeNode)eachParent.getParent();
2369 private boolean isLoadedInBackground(Object element) {
2370 return getLoadedInBackground(element) != null;
2373 private UpdateInfo getLoadedInBackground(Object element) {
2374 synchronized (myLoadedInBackground) {
2375 return myLoadedInBackground.get(element);
2379 private void addToLoadedInBackground(Object element, UpdateInfo info) {
2380 synchronized (myLoadedInBackground) {
2381 myLoadedInBackground.put(element, info);
2385 private void removeFromLoadedInBackground(final Object element) {
2386 synchronized (myLoadedInBackground) {
2387 myLoadedInBackground.remove(element);
2391 private boolean isLoadingInBackgroundNow() {
2392 synchronized (myLoadedInBackground) {
2393 return myLoadedInBackground.size() > 0;
2397 private boolean queueBackgroundUpdate(final UpdateInfo updateInfo, final DefaultMutableTreeNode node) {
2398 assertIsDispatchThread();
2400 final Object oldElementFromDescriptor = getElementFromDescriptor(updateInfo.getDescriptor());
2402 UpdateInfo loaded = getLoadedInBackground(oldElementFromDescriptor);
2403 if (loaded != null) {
2404 loaded.apply(updateInfo);
2408 addToLoadedInBackground(oldElementFromDescriptor, updateInfo);
2410 if (!isNodeBeingBuilt(node)) {
2411 LoadingNode loadingNode = new LoadingNode(getLoadingNodeText());
2412 myTreeModel.insertNodeInto(loadingNode, node, node.getChildCount());
2415 final Ref<LoadedChildren> children = new Ref<LoadedChildren>();
2416 final Ref<Object> elementFromDescriptor = new Ref<Object>();
2418 final DefaultMutableTreeNode[] nodeToProcessActions = new DefaultMutableTreeNode[1];
2420 final Runnable finalizeRunnable = new Runnable() {
2422 invokeLaterIfNeeded(new Runnable() {
2424 if (isReleased()) return;
2426 removeLoading(node, true);
2427 removeFromLoadedInBackground(elementFromDescriptor.get());
2428 removeFromLoadedInBackground(oldElementFromDescriptor);
2430 if (nodeToProcessActions[0] != null) {
2431 processNodeActionsIfReady(nodeToProcessActions[0]);
2439 Runnable buildRunnable = new Runnable() {
2441 if (updateInfo.getPass().isExpired()) {
2442 finalizeRunnable.run();
2446 if (!updateInfo.isDescriptorIsUpToDate()) {
2447 update(updateInfo.getDescriptor(), true);
2450 Object element = getElementFromDescriptor(updateInfo.getDescriptor());
2451 if (element == null) {
2452 removeFromLoadedInBackground(oldElementFromDescriptor);
2453 finalizeRunnable.run();
2457 elementFromDescriptor.set(element);
2459 Object[] loadedElements = getChildrenFor(getBuilder().getTreeStructureElement(updateInfo.getDescriptor()));
2461 final LoadedChildren loaded = new LoadedChildren(loadedElements);
2462 for (final Object each : loadedElements) {
2463 final NodeDescriptor eachChildDescriptor = getTreeStructure().createDescriptor(each, updateInfo.getDescriptor());
2464 execute(new Runnable() {
2466 loaded.putDescriptor(each, eachChildDescriptor, update(eachChildDescriptor, true).getResult());
2471 children.set(loaded);
2476 public String toString() {
2477 return "runnable=" + oldElementFromDescriptor;
2481 Runnable updateRunnable = new Runnable() {
2483 if (updateInfo.getPass().isExpired()) {
2484 finalizeRunnable.run();
2488 if (children.get() == null) {
2489 finalizeRunnable.run();
2493 if (isRerunNeeded(updateInfo.getPass())) {
2494 removeFromLoadedInBackground(elementFromDescriptor.get());
2495 getUpdater().addSubtreeToUpdate(updateInfo.getPass());
2499 removeFromLoadedInBackground(elementFromDescriptor.get());
2501 if (myUnbuiltNodes.contains(node)) {
2502 Pair<Boolean, LoadedChildren> unbuilt =
2503 processUnbuilt(node, updateInfo.getDescriptor(), updateInfo.getPass(), isExpanded(node, updateInfo.isWasExpanded()),
2505 if (unbuilt.getFirst()) {
2506 nodeToProcessActions[0] = node;
2511 updateNodeChildren(node, updateInfo.getPass(), children.get(), true, updateInfo.isCanSmartExpand(), updateInfo.isForceUpdate(),
2515 if (isRerunNeeded(updateInfo.getPass())) {
2516 getUpdater().addSubtreeToUpdate(updateInfo.getPass());
2520 Object element = elementFromDescriptor.get();
2522 if (element != null) {
2523 removeLoading(node, true);
2524 nodeToProcessActions[0] = node;
2528 queueToBackground(buildRunnable, updateRunnable, node).doWhenProcessed(finalizeRunnable).doWhenRejected(new Runnable() {
2530 updateInfo.getPass().expire();
2537 private boolean isExpanded(DefaultMutableTreeNode node, boolean isExpanded) {
2538 return isExpanded || myTree.isExpanded(getPathFor(node));
2541 private void removeLoading(DefaultMutableTreeNode parent, boolean removeFromUnbuilt) {
2542 for (int i = 0; i < parent.getChildCount(); i++) {
2543 TreeNode child = parent.getChildAt(i);
2544 if (removeIfLoading(child)) {
2549 if (removeFromUnbuilt) {
2550 removeFromUnbuilt(parent);
2553 if (parent == getRootNode() && !myTree.isRootVisible() && parent.getChildCount() == 0) {
2554 insertLoadingNode(parent, false);
2560 private void processNodeActionsIfReady(final DefaultMutableTreeNode node) {
2561 assertIsDispatchThread();
2563 if (isNodeBeingBuilt(node)) return;
2565 final Object o = node.getUserObject();
2566 if (!(o instanceof NodeDescriptor)) return;
2569 if (isYeildingNow()) {
2570 myPendingNodeActions.add(node);
2574 final Object element = getBuilder().getTreeStructureElement((NodeDescriptor)o);
2576 boolean childrenReady = !isLoadedInBackground(element);
2578 processActions(node, element, myNodeActions, childrenReady ? myNodeChildrenActions : null);
2579 if (childrenReady) {
2580 processActions(node, element, myNodeChildrenActions, null);
2583 if (!isUpdatingParent(node) && !isWorkerBusy()) {
2584 final UpdaterTreeState state = myUpdaterState;
2585 if (myNodeActions.size() == 0 && state != null && !state.isProcessingNow()) {
2586 if (canInitiateNewActivity()) {
2587 if (!state.restore(childrenReady ? node : null)) {
2588 setUpdaterState(state);
2598 private void processActions(DefaultMutableTreeNode node,
2600 final Map<Object, List<NodeAction>> nodeActions,
2601 @Nullable final Map<Object, List<NodeAction>> secondaryNodeAction) {
2602 final List<NodeAction> actions = nodeActions.get(element);
2603 if (actions != null) {
2604 nodeActions.remove(element);
2606 List<NodeAction> secondary = secondaryNodeAction != null ? secondaryNodeAction.get(element) : null;
2607 for (NodeAction each : actions) {
2608 if (secondary != null && secondary.contains(each)) {
2609 secondary.remove(each);
2617 private boolean canSmartExpand(DefaultMutableTreeNode node, boolean canSmartExpand) {
2618 if (!getBuilder().isSmartExpand()) return false;
2620 boolean smartExpand = !myNotForSmartExpand.contains(node) && canSmartExpand;
2621 return smartExpand ? validateAutoExpand(smartExpand, getElementFor(node)) : false;
2624 private void processSmartExpand(final DefaultMutableTreeNode node, final boolean canSmartExpand, boolean forced) {
2625 if (!getBuilder().isSmartExpand()) return;
2627 boolean can = canSmartExpand(node, canSmartExpand);
2629 if (!can && !forced) return;
2631 if (isNodeBeingBuilt(node) && !forced) {
2632 addNodeAction(getElementFor(node), new NodeAction() {
2633 public void onReady(DefaultMutableTreeNode node) {
2634 processSmartExpand(node, canSmartExpand, true);
2639 TreeNode child = getChildForSmartExpand(node);
2640 if (child != null) {
2641 final TreePath childPath = new TreePath(node.getPath()).pathByAddingChild(child);
2642 processInnerChange(new Runnable() {
2644 myTree.expandPath(childPath);
2652 private TreeNode getChildForSmartExpand(DefaultMutableTreeNode node) {
2653 int realChildCount = 0;
2654 TreeNode nodeToExpand = null;
2656 for (int i = 0; i < node.getChildCount(); i++) {
2657 TreeNode eachChild = node.getChildAt(i);
2659 if (!isLoadingNode(eachChild)) {
2661 if (nodeToExpand == null) {
2662 nodeToExpand = eachChild;
2666 if (realChildCount > 1) {
2667 nodeToExpand = null;
2672 return nodeToExpand;
2675 public boolean isLoadingChildrenFor(final Object nodeObject) {
2676 if (!(nodeObject instanceof DefaultMutableTreeNode)) return false;
2678 DefaultMutableTreeNode node = (DefaultMutableTreeNode)nodeObject;
2680 int loadingNodes = 0;
2681 for (int i = 0; i < Math.min(node.getChildCount(), 2); i++) {
2682 TreeNode child = node.getChildAt(i);
2683 if (isLoadingNode(child)) {
2687 return loadingNodes > 0 && loadingNodes == node.getChildCount();
2690 private boolean isParentLoading(Object nodeObject) {
2691 return getParentLoading(nodeObject) != null;
2694 private DefaultMutableTreeNode getParentLoading(Object nodeObject) {
2695 if (!(nodeObject instanceof DefaultMutableTreeNode)) return null;
2697 DefaultMutableTreeNode node = (DefaultMutableTreeNode)nodeObject;
2699 TreeNode eachParent = node.getParent();
2701 while (eachParent != null) {
2702 eachParent = eachParent.getParent();
2703 if (eachParent instanceof DefaultMutableTreeNode) {
2704 final Object eachElement = getElementFor((DefaultMutableTreeNode)eachParent);
2705 if (isLoadedInBackground(eachElement)) return (DefaultMutableTreeNode)eachParent;
2712 protected String getLoadingNodeText() {
2713 return IdeBundle.message("progress.searching");
2716 private ActionCallback processExistingNode(final DefaultMutableTreeNode childNode,
2717 final NodeDescriptor childDescriptor,
2718 final DefaultMutableTreeNode parentNode,
2719 final MutualMap<Object, Integer> elementToIndexMap,
2720 final TreeUpdatePass pass,
2721 final boolean canSmartExpand,
2722 final boolean forceUpdate,
2723 LoadedChildren parentPreloadedChildren) {
2725 final ActionCallback result = new ActionCallback();
2727 if (pass.isExpired()) {
2728 return new ActionCallback.Rejected();
2731 final Ref<NodeDescriptor> childDesc = new Ref<NodeDescriptor>(childDescriptor);
2733 if (childDesc.get() == null) {
2735 return new ActionCallback.Rejected();
2737 final Object oldElement = getElementFromDescriptor(childDesc.get());
2738 if (oldElement == null) {
2740 return new ActionCallback.Rejected();
2743 AsyncResult<Boolean> update = new AsyncResult<Boolean>();
2744 if (parentPreloadedChildren != null && parentPreloadedChildren.getDescriptor(oldElement) != null) {
2745 update.setDone(parentPreloadedChildren.isUpdated(oldElement));
2748 update = update(childDesc.get(), false);
2751 update.doWhenDone(new AsyncResult.Handler<Boolean>() {
2752 public void run(Boolean isChanged) {
2753 final Ref<Boolean> changes = new Ref<Boolean>(isChanged);
2755 final Ref<Boolean> forceRemapping = new Ref<Boolean>(false);
2756 final Ref<Object> newElement = new Ref<Object>(getElementFromDescriptor(childDesc.get()));
2758 final Integer index =
2759 newElement.get() != null ? elementToIndexMap.getValue(getBuilder().getTreeStructureElement(childDesc.get())) : null;
2760 final AsyncResult<Boolean> updateIndexDone = new AsyncResult<Boolean>();
2761 final ActionCallback indexReady = new ActionCallback();
2762 if (index != null) {
2763 final Object elementFromMap = elementToIndexMap.getKey(index);
2764 if (elementFromMap != newElement.get() && elementFromMap.equals(newElement.get())) {
2765 if (isInStructure(elementFromMap) && isInStructure(newElement.get())) {
2766 if (parentNode.getUserObject() instanceof NodeDescriptor) {
2767 final NodeDescriptor parentDescriptor = getDescriptorFrom(parentNode);
2768 childDesc.set(getTreeStructure().createDescriptor(elementFromMap, parentDescriptor));
2769 childNode.setUserObject(childDesc.get());
2770 newElement.set(elementFromMap);
2771 forceRemapping.set(true);
2772 update(childDesc.get(), false).doWhenDone(new AsyncResult.Handler<Boolean>() {
2773 public void run(Boolean isChanged) {
2774 changes.set(isChanged);
2775 updateIndexDone.setDone(isChanged);
2781 updateIndexDone.setDone(changes.get());
2785 updateIndexDone.setDone(changes.get());
2788 updateIndexDone.doWhenDone(new Runnable() {
2790 if (childDesc.get().getIndex() != index.intValue()) {
2793 childDesc.get().setIndex(index.intValue());
2794 indexReady.setDone();
2799 updateIndexDone.setDone();
2802 updateIndexDone.doWhenDone(new Runnable() {
2804 if (index != null && changes.get()) {
2805 updateNodeImageAndPosition(childNode, false);
2807 if (!oldElement.equals(newElement.get()) | forceRemapping.get()) {
2808 removeMapping(oldElement, childNode, newElement.get());
2809 if (newElement.get() != null) {
2810 createMapping(newElement.get(), childNode);
2812 NodeDescriptor parentDescriptor = getDescriptorFrom(parentNode);
2813 if (parentDescriptor != null) {
2814 parentDescriptor.setChildrenSortingStamp(-1);
2818 if (index == null) {
2819 int selectedIndex = -1;
2820 if (TreeBuilderUtil.isNodeOrChildSelected(myTree, childNode)) {
2821 selectedIndex = parentNode.getIndex(childNode);
2824 if (childNode.getParent() instanceof DefaultMutableTreeNode) {
2825 final DefaultMutableTreeNode parent = (DefaultMutableTreeNode)childNode.getParent();
2826 if (myTree.isExpanded(new TreePath(parent.getPath()))) {
2827 if (parent.getChildCount() == 1 && parent.getChildAt(0) == childNode) {
2828 insertLoadingNode(parent, false);
2833 Object disposedElement = getElementFor(childNode);
2835 removeNodeFromParent(childNode, selectedIndex >= 0);
2836 disposeNode(childNode);
2838 adjustSelectionOnChildRemove(parentNode, selectedIndex, disposedElement);
2841 elementToIndexMap.remove(getBuilder().getTreeStructureElement(childDesc.get()));
2842 updateNodeChildren(childNode, pass, null, false, canSmartExpand, forceUpdate, true);
2845 if (parentNode.equals(getRootNode())) {
2846 myTreeModel.nodeChanged(getRootNode());
2859 private void adjustSelectionOnChildRemove(DefaultMutableTreeNode parentNode, int selectedIndex, Object disposedElement) {
2860 DefaultMutableTreeNode node = getNodeForElement(disposedElement, false);
2861 if (node != null && isValidForSelectionAdjusting(node)) {
2862 Object newElement = getElementFor(node);
2863 addSelectionPath(getPathFor(node), true, getExpiredElementCondition(newElement), disposedElement);
2868 if (selectedIndex >= 0) {
2869 if (parentNode.getChildCount() > 0) {
2870 if (parentNode.getChildCount() > selectedIndex) {
2871 TreeNode newChildNode = parentNode.getChildAt(selectedIndex);
2872 if (isValidForSelectionAdjusting(newChildNode)) {
2873 addSelectionPath(new TreePath(myTreeModel.getPathToRoot(newChildNode)), true, getExpiredElementCondition(disposedElement),
2878 TreeNode newChild = parentNode.getChildAt(parentNode.getChildCount() - 1);
2879 if (isValidForSelectionAdjusting(newChild)) {
2880 addSelectionPath(new TreePath(myTreeModel.getPathToRoot(newChild)), true, getExpiredElementCondition(disposedElement),