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