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