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 Set<Runnable> myUserRunnables = new HashSet<Runnable>();
183 private Alarm myMaybeReady = new Alarm();
184 private Runnable myMaybeReadyRunnable = new Runnable() {
191 protected void init(AbstractTreeBuilder builder,
193 DefaultTreeModel treeModel,
194 AbstractTreeStructure treeStructure,
195 @Nullable Comparator<NodeDescriptor> comparator,
196 boolean updateIfInactive) {
199 myTreeModel = treeModel;
200 addModelListenerToDianoseAccessOutsideEdt();
201 TREE_NODE_WRAPPER = getBuilder().createSearchingTreeNodeWrapper();
202 myTree.setModel(myTreeModel);
203 setRootNode((DefaultMutableTreeNode)treeModel.getRoot());
204 setTreeStructure(treeStructure);
205 myNodeDescriptorComparator = comparator;
206 myUpdateIfInactive = updateIfInactive;
208 UIUtil.invokeLaterIfNeeded(new Runnable() {
210 if (!wasRootNodeInitialized()) {
211 if (myRootNode.getChildCount() == 0) {
212 insertLoadingNode(myRootNode, true);
218 myExpansionListener = new MyExpansionListener();
219 myTree.addTreeExpansionListener(myExpansionListener);
221 mySelectionListener = new MySelectionListener();
222 myTree.addTreeSelectionListener(mySelectionListener);
224 setUpdater(getBuilder().createUpdater());
225 myProgress = getBuilder().createProgressIndicator();
226 Disposer.register(getBuilder(), getUpdater());
228 final UiNotifyConnector uiNotify = new UiNotifyConnector(tree, new Activatable() {
229 public void showNotify() {
231 myWasEverShown = true;
232 if (canInitiateNewActivity()) {
237 public void hideNotify() {
239 if (canInitiateNewActivity()) {
244 Disposer.register(getBuilder(), uiNotify);
246 myTree.addFocusListener(myFocusListener);
250 boolean isNodeActionsPending() {
251 return !myNodeActions.isEmpty() || !myNodeChildrenActions.isEmpty();
254 private void clearNodeActions() {
255 myNodeActions.clear();
256 myNodeChildrenActions.clear();
259 private void maybeSetBusyAndScheduleWaiterForReady(boolean forcedBusy) {
260 if (!myShowBusyIndicator.asBoolean() || !canYield()) return;
262 if (myTree instanceof com.intellij.ui.treeStructure.Tree) {
263 final com.intellij.ui.treeStructure.Tree tree = (Tree)myTree;
264 final boolean isBusy = !isReady(true) || forcedBusy;
265 if (isBusy && tree.isShowing()) {
266 tree.setPaintBusy(true);
267 myBusyAlarm.cancelAllRequests();
268 myBusyAlarm.addRequest(myWaiterForReady, myWaitForReadyTime.asInteger());
271 tree.setPaintBusy(false);
276 private void setHoldSize(boolean holdSize) {
277 if (myTree instanceof com.intellij.ui.treeStructure.Tree) {
278 final com.intellij.ui.treeStructure.Tree tree = (Tree)myTree;
279 tree.setHoldSize(holdSize);
283 private void cleanUpAll() {
284 final long now = System.currentTimeMillis();
285 final AbstractTreeUi[] uis = ourUi2Countdown.keySet().toArray(new AbstractTreeUi[ourUi2Countdown.size()]);
286 for (AbstractTreeUi eachUi : uis) {
287 if (eachUi == null) continue;
288 final Long timeToCleanup = ourUi2Countdown.get(eachUi);
289 if (timeToCleanup == null) continue;
290 if (now >= timeToCleanup.longValue()) {
291 ourUi2Countdown.remove(eachUi);
292 Runnable runnable = new Runnable() {
294 if (!canInitiateNewActivity()) return;
296 myCleanupTask = null;
297 getBuilder().cleanUp();
300 if (isPassthroughMode()) {
304 UIUtil.invokeLaterIfNeeded(runnable);
310 protected void doCleanUp() {
311 Runnable cleanup = new Runnable() {
313 if (canInitiateNewActivity()) {
319 if (isPassthroughMode()) {
323 UIUtil.invokeLaterIfNeeded(cleanup);
327 private ActionCallback invokeLaterIfNeeded(@NotNull final Runnable runnable) {
328 final ActionCallback result = new ActionCallback();
330 Runnable actual = new Runnable() {
337 if (isPassthroughMode() || (!isEdt() && (!isTreeShowing() && !myWasEverShown))) {
341 UIUtil.invokeLaterIfNeeded(actual);
347 public void activate(boolean byShowing) {
348 cancelCurrentCleanupTask();
350 myCanProcessDeferredSelections = true;
351 ourUi2Countdown.remove(this);
353 if (!myWasEverShown || myUpdateFromRootRequested || myUpdateIfInactive) {
354 getBuilder().updateFromRoot();
357 getUpdater().showNotify();
359 myWasEverShown |= byShowing;
362 private void cancelCurrentCleanupTask() {
363 if (myCleanupTask != null) {
364 myCleanupTask.cancel();
365 myCleanupTask = null;
369 public void deactivate() {
370 getUpdater().hideNotify();
371 myBusyAlarm.cancelAllRequests();
373 if (!myWasEverShown) return;
377 myUpdateFromRootRequested = true;
380 if (getClearOnHideDelay() >= 0) {
381 ourUi2Countdown.put(this, System.currentTimeMillis() + getClearOnHideDelay());
386 private void sheduleCleanUpAll() {
387 cancelCurrentCleanupTask();
389 myCleanupTask = SimpleTimer.getInstance().setUp(new Runnable() {
393 }, getClearOnHideDelay());
396 public void requestRelease() {
397 myReleaseRequested = true;
398 cancelUpdate().doWhenDone(new Runnable() {
405 private void releaseNow() {
409 myTree.removeTreeExpansionListener(myExpansionListener);
410 myTree.removeTreeSelectionListener(mySelectionListener);
411 myTree.removeFocusListener(myFocusListener);
413 disposeNode(getRootNode());
414 myElementToNodeMap.clear();
415 getUpdater().cancelAllRequests();
416 if (myWorker != null) {
417 myWorker.dispose(true);
420 TREE_NODE_WRAPPER.setValue(null);
421 if (myProgress != null) {
425 cancelCurrentCleanupTask();
430 myTreeStructure = null;
431 myBuilder.releaseUi();
436 myDeferredSelections.clear();
437 myDeferredExpansions.clear();
438 myYeildingDoneRunnables.clear();
440 catch (InterruptedException e) {
447 public boolean isReleased() {
448 return myBuilder == null;
451 protected void doExpandNodeChildren(final DefaultMutableTreeNode node) {
452 if (!myUnbuiltNodes.contains(node)) return;
453 if (isLoadedInBackground(getElementFor(node))) return;
455 getTreeStructure().commit();
456 addSubtreeToUpdate(node);
457 getUpdater().performUpdate();
460 public final AbstractTreeStructure getTreeStructure() {
461 return myTreeStructure;
464 public final JTree getTree() {
469 private NodeDescriptor getDescriptorFrom(DefaultMutableTreeNode node) {
470 return (NodeDescriptor)node.getUserObject();
474 public final DefaultMutableTreeNode getNodeForElement(Object element, final boolean validateAgainstStructure) {
475 DefaultMutableTreeNode result = null;
476 if (validateAgainstStructure) {
479 final DefaultMutableTreeNode node = findNode(element, index);
480 if (node == null) break;
482 if (isNodeValidForElement(element, node)) {
491 result = getFirstNode(element);
495 if (result != null && !isNodeInStructure(result)) {
503 private boolean isNodeInStructure(DefaultMutableTreeNode node) {
504 return TreeUtil.isAncestor(getRootNode(), node) && getRootNode() == myTreeModel.getRoot();
507 private boolean isNodeValidForElement(final Object element, final DefaultMutableTreeNode node) {
508 return isSameHierarchy(element, node) || isValidChildOfParent(element, node);
511 private boolean isValidChildOfParent(final Object element, final DefaultMutableTreeNode node) {
512 final DefaultMutableTreeNode parent = (DefaultMutableTreeNode)node.getParent();
513 final Object parentElement = getElementFor(parent);
514 if (!isInStructure(parentElement)) return false;
516 if (parent instanceof ElementNode) {
517 return ((ElementNode)parent).isValidChild(element);
520 for (int i = 0; i < parent.getChildCount(); i++) {
521 final TreeNode child = parent.getChildAt(i);
522 final Object eachElement = getElementFor(child);
523 if (element.equals(eachElement)) return true;
530 private boolean isSameHierarchy(Object eachParent, DefaultMutableTreeNode eachParentNode) {
531 boolean valid = true;
533 if (eachParent == null) {
534 valid = eachParentNode == null;
538 if (!eachParent.equals(getElementFor(eachParentNode))) {
543 eachParent = getTreeStructure().getParentElement(eachParent);
544 eachParentNode = (DefaultMutableTreeNode)eachParentNode.getParent();
549 public final DefaultMutableTreeNode getNodeForPath(Object[] path) {
550 DefaultMutableTreeNode node = null;
551 for (final Object pathElement : path) {
552 node = node == null ? getFirstNode(pathElement) : findNodeForChildElement(node, pathElement);
560 public final void buildNodeForElement(Object element) {
561 getUpdater().performUpdate();
562 DefaultMutableTreeNode node = getNodeForElement(element, false);
564 final java.util.List<Object> elements = new ArrayList<Object>();
566 element = getTreeStructure().getParentElement(element);
567 if (element == null) {
570 elements.add(0, element);
573 for (final Object element1 : elements) {
574 node = getNodeForElement(element1, false);
582 public final void buildNodeForPath(Object[] path) {
583 getUpdater().performUpdate();
584 DefaultMutableTreeNode node = null;
585 for (final Object pathElement : path) {
586 node = node == null ? getFirstNode(pathElement) : findNodeForChildElement(node, pathElement);
587 if (node != null && node != path[path.length - 1]) {
593 public final void setNodeDescriptorComparator(Comparator<NodeDescriptor> nodeDescriptorComparator) {
594 myNodeDescriptorComparator = nodeDescriptorComparator;
595 myLastComparatorStamp = -1;
596 getBuilder().queueUpdateFrom(getTreeStructure().getRootElement(), true);
599 protected AbstractTreeBuilder getBuilder() {
603 protected final void initRootNode() {
604 if (myUpdateIfInactive) {
608 myUpdateFromRootRequested = true;
612 private boolean initRootNodeNowIfNeeded(final TreeUpdatePass pass) {
613 boolean wasCleanedUp = false;
614 if (myRootNodeWasQueuedToInitialize) {
615 Object root = getTreeStructure().getRootElement();
616 assert root != null : "Root element cannot be null";
618 Object currentRoot = getElementFor(myRootNode);
620 if (Comparing.equal(root, currentRoot)) return false;
622 Object rootAgain = getTreeStructure().getRootElement();
623 if (root != rootAgain && !root.equals(rootAgain)) {
624 assert false : "getRootElement() if called twice must return either root1 == root2 or root1.equals(root2)";
631 if (myRootNodeWasQueuedToInitialize) return wasCleanedUp;
633 myRootNodeWasQueuedToInitialize = true;
635 final Object rootElement = getTreeStructure().getRootElement();
636 addNodeAction(rootElement, new NodeAction() {
637 public void onReady(final DefaultMutableTreeNode node) {
638 processDeferredActions();
643 final Ref<NodeDescriptor> rootDescriptor = new Ref<NodeDescriptor>(null);
644 final boolean bgLoading = getTreeStructure().isToBuildChildrenInBackground(rootElement);
646 Runnable build = new Runnable() {
648 rootDescriptor.set(getTreeStructure().createDescriptor(rootElement, null));
649 getRootNode().setUserObject(rootDescriptor.get());
650 update(rootDescriptor.get(), true);
655 Runnable update = new Runnable() {
657 if (getElementFromDescriptor(rootDescriptor.get()) != null) {
658 createMapping(getElementFromDescriptor(rootDescriptor.get()), getRootNode());
662 insertLoadingNode(getRootNode(), true);
664 boolean willUpdate = false;
665 if (isAutoExpand(rootDescriptor.get())) {
666 willUpdate = myUnbuiltNodes.contains(getRootNode());
667 expand(getRootNode(), true);
670 updateNodeChildren(getRootNode(), pass, null, false, false, false, true);
672 if (getRootNode().getChildCount() == 0) {
673 myTreeModel.nodeChanged(getRootNode());
679 queueToBackground(build, update, rootDescriptor).doWhenProcessed(new Runnable() {
682 myRootNodeInitialized = true;
683 processNodeActionsIfReady(myRootNode);
690 myRootNodeInitialized = true;
691 processNodeActionsIfReady(myRootNode);
697 private boolean isAutoExpand(NodeDescriptor descriptor) {
698 return isAutoExpand(descriptor, true);
701 private boolean isAutoExpand(NodeDescriptor descriptor, boolean validate) {
702 if (descriptor == null) return false;
704 boolean autoExpand = getBuilder().isAutoExpandNode(descriptor);
706 Object element = getElementFromDescriptor(descriptor);
708 autoExpand = validateAutoExpand(autoExpand, element);
711 if (!autoExpand && !myTree.isRootVisible()) {
712 if (element != null && element.equals(getTreeStructure().getRootElement())) return true;
718 private boolean validateAutoExpand(boolean autoExpand, Object element) {
720 int distance = getDistanceToAutoExpandRoot(element);
722 myAutoExpandRoots.add(element);
725 if (distance >= myAutoExpandDepth.asInteger() - 1) {
731 DefaultMutableTreeNode node = getNodeForElement(element, false);
732 if (isInVisibleAutoExpandChain(node)) {
743 private boolean isInVisibleAutoExpandChain(DefaultMutableTreeNode child) {
744 TreeNode eachParent = child;
745 while (eachParent != null) {
747 if (myRootNode == eachParent) return true;
749 NodeDescriptor eachDescriptor = getDescriptorFrom((DefaultMutableTreeNode)eachParent);
750 if (!isAutoExpand(eachDescriptor, false)) {
751 TreePath path = getPathFor(eachParent);
752 if (myWillBeExpaned.contains(path.getLastPathComponent()) || (myTree.isExpanded(path) && myTree.isVisible(path))) {
759 eachParent = eachParent.getParent();
765 private int getDistanceToAutoExpandRoot(Object element) {
768 Object eachParent = element;
769 while (eachParent != null) {
770 if (myAutoExpandRoots.contains(eachParent)) break;
771 eachParent = getTreeStructure().getParentElement(eachParent);
775 return eachParent != null ? distance : -1;
778 private boolean isAutoExpand(DefaultMutableTreeNode node) {
779 return isAutoExpand(getDescriptorFrom(node));
782 private AsyncResult<Boolean> update(final NodeDescriptor nodeDescriptor, boolean now) {
783 final AsyncResult<Boolean> result = new AsyncResult<Boolean>();
785 if (now || isPassthroughMode()) {
786 return new AsyncResult<Boolean>().setDone(_update(nodeDescriptor));
789 Object element = getElementFromDescriptor(nodeDescriptor);
790 boolean bgLoading = getTreeStructure().isToBuildChildrenInBackground(element);
792 boolean edt = isEdt();
795 final Ref<Boolean> changes = new Ref<Boolean>(false);
796 queueToBackground(new Runnable() {
798 changes.set(_update(nodeDescriptor));
802 result.setDone(changes.get());
807 result.setDone(_update(nodeDescriptor));
811 if (edt || !myWasEverShown) {
812 result.setDone(_update(nodeDescriptor));
815 UIUtil.invokeLaterIfNeeded(new Runnable() {
817 execute(new Runnable() {
819 result.setDone(_update(nodeDescriptor));
827 result.doWhenDone(new AsyncResult.Handler<Boolean>() {
828 public void run(Boolean changes) {
830 final long updateStamp = nodeDescriptor.getUpdateCount();
831 UIUtil.invokeLaterIfNeeded(new Runnable() {
833 Object element = nodeDescriptor.getElement();
834 DefaultMutableTreeNode node = getNodeForElement(element, false);
836 TreePath path = getPathFor(node);
837 if (path != null && myTree.isVisible(path)) {
838 updateNodeImageAndPosition(node, false);
851 private boolean _update(final NodeDescriptor nodeDescriptor) {
853 final Ref<Boolean> update = new Ref<Boolean>();
856 execute(new Runnable() {
858 nodeDescriptor.setUpdateCount(nodeDescriptor.getUpdateCount() + 1);
859 update.set(getBuilder().updateNodeDescriptor(nodeDescriptor));
863 catch (InterruptedException e) {
864 throw new ProcessCanceledException();
865 } catch (ProcessCanceledException e) {
872 catch (IndexNotReadyException e) {
873 warnOnIndexNotReady();
878 private void assertIsDispatchThread() {
879 if (isPassthroughMode()) return;
881 if ((isTreeShowing() || myWasEverShown) && !isEdt()) {
882 LOG.error("Must be in event-dispatch thread");
886 private boolean isEdt() {
887 return SwingUtilities.isEventDispatchThread();
890 private boolean isTreeShowing() {
894 private void assertNotDispatchThread() {
895 if (isPassthroughMode()) return;
898 LOG.error("Must not be in event-dispatch thread");
902 private void processDeferredActions() {
903 processDeferredActions(myDeferredSelections);
904 processDeferredActions(myDeferredExpansions);
907 private void processDeferredActions(Set<Runnable> actions) {
908 final Runnable[] runnables = actions.toArray(new Runnable[actions.size()]);
910 for (Runnable runnable : runnables) {
915 //todo: to make real callback
916 public ActionCallback queueUpdate(Object element) {
918 AbstractTreeUpdater updater = getUpdater();
919 if (updater == null) {
920 return new ActionCallback.Rejected();
923 final ActionCallback result = new ActionCallback();
924 DefaultMutableTreeNode node = getNodeForElement(element, false);
926 addSubtreeToUpdate(node);
929 addSubtreeToUpdate(getRootNode());
932 updater.runAfterUpdate(new Runnable() {
939 catch (ProcessCanceledException e) {
940 return new ActionCallback.Rejected();
944 public void doUpdateFromRoot() {
945 updateSubtree(getRootNode(), false);
948 public ActionCallback doUpdateFromRootCB() {
949 final ActionCallback cb = new ActionCallback();
950 getUpdater().runAfterUpdate(new Runnable() {
955 updateSubtree(getRootNode(), false);
959 public final void updateSubtree(DefaultMutableTreeNode node, boolean canSmartExpand) {
960 updateSubtree(new TreeUpdatePass(node), canSmartExpand);
963 public final void updateSubtree(TreeUpdatePass pass, boolean canSmartExpand) {
964 if (getUpdater() != null) {
965 getUpdater().addSubtreeToUpdate(pass);
968 updateSubtreeNow(pass, canSmartExpand);
972 final void updateSubtreeNow(TreeUpdatePass pass, boolean canSmartExpand) {
975 maybeSetBusyAndScheduleWaiterForReady(true);
978 boolean consumed = initRootNodeNowIfNeeded(pass);
979 if (consumed) return;
981 final DefaultMutableTreeNode node = pass.getNode();
983 if (!(node.getUserObject() instanceof NodeDescriptor)) return;
985 setUpdaterState(new UpdaterTreeState(this)).beforeSubtreeUpdate();
987 boolean forceUpdate = true;
988 TreePath path = getPathFor(node);
989 boolean invisible = !myTree.isExpanded(path) && (path.getParentPath() == null || !myTree.isExpanded(path.getParentPath()));
991 if (invisible && myUnbuiltNodes.contains(node)) {
995 updateNodeChildren(node, pass, null, false, canSmartExpand, forceUpdate, false);
998 private boolean isToBuildInBackground(NodeDescriptor descriptor) {
999 return getTreeStructure().isToBuildChildrenInBackground(getBuilder().getTreeStructureElement(descriptor));
1003 UpdaterTreeState setUpdaterState(UpdaterTreeState state) {
1004 if (myUpdaterState != null && myUpdaterState.equals(state)) return state;
1006 final UpdaterTreeState oldState = myUpdaterState;
1007 if (oldState == null) {
1008 myUpdaterState = state;
1012 oldState.addAll(state);
1017 protected void doUpdateNode(final DefaultMutableTreeNode node) {
1018 if (!(node.getUserObject() instanceof NodeDescriptor)) return;
1019 final NodeDescriptor descriptor = getDescriptorFrom(node);
1020 final Object prevElement = getElementFromDescriptor(descriptor);
1021 if (prevElement == null) return;
1022 update(descriptor, false).doWhenDone(new AsyncResult.Handler<Boolean>() {
1023 public void run(Boolean changes) {
1024 if (!isValid(descriptor)) {
1025 if (isInStructure(prevElement)) {
1026 getUpdater().addSubtreeToUpdateByElement(getTreeStructure().getParentElement(prevElement));
1031 updateNodeImageAndPosition(node, true);
1037 public Object getElementFromDescriptor(NodeDescriptor descriptor) {
1038 return getBuilder().getTreeStructureElement(descriptor);
1041 private void updateNodeChildren(final DefaultMutableTreeNode node,
1042 final TreeUpdatePass pass,
1043 @Nullable final LoadedChildren loadedChildren,
1044 final boolean forcedNow,
1045 final boolean toSmartExpand,
1046 final boolean forceUpdate,
1047 final boolean descriptorIsUpToDate) {
1049 removeFromCancelled(node);
1051 execute(new Runnable() {
1054 getTreeStructure().commit();
1057 final NodeDescriptor descriptor = getDescriptorFrom(node);
1058 if (descriptor == null) {
1059 removeFromUnbuilt(node);
1060 removeLoading(node, true);
1064 final boolean wasExpanded = myTree.isExpanded(new TreePath(node.getPath())) || isAutoExpand(node);
1065 final boolean wasLeaf = node.getChildCount() == 0;
1068 boolean bgBuild = isToBuildInBackground(descriptor);
1069 boolean notRequiredToUpdateChildren = !forcedNow && !wasExpanded;
1071 if (notRequiredToUpdateChildren && forceUpdate && !wasExpanded) {
1072 boolean alwaysPlus = getBuilder().isAlwaysShowPlus(descriptor);
1073 if (alwaysPlus && wasLeaf) {
1074 notRequiredToUpdateChildren = false;
1077 notRequiredToUpdateChildren = alwaysPlus;
1081 final Ref<LoadedChildren> preloaded = new Ref<LoadedChildren>(loadedChildren);
1082 boolean descriptorWasUpdated = descriptorIsUpToDate;
1084 if (notRequiredToUpdateChildren) {
1085 if (myUnbuiltNodes.contains(node) && node.getChildCount() == 0) {
1086 insertLoadingNode(node, true);
1093 if (myUnbuiltNodes.contains(node)) {
1094 if (!descriptorWasUpdated) {
1095 update(descriptor, true);
1096 descriptorWasUpdated = true;
1099 if (processAlwaysLeaf(node)) return;
1101 Pair<Boolean, LoadedChildren> unbuilt = processUnbuilt(node, descriptor, pass, wasExpanded, null);
1103 if (unbuilt.getFirst()) return;
1104 preloaded.set(unbuilt.getSecond());
1110 final boolean childForceUpdate = isChildNodeForceUpdate(node, forceUpdate, wasExpanded);
1112 if (!forcedNow && isToBuildInBackground(descriptor)) {
1113 if (processAlwaysLeaf(node)) return;
1115 queueBackgroundUpdate(
1116 new UpdateInfo(descriptor, pass, canSmartExpand(node, toSmartExpand), wasExpanded, childForceUpdate, descriptorWasUpdated), node);
1120 if (!descriptorWasUpdated) {
1121 update(descriptor, false).doWhenDone(new Runnable() {
1123 if (processAlwaysLeaf(node)) return;
1124 updateNodeChildrenNow(node, pass, preloaded.get(), toSmartExpand, wasExpanded, wasLeaf, childForceUpdate);
1129 if (processAlwaysLeaf(node)) return;
1131 updateNodeChildrenNow(node, pass, preloaded.get(), toSmartExpand, wasExpanded, wasLeaf, childForceUpdate);
1136 if (isReleased()) return;
1138 processNodeActionsIfReady(node);
1144 private boolean processAlwaysLeaf(DefaultMutableTreeNode node) {
1145 Object element = getElementFor(node);
1146 NodeDescriptor desc = getDescriptorFrom(node);
1148 if (desc == null) return false;
1150 if (getTreeStructure().isAlwaysLeaf(element)) {
1151 removeFromUnbuilt(node);
1152 removeLoading(node, true);
1154 if (node.getChildCount() > 0) {
1155 final TreeNode[] children = new TreeNode[node.getChildCount()];
1156 for (int i = 0; i < node.getChildCount(); i++) {
1157 children[i] = node.getChildAt(i);
1160 if (isSelectionInside(node)) {
1161 addSelectionPath(getPathFor(node), true, Condition.TRUE, null);
1164 processInnerChange(new Runnable() {
1166 for (TreeNode each : children) {
1167 removeNodeFromParent((MutableTreeNode)each, true);
1168 disposeNode((DefaultMutableTreeNode)each);
1174 removeFromUnbuilt(node);
1175 desc.setWasDeclaredAlwaysLeaf(true);
1176 processNodeActionsIfReady(node);
1180 boolean wasLeaf = desc.isWasDeclaredAlwaysLeaf();
1181 desc.setWasDeclaredAlwaysLeaf(false);
1184 insertLoadingNode(node, true);
1191 private boolean isChildNodeForceUpdate(DefaultMutableTreeNode node, boolean parentForceUpdate, boolean parentExpanded) {
1192 TreePath path = getPathFor(node);
1193 return parentForceUpdate && (parentExpanded || myTree.isExpanded(path));
1196 private void updateNodeChildrenNow(final DefaultMutableTreeNode node,
1197 final TreeUpdatePass pass,
1198 final LoadedChildren preloadedChildren,
1199 final boolean toSmartExpand,
1200 final boolean wasExpanded,
1201 final boolean wasLeaf,
1202 final boolean forceUpdate) {
1203 if (!canInitiateNewActivity()) {
1204 throw new ProcessCanceledException();
1207 final NodeDescriptor descriptor = getDescriptorFrom(node);
1209 final MutualMap<Object, Integer> elementToIndexMap = loadElementsFromStructure(descriptor, preloadedChildren);
1210 final LoadedChildren loadedChildren =
1211 preloadedChildren != null ? preloadedChildren : new LoadedChildren(elementToIndexMap.getKeys().toArray());
1214 addToUpdating(node);
1215 pass.setCurrentNode(node);
1217 final boolean canSmartExpand = canSmartExpand(node, toSmartExpand);
1219 removeFromUnbuilt(node);
1221 processExistingNodes(node, elementToIndexMap, pass, canSmartExpand(node, toSmartExpand), forceUpdate, wasExpanded, preloadedChildren)
1222 .doWhenDone(new Runnable() {
1224 if (isDisposed(node)) {
1225 removeFromUpdating(node);
1229 removeLoading(node, false);
1231 final boolean expanded = isExpanded(node, wasExpanded);
1234 myWillBeExpaned.add(node);
1237 myWillBeExpaned.remove(node);
1240 collectNodesToInsert(descriptor, elementToIndexMap, node, expanded, loadedChildren)
1241 .doWhenDone(new AsyncResult.Handler<ArrayList<TreeNode>>() {
1242 public void run(ArrayList<TreeNode> nodesToInsert) {
1243 insertNodesInto(nodesToInsert, node);
1244 updateNodesToInsert(nodesToInsert, pass, canSmartExpand, isChildNodeForceUpdate(node, forceUpdate, expanded));
1245 removeLoading(node, false);
1246 removeFromUpdating(node);
1248 if (node.getChildCount() > 0) {
1250 expand(node, canSmartExpand);
1254 final Object element = getElementFor(node);
1255 addNodeAction(element, new NodeAction() {
1256 public void onReady(final DefaultMutableTreeNode node) {
1257 removeLoading(node, false);
1261 processNodeActionsIfReady(node);
1263 }).doWhenProcessed(new Runnable() {
1265 myWillBeExpaned.remove(node);
1266 removeFromUpdating(node);
1267 processNodeActionsIfReady(node);
1271 }).doWhenRejected(new Runnable() {
1273 removeFromUpdating(node);
1274 processNodeActionsIfReady(node);
1279 private boolean isDisposed(DefaultMutableTreeNode node) {
1280 return !node.isNodeAncestor((DefaultMutableTreeNode)myTree.getModel().getRoot());
1283 private void expandSilently(TreePath path) {
1284 assertIsDispatchThread();
1287 mySilentExpand = path;
1288 getTree().expandPath(path);
1291 mySilentExpand = null;
1295 private void addSelectionSilently(TreePath path) {
1296 assertIsDispatchThread();
1299 mySilentSelect = path;
1300 getTree().getSelectionModel().addSelectionPath(path);
1303 mySilentSelect = null;
1307 private void expand(DefaultMutableTreeNode node, boolean canSmartExpand) {
1308 expand(new TreePath(node.getPath()), canSmartExpand);
1311 private void expand(final TreePath path, boolean canSmartExpand) {
1312 if (path == null) return;
1315 final Object last = path.getLastPathComponent();
1316 boolean isLeaf = myTree.getModel().isLeaf(path.getLastPathComponent());
1317 final boolean isRoot = last == myTree.getModel().getRoot();
1318 final TreePath parent = path.getParentPath();
1319 if (isRoot && !myTree.isExpanded(path)) {
1320 if (myTree.isRootVisible() || myUnbuiltNodes.contains(last)) {
1321 insertLoadingNode((DefaultMutableTreeNode)last, false);
1323 expandPath(path, canSmartExpand);
1325 else if (myTree.isExpanded(path) || (isLeaf && parent != null && myTree.isExpanded(parent) && !myUnbuiltNodes.contains(last) && !isCancelled(last))) {
1326 if (last instanceof DefaultMutableTreeNode) {
1327 processNodeActionsIfReady((DefaultMutableTreeNode)last);
1331 if (isLeaf && (myUnbuiltNodes.contains(last) || isCancelled(last))) {
1332 insertLoadingNode((DefaultMutableTreeNode)last, true);
1333 expandPath(path, canSmartExpand);
1335 else if (isLeaf && parent != null) {
1336 final DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode)parent.getLastPathComponent();
1337 if (parentNode != null) {
1338 addToUnbuilt(parentNode);
1340 expandPath(parent, canSmartExpand);
1343 expandPath(path, canSmartExpand);
1348 private void addToUnbuilt(DefaultMutableTreeNode node) {
1349 myUnbuiltNodes.add(node);
1352 private void removeFromUnbuilt(DefaultMutableTreeNode node) {
1353 myUnbuiltNodes.remove(node);
1356 private Pair<Boolean, LoadedChildren> processUnbuilt(final DefaultMutableTreeNode node,
1357 final NodeDescriptor descriptor,
1358 final TreeUpdatePass pass,
1359 final boolean isExpanded,
1360 final LoadedChildren loadedChildren) {
1361 final Ref<Pair<Boolean, LoadedChildren>> result = new Ref<Pair<Boolean, LoadedChildren>>();
1363 execute(new Runnable() {
1365 if (!isExpanded && getBuilder().isAlwaysShowPlus(descriptor)) {
1366 result.set(new Pair<Boolean, LoadedChildren>(true, null));
1370 final Object element = getElementFor(node);
1372 addToUpdating(node);
1375 final LoadedChildren children = loadedChildren != null ? loadedChildren : new LoadedChildren(getChildrenFor(element));
1379 if (children.getElements().size() == 0) {
1380 removeFromUnbuilt(node);
1381 removeLoading(node, true);
1385 if (isAutoExpand(node)) {
1386 addNodeAction(getElementFor(node), new NodeAction() {
1387 public void onReady(final DefaultMutableTreeNode node) {
1388 final TreePath path = new TreePath(node.getPath());
1389 if (getTree().isExpanded(path) || children.getElements().size() == 0) {
1390 removeLoading(node, false);
1393 maybeYeild(new ActiveRunnable() {
1394 public ActionCallback run() {
1395 expand(element, null);
1396 return new ActionCallback.Done();
1406 removeFromUpdating(node);
1408 processNodeActionsIfReady(node);
1410 result.set(new Pair<Boolean, LoadedChildren>(processed, children));
1413 removeFromUpdating(node);
1418 return result.get();
1421 private boolean removeIfLoading(TreeNode node) {
1422 if (isLoadingNode(node)) {
1423 moveSelectionToParentIfNeeded(node);
1424 removeNodeFromParent((MutableTreeNode)node, false);
1431 private void moveSelectionToParentIfNeeded(TreeNode node) {
1432 TreePath path = getPathFor(node);
1433 if (myTree.getSelectionModel().isPathSelected(path)) {
1434 TreePath parentPath = path.getParentPath();
1435 myTree.getSelectionModel().removeSelectionPath(path);
1436 if (parentPath != null) {
1437 myTree.getSelectionModel().addSelectionPath(parentPath);
1442 //todo [kirillk] temporary consistency check
1443 private Object[] getChildrenFor(final Object element) {
1444 final Ref<Object[]> passOne = new Ref<Object[]>();
1447 execute(new Runnable() {
1449 passOne.set(getTreeStructure().getChildElements(element));
1453 catch (IndexNotReadyException e) {
1454 warnOnIndexNotReady();
1455 return ArrayUtil.EMPTY_OBJECT_ARRAY;
1457 catch (InterruptedException e) {
1458 throw new ProcessCanceledException();
1463 if (!Registry.is("ide.tree.checkStructure")) return passOne.get();
1465 final Object[] passTwo = getTreeStructure().getChildElements(element);
1467 final HashSet two = new HashSet(Arrays.asList(passTwo));
1469 if (passOne.get().length != passTwo.length) {
1471 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
1475 for (Object eachInOne : passOne.get()) {
1476 if (!two.contains(eachInOne)) {
1478 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
1485 return passOne.get();
1488 private void warnOnIndexNotReady() {
1489 if (!myWasEverIndexNotReady) {
1490 myWasEverIndexNotReady = true;
1491 LOG.warn("Tree is not dumb-mode-aware; treeBuilder=" + getBuilder() + " treeStructure=" + getTreeStructure());
1495 private void updateNodesToInsert(final ArrayList<TreeNode> nodesToInsert,
1496 TreeUpdatePass pass,
1497 boolean canSmartExpand,
1498 boolean forceUpdate) {
1499 for (TreeNode aNodesToInsert : nodesToInsert) {
1500 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)aNodesToInsert;
1501 updateNodeChildren(childNode, pass, null, false, canSmartExpand, forceUpdate, true);
1505 private ActionCallback processExistingNodes(final DefaultMutableTreeNode node,
1506 final MutualMap<Object, Integer> elementToIndexMap,
1507 final TreeUpdatePass pass,
1508 final boolean canSmartExpand,
1509 final boolean forceUpdate,
1510 final boolean wasExpaned,
1511 final LoadedChildren preloaded) {
1513 final ArrayList<TreeNode> childNodes = TreeUtil.childrenToArray(node);
1514 return maybeYeild(new ActiveRunnable() {
1515 public ActionCallback run() {
1516 if (pass.isExpired()) return new ActionCallback.Rejected();
1517 if (childNodes.size() == 0) return new ActionCallback.Done();
1520 final ActionCallback result = new ActionCallback(childNodes.size());
1522 for (TreeNode each : childNodes) {
1523 final DefaultMutableTreeNode eachChild = (DefaultMutableTreeNode)each;
1524 if (isLoadingNode(eachChild)) {
1529 final boolean childForceUpdate = isChildNodeForceUpdate(eachChild, forceUpdate, wasExpaned);
1531 maybeYeild(new ActiveRunnable() {
1533 public ActionCallback run() {
1534 return processExistingNode(eachChild, getDescriptorFrom(eachChild), node, elementToIndexMap, pass, canSmartExpand,
1535 childForceUpdate, preloaded);
1537 }, pass, node).notify(result);
1539 if (result.isRejected()) {
1549 private boolean isRerunNeeded(TreeUpdatePass pass) {
1550 if (pass.isExpired() || !canInitiateNewActivity()) return false;
1552 final boolean rerunBecauseTreeIsHidden = !pass.isExpired() && !isTreeShowing() && getUpdater().isInPostponeMode();
1554 return rerunBecauseTreeIsHidden || getUpdater().isRerunNeededFor(pass);
1557 private ActionCallback maybeYeild(final ActiveRunnable processRunnable, final TreeUpdatePass pass, final DefaultMutableTreeNode node) {
1558 final ActionCallback result = new ActionCallback();
1560 if (isRerunNeeded(pass)) {
1561 getUpdater().addSubtreeToUpdate(pass);
1562 result.setRejected();
1565 if (isToYieldUpdateFor(node)) {
1566 pass.setCurrentNode(node);
1567 boolean wasRun = yieldAndRun(new Runnable() {
1569 if (pass.isExpired()) {
1570 result.setRejected();
1574 if (isRerunNeeded(pass)) {
1575 runDone(new Runnable() {
1577 if (!pass.isExpired()) {
1578 getUpdater().addSubtreeToUpdate(pass);
1582 result.setRejected();
1586 execute(processRunnable).notify(result);
1588 catch (ProcessCanceledException e) {
1590 result.setRejected();
1597 result.setRejected();
1602 execute(processRunnable).notify(result);
1604 catch (ProcessCanceledException e) {
1606 result.setRejected();
1615 private ActionCallback execute(final ActiveRunnable runnable) throws ProcessCanceledException {
1616 final ActionCallback result = new ActionCallback();
1617 execute(new Runnable() {
1619 runnable.run().notify(result);
1625 private void execute(Runnable runnable) {
1626 execute(runnable, null);
1629 private void execute(Runnable runnable, @Nullable DefaultMutableTreeNode node) throws ProcessCanceledException {
1631 if (!canInitiateNewActivity()) {
1632 throw new ProcessCanceledException();
1637 if (!canInitiateNewActivity()) {
1638 throw new ProcessCanceledException();
1641 catch (ProcessCanceledException e) {
1643 addToCancelled(node);
1645 if (!isReleased()) {
1646 setCancelRequested(true);
1653 private boolean canInitiateNewActivity() {
1654 return !isCancelProcessed() && !myReleaseRequested && !isReleased();
1657 private ActionCallback resetToReady() {
1658 final ActionCallback result = new ActionCallback();
1665 myResettingToReadyNow.set(true);
1667 invokeLaterIfNeeded(new Runnable() {
1669 Progressive[] progressives = myBatchIndicators.keySet().toArray(new Progressive[myBatchIndicators.size()]);
1670 for (Progressive each : progressives) {
1671 myBatchIndicators.remove(each).cancel();
1672 myBatchCallbacks.remove(each).setRejected();
1675 resetToReadyNow().notify(result);
1682 private ActionCallback resetToReadyNow() {
1683 if (isReleased()) return new ActionCallback.Rejected();
1685 assertIsDispatchThread();
1687 DefaultMutableTreeNode[] uc = myUpdatingChildren.toArray(new DefaultMutableTreeNode[myUpdatingChildren.size()]);
1688 for (DefaultMutableTreeNode each : uc) {
1689 resetIncompleteNode(each);
1693 Object[] bg = myLoadedInBackground.keySet().toArray(new Object[myLoadedInBackground.size()]);
1694 for (Object each : bg) {
1695 resetIncompleteNode(getNodeForElement(each, false));
1698 myUpdaterState = null;
1699 getUpdater().reset();
1702 myYeildingNow = false;
1703 myYeildingPasses.clear();
1704 myYeildingDoneRunnables.clear();
1706 myNodeActions.clear();
1707 myNodeChildrenActions.clear();
1709 myUpdatingChildren.clear();
1710 myLoadedInBackground.clear();
1712 myDeferredExpansions.clear();
1713 myDeferredSelections.clear();
1715 ActionCallback result = getReady(this);
1716 result.doWhenDone(new Runnable() {
1718 myResettingToReadyNow.set(false);
1719 setCancelRequested(false);
1728 public void addToCancelled(DefaultMutableTreeNode node) {
1729 myCancelledBuild.put(node, node);
1732 public void removeFromCancelled(DefaultMutableTreeNode node) {
1733 myCancelledBuild.remove(node);
1736 public boolean isCancelled(Object node) {
1737 if (node instanceof DefaultMutableTreeNode) {
1738 return myCancelledBuild.containsKey((DefaultMutableTreeNode)node);
1744 private void resetIncompleteNode(DefaultMutableTreeNode node) {
1745 if (myReleaseRequested) return;
1747 addToCancelled(node);
1749 if (!isExpanded(node, false)) {
1750 node.removeAllChildren();
1751 if (!getTreeStructure().isAlwaysLeaf(getElementFor(node))) {
1752 insertLoadingNode(node, true);
1756 removeFromUnbuilt(node);
1757 removeLoading(node, true);
1761 private boolean yieldAndRun(final Runnable runnable, final TreeUpdatePass pass) {
1762 myYeildingPasses.add(pass);
1763 myYeildingNow = true;
1764 yield(new Runnable() {
1766 if (isReleased()) return;
1768 runOnYieldingDone(new Runnable() {
1770 if (isReleased()) return;
1772 executeYieldingRequest(runnable, pass);
1781 public boolean isYeildingNow() {
1782 return myYeildingNow;
1785 private boolean hasSheduledUpdates() {
1786 return getUpdater().hasNodesToUpdate();
1789 public boolean isReady() {
1790 return isReady(false);
1793 public boolean isReady(boolean attempt) {
1794 Boolean ready = _isReady(attempt);
1795 return ready != null && ready.booleanValue();
1799 public Boolean _isReady(boolean attempt) {
1800 Boolean ready = checkValue(new Computable<Boolean>() {
1802 public Boolean compute() {
1803 return Boolean.valueOf(isIdle() && !hasPendingWork() && !isNodeActionsPending());
1807 return ready != null && ready.booleanValue();
1810 private Boolean checkValue(Computable<Boolean> computable, boolean attempt, Boolean defaultValue) {
1811 boolean toRelease = true;
1814 if (!attemptLock()) {
1816 return defaultValue != null ? defaultValue : computable.compute();
1821 return computable.compute();
1823 catch (InterruptedException e) {
1825 return defaultValue;
1833 public String getStatus() {
1834 return "isReady=" + isReady() + "\n" +
1835 " isIdle=" + isIdle() + "\n" +
1836 " isYeildingNow=" + isYeildingNow() + "\n" +
1837 " isWorkerBusy=" + isWorkerBusy() + "\n" +
1838 " hasUpdatingNow=" + hasUpdatingNow() + "\n" +
1839 " isLoadingInBackgroundNow=" + isLoadingInBackgroundNow() + "\n" +
1840 " hasPendingWork=" + hasPendingWork() + "\n" +
1841 " hasNodesToUpdate=" + hasNodesToUpdate() + "\n" +
1842 " updaterState=" + myUpdaterState + "\n" +
1843 " hasScheduledUpdates=" + hasSheduledUpdates() + "\n" +
1844 " isPostponedMode=" + getUpdater().isInPostponeMode() + "\n" +
1845 " nodeActions=" + myNodeActions.keySet() + "\n" +
1846 " nodeChildrenActions=" + myNodeChildrenActions.keySet() + "\n" +
1847 "isReleased=" + isReleased() + "\n" +
1848 " isReleaseRequested=" + isReleaseRequested() + "\n" +
1849 "isCancelProcessed=" + isCancelProcessed() + "\n" +
1850 " isCancelRequested=" + myCancelRequest + "\n" +
1851 " isResettingToReadyNow=" + myResettingToReadyNow + "\n" +
1852 "canInitiateNewActivity=" + canInitiateNewActivity();
1855 public boolean hasPendingWork() {
1856 return hasNodesToUpdate() || (myUpdaterState != null && myUpdaterState.isProcessingNow()) || (hasSheduledUpdates() && !getUpdater().isInPostponeMode());
1859 public boolean isIdle() {
1860 return !isYeildingNow() && !isWorkerBusy() && !hasUpdatingNow() && !isLoadingInBackgroundNow();
1863 private void executeYieldingRequest(Runnable runnable, TreeUpdatePass pass) {
1866 myYeildingPasses.remove(pass);
1870 if (!isReleased()) {
1871 maybeYeildingFinished();
1875 catch (ProcessCanceledException e) {
1880 private void maybeYeildingFinished() {
1881 if (myYeildingPasses.size() == 0) {
1882 myYeildingNow = false;
1883 flushPendingNodeActions();
1888 assertIsDispatchThread();
1890 if (isReleased()) return;
1892 Boolean ready = _isReady(true);
1893 if (ready != null && ready.booleanValue()) {
1894 myRevalidatedObjects.clear();
1896 setCancelRequested(false);
1897 myResettingToReadyNow.set(false);
1899 myInitialized.setDone();
1901 if (canInitiateNewActivity()) {
1902 if (myUpdaterState != null && !myUpdaterState.isProcessingNow()) {
1903 UpdaterTreeState oldState = myUpdaterState;
1904 if (!myUpdaterState.restore(null)) {
1905 setUpdaterState(oldState);
1916 if (myTree.isShowing()) {
1917 if (getBuilder().isToEnsureSelectionOnFocusGained() && Registry.is("ide.tree.ensureSelectionOnFocusGained")) {
1918 TreeUtil.ensureSelection(myTree);
1922 if (myInitialized.isDone()) {
1923 myBusyObject.onReady();
1926 if (canInitiateNewActivity()) {
1927 TreePath[] selection = getTree().getSelectionPaths();
1928 Rectangle visible = getTree().getVisibleRect();
1929 if (selection != null) {
1930 for (TreePath each : selection) {
1931 Rectangle bounds = getTree().getPathBounds(each);
1932 if (bounds != null && (visible.contains(bounds) || visible.intersects(bounds))) {
1933 getTree().repaint(bounds);
1938 } else if (ready == null) {
1939 scheduleMaybeReady();
1943 private void scheduleMaybeReady() {
1944 myMaybeReady.cancelAllRequests();
1945 myMaybeReady.addRequest(myMaybeReadyRunnable, Registry.intValue("ide.tree.waitForReadySchedule"));
1948 private void flushPendingNodeActions() {
1949 final DefaultMutableTreeNode[] nodes = myPendingNodeActions.toArray(new DefaultMutableTreeNode[myPendingNodeActions.size()]);
1950 myPendingNodeActions.clear();
1952 for (DefaultMutableTreeNode each : nodes) {
1953 processNodeActionsIfReady(each);
1956 final Runnable[] actions = myYeildingDoneRunnables.toArray(new Runnable[myYeildingDoneRunnables.size()]);
1957 for (Runnable each : actions) {
1958 if (!isYeildingNow()) {
1959 myYeildingDoneRunnables.remove(each);
1967 protected void runOnYieldingDone(Runnable onDone) {
1968 getBuilder().runOnYeildingDone(onDone);
1971 protected void yield(Runnable runnable) {
1972 getBuilder().yield(runnable);
1975 private boolean isToYieldUpdateFor(final DefaultMutableTreeNode node) {
1976 if (!canYield()) return false;
1977 return getBuilder().isToYieldUpdateFor(node);
1980 private MutualMap<Object, Integer> loadElementsFromStructure(final NodeDescriptor descriptor,
1981 @Nullable LoadedChildren preloadedChildren) {
1982 MutualMap<Object, Integer> elementToIndexMap = new MutualMap<Object, Integer>(true);
1983 List children = preloadedChildren != null
1984 ? preloadedChildren.getElements()
1985 : Arrays.asList(getChildrenFor(getBuilder().getTreeStructureElement(descriptor)));
1987 for (Object child : children) {
1988 if (!isValid(child)) continue;
1989 elementToIndexMap.put(child, Integer.valueOf(index));
1992 return elementToIndexMap;
1995 private void expand(final DefaultMutableTreeNode node,
1996 final NodeDescriptor descriptor,
1997 final boolean wasLeaf,
1998 final boolean canSmartExpand) {
1999 final Alarm alarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
2000 alarm.addRequest(new Runnable() {
2002 myTree.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
2004 }, WAIT_CURSOR_DELAY);
2006 if (wasLeaf && isAutoExpand(descriptor)) {
2007 expand(node, canSmartExpand);
2010 ArrayList<TreeNode> nodes = TreeUtil.childrenToArray(node);
2011 for (TreeNode node1 : nodes) {
2012 final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)node1;
2013 if (isLoadingNode(childNode)) continue;
2014 NodeDescriptor childDescr = getDescriptorFrom(childNode);
2015 if (isAutoExpand(childDescr)) {
2016 addNodeAction(getElementFor(childNode), new NodeAction() {
2017 public void onReady(DefaultMutableTreeNode node) {
2018 expand(childNode, canSmartExpand);
2021 addSubtreeToUpdate(childNode);
2025 int n = alarm.cancelAllRequests();
2027 myTree.setCursor(Cursor.getDefaultCursor());
2031 public static boolean isLoadingNode(final Object node) {
2032 return node instanceof LoadingNode;
2035 private AsyncResult<ArrayList<TreeNode>> collectNodesToInsert(final NodeDescriptor descriptor,
2036 final MutualMap<Object, Integer> elementToIndexMap,
2037 final DefaultMutableTreeNode parent,
2038 final boolean addLoadingNode,
2039 @NotNull final LoadedChildren loadedChildren) {
2040 final AsyncResult<ArrayList<TreeNode>> result = new AsyncResult<ArrayList<TreeNode>>();
2042 final ArrayList<TreeNode> nodesToInsert = new ArrayList<TreeNode>();
2043 final Collection<Object> allElements = elementToIndexMap.getKeys();
2045 final ActionCallback processingDone = new ActionCallback(allElements.size());
2047 for (final Object child : allElements) {
2048 Integer index = elementToIndexMap.getValue(child);
2049 final Ref<NodeDescriptor> childDescr = new Ref<NodeDescriptor>(loadedChildren.getDescriptor(child));
2050 boolean needToUpdate = false;
2051 if (childDescr.get() == null) {
2052 childDescr.set(getTreeStructure().createDescriptor(child, descriptor));
2053 needToUpdate = true;
2056 if (childDescr.get() == null) {
2057 processingDone.setDone();
2060 childDescr.get().setIndex(index.intValue());
2062 final ActionCallback update = new ActionCallback();
2064 update(childDescr.get(), false).doWhenDone(new AsyncResult.Handler<Boolean>() {
2065 public void run(Boolean changes) {
2066 loadedChildren.putDescriptor(child, childDescr.get(), changes);
2075 update.doWhenDone(new Runnable() {
2077 Object element = getElementFromDescriptor(childDescr.get());
2078 if (element == null) {
2079 processingDone.setDone();
2082 DefaultMutableTreeNode node = getNodeForElement(element, false);
2083 if (node == null || node.getParent() != parent) {
2084 final DefaultMutableTreeNode childNode = createChildNode(childDescr.get());
2085 if (addLoadingNode || getBuilder().isAlwaysShowPlus(childDescr.get())) {
2086 insertLoadingNode(childNode, true);
2089 addToUnbuilt(childNode);
2091 nodesToInsert.add(childNode);
2092 createMapping(element, childNode);
2094 processingDone.setDone();
2100 processingDone.doWhenDone(new Runnable() {
2102 result.setDone(nodesToInsert);
2109 protected DefaultMutableTreeNode createChildNode(final NodeDescriptor descriptor) {
2110 return new ElementNode(this, descriptor);
2113 protected boolean canYield() {
2114 return myCanYield && myYeildingUpdate.asBoolean();
2117 public long getClearOnHideDelay() {
2118 return myClearOnHideDelay > 0 ? myClearOnHideDelay : Registry.intValue("ide.tree.clearOnHideTime");
2121 public ActionCallback getInitialized() {
2122 return myInitialized;
2125 public ActionCallback getReady(Object requestor) {
2126 return myBusyObject.getReady(requestor);
2129 private void addToUpdating(DefaultMutableTreeNode node) {
2130 synchronized (myUpdatingChildren) {
2131 myUpdatingChildren.add(node);
2135 private void removeFromUpdating(DefaultMutableTreeNode node) {
2136 synchronized (myUpdatingChildren) {
2137 myUpdatingChildren.remove(node);
2141 public boolean isUpdatingNow(DefaultMutableTreeNode node) {
2142 synchronized (myUpdatingChildren) {
2143 return myUpdatingChildren.contains(node);
2147 boolean hasUpdatingNow() {
2148 synchronized (myUpdatingChildren) {
2149 return myUpdatingChildren.size() > 0;
2153 public Map getNodeActions() {
2154 return myNodeActions;
2157 public List<Object> getLoadedChildrenFor(Object element) {
2158 List<Object> result = new ArrayList<Object>();
2160 DefaultMutableTreeNode node = (DefaultMutableTreeNode)getNodeForElement(element, false);
2162 for (int i = 0; i < node.getChildCount(); i++) {
2163 TreeNode each = node.getChildAt(i);
2164 if (isLoadingNode(each)) continue;
2166 result.add(getElementFor(each));
2173 public boolean hasNodesToUpdate() {
2174 return getUpdater().hasNodesToUpdate();
2177 public List<Object> getExpandedElements() {
2178 List<Object> result = new ArrayList<Object>();
2179 Enumeration<TreePath> enumeration = myTree.getExpandedDescendants(getPathFor(getRootNode()));
2180 while (enumeration.hasMoreElements()) {
2181 TreePath each = enumeration.nextElement();
2182 Object eachElement = getElementFor(each.getLastPathComponent());
2183 if (eachElement != null) {
2184 result.add(eachElement);
2191 public ActionCallback cancelUpdate() {
2192 if (isReleased()) return new ActionCallback.Rejected();
2194 setCancelRequested(true);
2196 final ActionCallback done = new ActionCallback();
2198 invokeLaterIfNeeded(new Runnable() {
2205 if (myResettingToReadyNow.get()) {
2206 getReady(this).notify(done);
2207 } else if (isReady()) {
2211 if (isIdle() && hasPendingWork()) {
2215 getReady(this).notify(done);
2223 if (isEdt() || isPassthroughMode()) {
2230 private void setCancelRequested(boolean requested) {
2232 if (isUnitTestingMode()) {
2237 myCancelRequest.set(requested);
2239 catch (InterruptedException e) {
2247 private boolean attemptLock() throws InterruptedException {
2248 return myStateLock.tryLock(Registry.intValue("ide.tree.uiLockAttempt"), TimeUnit.MILLISECONDS);
2251 private void acquireLock() throws InterruptedException {
2255 private void releaseLock() {
2256 myStateLock.unlock();
2260 public ActionCallback batch(final Progressive progressive) {
2261 assertIsDispatchThread();
2263 EmptyProgressIndicator indicator = new EmptyProgressIndicator();
2264 final ActionCallback callback = new ActionCallback();
2266 myBatchIndicators.put(progressive, indicator);
2267 myBatchCallbacks.put(progressive, callback);
2270 progressive.run(indicator);
2271 } catch (ProcessCanceledException e) {
2272 resetToReadyNow().doWhenProcessed(new Runnable() {
2274 callback.setRejected();
2279 if (isReleased()) return new ActionCallback.Rejected();
2281 getReady(this).doWhenDone(new Runnable() {
2283 if (myBatchIndicators.containsKey(progressive)) {
2284 ProgressIndicator indicator = myBatchIndicators.remove(progressive);
2285 myBatchCallbacks.remove(progressive);
2287 if (indicator.isCanceled()) {
2288 callback.setRejected();
2293 callback.setRejected();
2305 public boolean isCancelProcessed() {
2306 Boolean processed = checkValue(new Computable<Boolean>() {
2308 public Boolean compute() {
2309 return Boolean.valueOf(myCancelRequest.get() || myResettingToReadyNow.get());
2313 return processed != null && processed.booleanValue();
2316 public boolean isToPaintSelection() {
2317 return isReady(true) || !mySelectionIsAdjusted;
2320 public boolean isReleaseRequested() {
2321 return myReleaseRequested;
2324 public void executeUserRunnable(Runnable runnable) {
2326 myUserRunnables.add(runnable);
2330 myUserRunnables.remove(runnable);
2334 static class ElementNode extends DefaultMutableTreeNode {
2336 Set<Object> myElements = new HashSet<Object>();
2337 AbstractTreeUi myUi;
2339 ElementNode(AbstractTreeUi ui, NodeDescriptor descriptor) {
2345 public void insert(final MutableTreeNode newChild, final int childIndex) {
2346 super.insert(newChild, childIndex);
2347 final Object element = myUi.getElementFor(newChild);
2348 if (element != null) {
2349 myElements.add(element);
2354 public void remove(final int childIndex) {
2355 final TreeNode node = getChildAt(childIndex);
2356 super.remove(childIndex);
2357 final Object element = myUi.getElementFor(node);
2358 if (element != null) {
2359 myElements.remove(element);
2363 boolean isValidChild(Object childElement) {
2364 return myElements.contains(childElement);
2368 public String toString() {
2369 return String.valueOf(getUserObject());
2373 private boolean isUpdatingParent(DefaultMutableTreeNode kid) {
2374 return getUpdatingParent(kid) != null;
2377 private DefaultMutableTreeNode getUpdatingParent(DefaultMutableTreeNode kid) {
2378 DefaultMutableTreeNode eachParent = kid;
2379 while (eachParent != null) {
2380 if (isUpdatingNow(eachParent)) return eachParent;
2381 eachParent = (DefaultMutableTreeNode)eachParent.getParent();
2387 private boolean isLoadedInBackground(Object element) {
2388 return getLoadedInBackground(element) != null;
2391 private UpdateInfo getLoadedInBackground(Object element) {
2392 synchronized (myLoadedInBackground) {
2393 return myLoadedInBackground.get(element);
2397 private void addToLoadedInBackground(Object element, UpdateInfo info) {
2398 synchronized (myLoadedInBackground) {
2399 myLoadedInBackground.put(element, info);
2403 private void removeFromLoadedInBackground(final Object element) {
2404 synchronized (myLoadedInBackground) {
2405 myLoadedInBackground.remove(element);
2409 private boolean isLoadingInBackgroundNow() {
2410 synchronized (myLoadedInBackground) {
2411 return myLoadedInBackground.size() > 0;
2415 private boolean queueBackgroundUpdate(final UpdateInfo updateInfo, final DefaultMutableTreeNode node) {
2416 assertIsDispatchThread();
2418 final Object oldElementFromDescriptor = getElementFromDescriptor(updateInfo.getDescriptor());
2420 UpdateInfo loaded = getLoadedInBackground(oldElementFromDescriptor);
2421 if (loaded != null) {
2422 loaded.apply(updateInfo);
2426 addToLoadedInBackground(oldElementFromDescriptor, updateInfo);
2428 if (!isNodeBeingBuilt(node)) {
2429 LoadingNode loadingNode = new LoadingNode(getLoadingNodeText());
2430 myTreeModel.insertNodeInto(loadingNode, node, node.getChildCount());
2433 removeFromUnbuilt(node);
2435 final Ref<LoadedChildren> children = new Ref<LoadedChildren>();
2436 final Ref<Object> elementFromDescriptor = new Ref<Object>();
2438 final DefaultMutableTreeNode[] nodeToProcessActions = new DefaultMutableTreeNode[1];
2440 final Runnable finalizeRunnable = new Runnable() {
2442 invokeLaterIfNeeded(new Runnable() {
2444 if (isReleased()) return;
2446 removeLoading(node, false);
2447 removeFromLoadedInBackground(elementFromDescriptor.get());
2448 removeFromLoadedInBackground(oldElementFromDescriptor);
2450 if (nodeToProcessActions[0] != null) {
2451 processNodeActionsIfReady(nodeToProcessActions[0]);
2459 Runnable buildRunnable = new Runnable() {
2461 if (updateInfo.getPass().isExpired()) {
2462 finalizeRunnable.run();
2466 if (!updateInfo.isDescriptorIsUpToDate()) {
2467 update(updateInfo.getDescriptor(), true);
2470 Object element = getElementFromDescriptor(updateInfo.getDescriptor());
2471 if (element == null) {
2472 removeFromLoadedInBackground(oldElementFromDescriptor);
2473 finalizeRunnable.run();
2477 elementFromDescriptor.set(element);
2479 Object[] loadedElements = getChildrenFor(getBuilder().getTreeStructureElement(updateInfo.getDescriptor()));
2481 final LoadedChildren loaded = new LoadedChildren(loadedElements);
2482 for (final Object each : loadedElements) {
2483 final NodeDescriptor eachChildDescriptor = getTreeStructure().createDescriptor(each, updateInfo.getDescriptor());
2484 execute(new Runnable() {
2486 loaded.putDescriptor(each, eachChildDescriptor, update(eachChildDescriptor, true).getResult());
2491 children.set(loaded);
2496 public String toString() {
2497 return "runnable=" + oldElementFromDescriptor;
2501 Runnable updateRunnable = new Runnable() {
2503 if (updateInfo.getPass().isExpired()) {
2504 finalizeRunnable.run();
2508 if (children.get() == null) {
2509 finalizeRunnable.run();
2513 if (isRerunNeeded(updateInfo.getPass())) {
2514 removeFromLoadedInBackground(elementFromDescriptor.get());
2515 getUpdater().addSubtreeToUpdate(updateInfo.getPass());
2519 removeFromLoadedInBackground(elementFromDescriptor.get());
2521 if (myUnbuiltNodes.contains(node)) {
2522 Pair<Boolean, LoadedChildren> unbuilt =
2523 processUnbuilt(node, updateInfo.getDescriptor(), updateInfo.getPass(), isExpanded(node, updateInfo.isWasExpanded()),
2525 if (unbuilt.getFirst()) {
2526 nodeToProcessActions[0] = node;
2531 updateNodeChildren(node, updateInfo.getPass(), children.get(), true, updateInfo.isCanSmartExpand(), updateInfo.isForceUpdate(),
2535 if (isRerunNeeded(updateInfo.getPass())) {
2536 getUpdater().addSubtreeToUpdate(updateInfo.getPass());
2540 Object element = elementFromDescriptor.get();
2542 if (element != null) {
2543 removeLoading(node, false);
2544 nodeToProcessActions[0] = node;
2548 queueToBackground(buildRunnable, updateRunnable, node).doWhenProcessed(finalizeRunnable).doWhenRejected(new Runnable() {
2550 updateInfo.getPass().expire();
2557 private boolean isExpanded(DefaultMutableTreeNode node, boolean isExpanded) {
2558 return isExpanded || myTree.isExpanded(getPathFor(node));
2561 private void removeLoading(DefaultMutableTreeNode parent, boolean forced) {
2562 if (!forced && myUnbuiltNodes.contains(parent) && !myCancelledBuild.containsKey(parent)) {
2566 for (int i = 0; i < parent.getChildCount(); i++) {
2567 TreeNode child = parent.getChildAt(i);
2568 if (removeIfLoading(child)) {
2573 if (parent == getRootNode() && !myTree.isRootVisible() && parent.getChildCount() == 0) {
2574 insertLoadingNode(parent, false);
2580 private void processNodeActionsIfReady(final DefaultMutableTreeNode node) {
2581 assertIsDispatchThread();
2583 if (isNodeBeingBuilt(node)) return;
2585 final Object o = node.getUserObject();
2586 if (!(o instanceof NodeDescriptor)) return;
2589 if (isYeildingNow()) {
2590 myPendingNodeActions.add(node);
2594 final Object element = getBuilder().getTreeStructureElement((NodeDescriptor)o);
2596 boolean childrenReady = !isLoadedInBackground(element);
2598 processActions(node, element, myNodeActions, childrenReady ? myNodeChildrenActions : null);
2599 if (childrenReady) {
2600 processActions(node, element, myNodeChildrenActions, null);
2603 if (!isUpdatingParent(node) && !isWorkerBusy()) {
2604 final UpdaterTreeState state = myUpdaterState;
2605 if (myNodeActions.size() == 0 && state != null && !state.isProcessingNow()) {
2606 if (canInitiateNewActivity()) {
2607 if (!state.restore(childrenReady ? node : null)) {
2608 setUpdaterState(state);
2618 private void processActions(DefaultMutableTreeNode node,
2620 final Map<Object, List<NodeAction>> nodeActions,
2621 @Nullable final Map<Object, List<NodeAction>> secondaryNodeAction) {
2622 final List<NodeAction> actions = nodeActions.get(element);
2623 if (actions != null) {
2624 nodeActions.remove(element);
2626 List<NodeAction> secondary = secondaryNodeAction != null ? secondaryNodeAction.get(element) : null;
2627 for (NodeAction each : actions) {
2628 if (secondary != null && secondary.contains(each)) {
2629 secondary.remove(each);
2637 private boolean canSmartExpand(DefaultMutableTreeNode node, boolean canSmartExpand) {
2638 if (!getBuilder().isSmartExpand()) return false;
2640 boolean smartExpand = !myNotForSmartExpand.contains(node) && canSmartExpand;
2641 return smartExpand ? validateAutoExpand(smartExpand, getElementFor(node)) : false;
2644 private void processSmartExpand(final DefaultMutableTreeNode node, final boolean canSmartExpand, boolean forced) {
2645 if (!getBuilder().isSmartExpand()) return;
2647 boolean can = canSmartExpand(node, canSmartExpand);
2649 if (!can && !forced) return;
2651 if (isNodeBeingBuilt(node) && !forced) {
2652 addNodeAction(getElementFor(node), new NodeAction() {
2653 public void onReady(DefaultMutableTreeNode node) {
2654 processSmartExpand(node, canSmartExpand, true);
2659 TreeNode child = getChildForSmartExpand(node);
2660 if (child != null) {
2661 final TreePath childPath = new TreePath(node.getPath()).pathByAddingChild(child);
2662 processInnerChange(new Runnable() {
2664 myTree.expandPath(childPath);
2672 private TreeNode getChildForSmartExpand(DefaultMutableTreeNode node) {
2673 int realChildCount = 0;
2674 TreeNode nodeToExpand = null;
2676 for (int i = 0; i < node.getChildCount(); i++) {
2677 TreeNode eachChild = node.getChildAt(i);
2679 if (!isLoadingNode(eachChild)) {
2681 if (nodeToExpand == null) {
2682 nodeToExpand = eachChild;
2686 if (realChildCount > 1) {
2687 nodeToExpand = null;
2692 return nodeToExpand;
2695 public boolean isLoadingChildrenFor(final Object nodeObject) {
2696 if (!(nodeObject instanceof DefaultMutableTreeNode)) return false;
2698 DefaultMutableTreeNode node = (DefaultMutableTreeNode)nodeObject;
2700 int loadingNodes = 0;
2701 for (int i = 0; i < Math.min(node.getChildCount(), 2); i++) {
2702 TreeNode child = node.getChildAt(i);
2703 if (isLoadingNode(child)) {
2707 return loadingNodes > 0 && loadingNodes == node.getChildCount();
2710 private boolean isParentLoading(Object nodeObject) {
2711 return getParentLoading(nodeObject) != null;
2714 private DefaultMutableTreeNode getParentLoading(Object nodeObject) {
2715 if (!(nodeObject instanceof DefaultMutableTreeNode)) return null;
2717 DefaultMutableTreeNode node = (DefaultMutableTreeNode)nodeObject;
2719 TreeNode eachParent = node.getParent();
2721 while (eachParent != null) {
2722 eachParent = eachParent.getParent();
2723 if (eachParent instanceof DefaultMutableTreeNode) {
2724 final Object eachElement = getElementFor((DefaultMutableTreeNode)eachParent);
2725 if (isLoadedInBackground(eachElement)) return (DefaultMutableTreeNode)eachParent;
2732 protected String getLoadingNodeText() {
2733 return IdeBundle.message("progress.searching");
2736 private ActionCallback processExistingNode(final DefaultMutableTreeNode childNode,
2737 final NodeDescriptor childDescriptor,
2738 final DefaultMutableTreeNode parentNode,
2739 final MutualMap<Object, Integer> elementToIndexMap,
2740 final TreeUpdatePass pass,
2741 final boolean canSmartExpand,
2742 final boolean forceUpdate,
2743 LoadedChildren parentPreloadedChildren) {
2745 final ActionCallback result = new ActionCallback();
2747 if (pass.isExpired()) {
2748 return new ActionCallback.Rejected();
2751 final Ref<NodeDescriptor> childDesc = new Ref<NodeDescriptor>(childDescriptor);
2753 if (childDesc.get() == null) {
2755 return new ActionCallback.Rejected();
2757 final Object oldElement = getElementFromDescriptor(childDesc.get());
2758 if (oldElement == null) {
2760 return new ActionCallback.Rejected();
2763 AsyncResult<Boolean> update = new AsyncResult<Boolean>();
2764 if (parentPreloadedChildren != null && parentPreloadedChildren.getDescriptor(oldElement) != null) {
2765 update.setDone(parentPreloadedChildren.isUpdated(oldElement));
2768 update = update(childDesc.get(), false);
2771 update.doWhenDone(new AsyncResult.Handler<Boolean>() {
2772 public void run(Boolean isChanged) {
2773 final Ref<Boolean> changes = new Ref<Boolean>(isChanged);
2775 final Ref<Boolean> forceRemapping = new Ref<Boolean>(false);
2776 final Ref<Object> newElement = new Ref<Object>(getElementFromDescriptor(childDesc.get()));
2778 final Integer index =
2779 newElement.get() != null ? elementToIndexMap.getValue(getBuilder().getTreeStructureElement(childDesc.get())) : null;
2780 final AsyncResult<Boolean> updateIndexDone = new AsyncResult<Boolean>();
2781 final ActionCallback indexReady = new ActionCallback();
2782 if (index != null) {
2783 final Object elementFromMap = elementToIndexMap.getKey(index);
2784 if (elementFromMap != newElement.get() && elementFromMap.equals(newElement.get())) {
2785 if (isInStructure(elementFromMap) && isInStructure(newElement.get())) {
2786 if (parentNode.getUserObject() instanceof NodeDescriptor) {
2787 final NodeDescriptor parentDescriptor = getDescriptorFrom(parentNode);
2788 childDesc.set(getTreeStructure().createDescriptor(elementFromMap, parentDescriptor));
2789 childNode.setUserObject(childDesc.get());
2790 newElement.set(elementFromMap);
2791 forceRemapping.set(true);
2792 update(childDesc.get(), false).doWhenDone(new AsyncResult.Handler<Boolean>() {
2793 public void run(Boolean isChanged) {
2794 changes.set(isChanged);
2795 updateIndexDone.setDone(isChanged);
2801 updateIndexDone.setDone(changes.get());
2805 updateIndexDone.setDone(changes.get());
2808 updateIndexDone.doWhenDone(new Runnable() {
2810 if (childDesc.get().getIndex() != index.intValue()) {
2813 childDesc.get().setIndex(index.intValue());
2814 indexReady.setDone();
2819 updateIndexDone.setDone();
2822 updateIndexDone.doWhenDone(new Runnable() {
2824 if (index != null && changes.get()) {
2825 updateNodeImageAndPosition(childNode, false);
2827 if (!oldElement.equals(newElement.get()) | forceRemapping.get()) {
2828 removeMapping(oldElement, childNode, newElement.get());
2829 if (newElement.get() != null) {
2830 createMapping(newElement.get(), childNode);
2832 NodeDescriptor parentDescriptor = getDescriptorFrom(parentNode);
2833 if (parentDescriptor != null) {
2834 parentDescriptor.setChildrenSortingStamp(-1);
2838 if (index == null) {
2839 int selectedIndex = -1;
2840 if (TreeBuilderUtil.isNodeOrChildSelected(myTree, childNode)) {
2841 selectedIndex = parentNode.getIndex(childNode);
2844 if (childNode.getParent() instanceof DefaultMutableTreeNode) {
2845 final DefaultMutableTreeNode parent = (DefaultMutableTreeNode)childNode.getParent();
2846 if (myTree.isExpanded(new TreePath(parent.getPath()))) {
2847 if (parent.getChildCount() == 1 && parent.getChildAt(0) == childNode) {
2848 insertLoadingNode(parent, false);
2853 Object disposedElement = getElementFor(childNode);
2855 removeNodeFromParent(childNode, selectedIndex >= 0);
2856 disposeNode(childNode);
2858 adjustSelectionOnChildRemove(parentNode, selectedIndex, disposedElement);
2861 elementToIndexMap.remove(getBuilder().getTreeStructureElement(childDesc.get()));
2862 updateNodeChildren(childNode, pass, null, false, canSmartExpand, forceUpdate, true);
2865 if (parentNode.equals(getRootNode())) {
2866 myTreeModel.nodeChanged(getRootNode());
2879 private void adjustSelectionOnChildRemove(DefaultMutableTreeNode parentNode, int selectedIndex, Object disposedElement) {
2880 DefaultMutableTreeNode node = getNodeForElement(disposedElement, false);
2881 if (node != null && isValidForSelectionAdjusting(node)) {
2882 Object newElement = getElementFor(node);
2883 addSelectionPath(getPathFor(node), true, getExpiredElementCondition(newElement), disposedElement);