helper method isEditorActivity
[fedora-idea.git] / platform / platform-api / src / com / intellij / ide / util / treeView / AbstractTreeUi.java
blob676013c50902553acd5437bb63080eec2b4f8b2a
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.ide.util.treeView;
18 import com.intellij.ide.IdeBundle;
19 import com.intellij.openapi.application.Application;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.progress.ProcessCanceledException;
23 import com.intellij.openapi.progress.ProgressIndicator;
24 import com.intellij.openapi.progress.ProgressManager;
25 import com.intellij.openapi.project.IndexNotReadyException;
26 import com.intellij.openapi.util.*;
27 import com.intellij.openapi.util.registry.Registry;
28 import com.intellij.openapi.util.registry.RegistryValue;
29 import com.intellij.ui.LoadingNode;
30 import com.intellij.ui.treeStructure.Tree;
31 import com.intellij.util.Alarm;
32 import com.intellij.util.ArrayUtil;
33 import com.intellij.util.ConcurrencyUtil;
34 import com.intellij.util.Time;
35 import com.intellij.util.concurrency.WorkerThread;
36 import com.intellij.util.containers.HashSet;
37 import com.intellij.util.enumeration.EnumerationCopy;
38 import com.intellij.util.ui.UIUtil;
39 import com.intellij.util.ui.tree.TreeUtil;
40 import com.intellij.util.ui.update.Activatable;
41 import com.intellij.util.ui.update.UiNotifyConnector;
42 import org.jetbrains.annotations.NotNull;
43 import org.jetbrains.annotations.Nullable;
45 import javax.swing.*;
46 import javax.swing.event.TreeExpansionEvent;
47 import javax.swing.event.TreeExpansionListener;
48 import javax.swing.event.TreeSelectionEvent;
49 import javax.swing.event.TreeSelectionListener;
50 import javax.swing.tree.*;
51 import java.awt.*;
52 import java.awt.event.FocusAdapter;
53 import java.awt.event.FocusEvent;
54 import java.security.AccessControlException;
55 import java.util.*;
56 import java.util.List;
57 import java.util.concurrent.ScheduledExecutorService;
58 import java.util.concurrent.TimeUnit;
60 public class AbstractTreeUi {
61 private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.treeView.AbstractTreeBuilder");
62 protected JTree myTree;// protected for TestNG
63 @SuppressWarnings({"WeakerAccess"}) protected DefaultTreeModel myTreeModel;
64 private AbstractTreeStructure myTreeStructure;
65 private AbstractTreeUpdater myUpdater;
67 private Comparator<NodeDescriptor> myNodeDescriptorComparator;
68 private final Comparator<TreeNode> myNodeComparator = new Comparator<TreeNode>() {
69 public int compare(TreeNode n1, TreeNode n2) {
70 if (isLoadingNode(n1) || isLoadingNode(n2)) return 0;
71 NodeDescriptor nodeDescriptor1 = getDescriptorFrom(((DefaultMutableTreeNode)n1));
72 NodeDescriptor nodeDescriptor2 = getDescriptorFrom(((DefaultMutableTreeNode)n2));
73 return myNodeDescriptorComparator != null
74 ? myNodeDescriptorComparator.compare(nodeDescriptor1, nodeDescriptor2)
75 : nodeDescriptor1.getIndex() - nodeDescriptor2.getIndex();
78 long myOwnComparatorStamp;
79 long myLastComparatorStamp;
81 private DefaultMutableTreeNode myRootNode;
82 private final HashMap<Object, Object> myElementToNodeMap = new HashMap<Object, Object>();
83 private final HashSet<DefaultMutableTreeNode> myUnbuiltNodes = new HashSet<DefaultMutableTreeNode>();
84 private TreeExpansionListener myExpansionListener;
85 private MySelectionListener mySelectionListener;
87 private WorkerThread myWorker = null;
88 private final Set<Runnable> myActiveWorkerTasks = new HashSet<Runnable>();
90 private ProgressIndicator myProgress;
91 private static final int WAIT_CURSOR_DELAY = 100;
92 private AbstractTreeNode<Object> TREE_NODE_WRAPPER;
93 private boolean myRootNodeWasInitialized = false;
94 private final Map<Object, List<NodeAction>> myNodeActions = new HashMap<Object, List<NodeAction>>();
95 private boolean myUpdateFromRootRequested;
96 private boolean myWasEverShown;
97 private boolean myUpdateIfInactive;
99 private final Map<Object, UpdateInfo> myLoadedInBackground = new HashMap<Object, UpdateInfo>();
100 private final Map<Object, List<NodeAction>> myNodeChildrenActions = new HashMap<Object, List<NodeAction>>();
102 private long myClearOnHideDelay = -1;
103 private ScheduledExecutorService ourClearanceService;
104 private final Map<AbstractTreeUi, Long> ourUi2Countdown = Collections.synchronizedMap(new WeakHashMap<AbstractTreeUi, Long>());
106 private final Set<Runnable> myDeferredSelections = new HashSet<Runnable>();
107 private final Set<Runnable> myDeferredExpansions = new HashSet<Runnable>();
109 private boolean myCanProcessDeferredSelections;
111 private UpdaterTreeState myUpdaterState;
112 private AbstractTreeBuilder myBuilder;
113 private boolean myReleaseRequested;
115 private final Set<DefaultMutableTreeNode> myUpdatingChildren = new HashSet<DefaultMutableTreeNode>();
116 private long myJanitorPollPeriod = Time.SECOND * 10;
117 private boolean myCheckStructure = false;
120 private boolean myCanYield = false;
122 private final List<TreeUpdatePass> myYeildingPasses = new ArrayList<TreeUpdatePass>();
124 private boolean myYeildingNow;
126 private final Set<DefaultMutableTreeNode> myPendingNodeActions = new HashSet<DefaultMutableTreeNode>();
127 private final Set<Runnable> myYeildingDoneRunnables = new HashSet<Runnable>();
129 private final Alarm myBusyAlarm = new Alarm();
130 private final Runnable myWaiterForReady = new Runnable() {
131 public void run() {
132 maybeSetBusyAndScheduleWaiterForReady(false);
136 private final RegistryValue myYeildingUpdate = Registry.get("ide.tree.yeildingUiUpdate");
137 private final RegistryValue myShowBusyIndicator = Registry.get("ide.tree.showBusyIndicator");
138 private final RegistryValue myWaitForReadyTime = Registry.get("ide.tree.waitForReadyTimout");
140 private boolean myWasEverIndexNotReady;
141 private boolean myShowing;
142 private FocusAdapter myFocusListener = new FocusAdapter() {
143 @Override
144 public void focusGained(FocusEvent e) {
145 maybeReady();
148 private Set<DefaultMutableTreeNode> myNotForSmartExpand = new HashSet<DefaultMutableTreeNode>();
149 private TreePath myRequestedExpand;
150 private ActionCallback myInitialized = new ActionCallback();
151 private Map<Object, ActionCallback> myReadyCallbacks = new WeakHashMap<Object, ActionCallback>();
153 private boolean myPassthroughMode = false;
156 private Set<Object> myAutoExpandRoots = new HashSet<Object>();
157 private final RegistryValue myAutoExpandDepth = Registry.get("ide.tree.autoExpandMaxDepth");
159 private Set<DefaultMutableTreeNode> myWillBeExpaned = new HashSet<DefaultMutableTreeNode>();
161 protected void init(AbstractTreeBuilder builder,
162 JTree tree,
163 DefaultTreeModel treeModel,
164 AbstractTreeStructure treeStructure,
165 @Nullable Comparator<NodeDescriptor> comparator,
166 boolean updateIfInactive) {
167 myBuilder = builder;
168 myTree = tree;
169 myTreeModel = treeModel;
170 TREE_NODE_WRAPPER = getBuilder().createSearchingTreeNodeWrapper();
171 myTree.setModel(myTreeModel);
172 setRootNode((DefaultMutableTreeNode)treeModel.getRoot());
173 setTreeStructure(treeStructure);
174 myNodeDescriptorComparator = comparator;
175 myUpdateIfInactive = updateIfInactive;
177 myExpansionListener = new MyExpansionListener();
178 myTree.addTreeExpansionListener(myExpansionListener);
180 mySelectionListener = new MySelectionListener();
181 myTree.addTreeSelectionListener(mySelectionListener);
183 setUpdater(getBuilder().createUpdater());
184 myProgress = getBuilder().createProgressIndicator();
185 Disposer.register(getBuilder(), getUpdater());
187 final UiNotifyConnector uiNotify = new UiNotifyConnector(tree, new Activatable() {
188 public void showNotify() {
189 myShowing = true;
190 myWasEverShown = true;
191 if (!isReleaseRequested()) {
192 activate(true);
196 public void hideNotify() {
197 myShowing = false;
198 if (!validateReleaseRequested()) {
199 deactivate();
203 Disposer.register(getBuilder(), uiNotify);
205 myTree.addFocusListener(myFocusListener);
209 boolean isNodeActionsPending() {
210 return !myNodeActions.isEmpty() || !myNodeChildrenActions.isEmpty();
213 private void clearNodeActions() {
214 myNodeActions.clear();
215 myNodeChildrenActions.clear();
218 private void maybeSetBusyAndScheduleWaiterForReady(boolean forcedBusy) {
219 if (!myShowBusyIndicator.asBoolean() || !canYield()) return;
221 if (myTree instanceof com.intellij.ui.treeStructure.Tree) {
222 final com.intellij.ui.treeStructure.Tree tree = (Tree)myTree;
223 final boolean isBusy = !isReady() || forcedBusy;
224 if (isBusy && tree.isShowing()) {
225 tree.setPaintBusy(true);
226 myBusyAlarm.cancelAllRequests();
227 myBusyAlarm.addRequest(myWaiterForReady, myWaitForReadyTime.asInteger());
229 else {
230 tree.setPaintBusy(false);
235 private void initClearanceServiceIfNeeded() {
236 if (ourClearanceService != null) return;
238 ourClearanceService = ConcurrencyUtil.newSingleScheduledThreadExecutor("AbstractTreeBuilder's janitor", Thread.MIN_PRIORITY + 1);
239 ourClearanceService.scheduleWithFixedDelay(new Runnable() {
240 public void run() {
241 cleanUpAll();
243 }, myJanitorPollPeriod, myJanitorPollPeriod, TimeUnit.MILLISECONDS);
246 private void cleanUpAll() {
247 final long now = System.currentTimeMillis();
248 final AbstractTreeUi[] uis = ourUi2Countdown.keySet().toArray(new AbstractTreeUi[ourUi2Countdown.size()]);
249 for (AbstractTreeUi eachUi : uis) {
250 if (eachUi == null) continue;
251 final Long timeToCleanup = ourUi2Countdown.get(eachUi);
252 if (timeToCleanup == null) continue;
253 if (now >= timeToCleanup.longValue()) {
254 ourUi2Countdown.remove(eachUi);
255 Runnable runnable = new Runnable() {
256 public void run() {
257 getBuilder().cleanUp();
260 if (isPassthroughMode()) {
261 runnable.run();
262 } else {
263 UIUtil.invokeAndWaitIfNeeded(runnable);
269 protected void doCleanUp() {
270 Runnable cleanup = new Runnable() {
271 public void run() {
272 if (!isReleaseRequested()) {
273 cleanUpNow();
278 if (isPassthroughMode()) {
279 cleanup.run();
280 } else {
281 UIUtil.invokeLaterIfNeeded(cleanup);
285 private void disposeClearanceService() {
286 try {
287 if (ourClearanceService != null) {
288 ourClearanceService.shutdown();
289 ourClearanceService = null;
292 catch (AccessControlException e) {
293 LOG.warn(e);
297 public void activate(boolean byShowing) {
298 myCanProcessDeferredSelections = true;
299 ourUi2Countdown.remove(this);
301 if (!myWasEverShown || myUpdateFromRootRequested || myUpdateIfInactive) {
302 getBuilder().updateFromRoot();
305 getUpdater().showNotify();
307 myWasEverShown |= byShowing;
310 public void deactivate() {
311 getUpdater().hideNotify();
312 myBusyAlarm.cancelAllRequests();
314 if (!myWasEverShown) return;
316 if (isNodeActionsPending()) {
317 cancelBackgroundLoading();
318 myUpdateFromRootRequested = true;
321 if (getClearOnHideDelay() >= 0) {
322 ourUi2Countdown.put(this, System.currentTimeMillis() + getClearOnHideDelay());
323 initClearanceServiceIfNeeded();
327 public void requestRelease() {
328 if (isReleaseRequested()) return;
330 assertIsDispatchThread();
332 myReleaseRequested = true;
334 getUpdater().requestRelease();
336 maybeReady();
339 private void releaseNow() {
340 myTree.removeTreeExpansionListener(myExpansionListener);
341 myTree.removeTreeSelectionListener(mySelectionListener);
342 myTree.removeFocusListener(myFocusListener);
344 disposeNode(getRootNode());
345 myElementToNodeMap.clear();
346 getUpdater().cancelAllRequests();
347 if (myWorker != null) {
348 myWorker.dispose(true);
349 clearWorkerTasks();
351 TREE_NODE_WRAPPER.setValue(null);
352 if (myProgress != null) {
353 myProgress.cancel();
355 disposeClearanceService();
357 myTree = null;
358 setUpdater(null);
359 myWorker = null;
360 myTreeStructure = null;
361 myBuilder.releaseUi();
362 myBuilder = null;
364 clearNodeActions();
366 myDeferredSelections.clear();
367 myDeferredExpansions.clear();
368 myYeildingDoneRunnables.clear();
371 public boolean isReleaseRequested() {
372 return myReleaseRequested;
375 public boolean validateReleaseRequested() {
376 if (isReleaseRequested()) {
377 SwingUtilities.invokeLater(new Runnable() {
378 public void run() {
379 maybeReady();
382 return true;
383 } else {
384 return false;
388 public boolean isReleased() {
389 return myBuilder == null;
392 protected void doExpandNodeChildren(final DefaultMutableTreeNode node) {
393 if (!myUnbuiltNodes.contains(node)) return;
394 if (isLoadedInBackground(getElementFor(node))) return;
396 if (!isReleaseRequested()) {
397 getTreeStructure().commit();
398 addSubtreeToUpdate(node);
399 getUpdater().performUpdate();
400 } else {
401 processNodeActionsIfReady(node);
405 public final AbstractTreeStructure getTreeStructure() {
406 return myTreeStructure;
409 public final JTree getTree() {
410 return myTree;
413 @Nullable
414 private NodeDescriptor getDescriptorFrom(DefaultMutableTreeNode node) {
415 return (NodeDescriptor)node.getUserObject();
418 @Nullable
419 public final DefaultMutableTreeNode getNodeForElement(Object element, final boolean validateAgainstStructure) {
420 DefaultMutableTreeNode result = null;
421 if (validateAgainstStructure) {
422 int index = 0;
423 while (true) {
424 final DefaultMutableTreeNode node = findNode(element, index);
425 if (node == null) break;
427 if (isNodeValidForElement(element, node)) {
428 result = node;
429 break;
432 index++;
435 else {
436 result = getFirstNode(element);
440 if (result != null && !isNodeInStructure(result)) {
441 disposeNode(result);
442 result = null;
445 return result;
448 private boolean isNodeInStructure(DefaultMutableTreeNode node) {
449 return TreeUtil.isAncestor(getRootNode(), node) && getRootNode() == myTreeModel.getRoot();
452 private boolean isNodeValidForElement(final Object element, final DefaultMutableTreeNode node) {
453 return isSameHierarchy(element, node) || isValidChildOfParent(element, node);
456 private boolean isValidChildOfParent(final Object element, final DefaultMutableTreeNode node) {
457 final DefaultMutableTreeNode parent = (DefaultMutableTreeNode)node.getParent();
458 final Object parentElement = getElementFor(parent);
459 if (!isInStructure(parentElement)) return false;
461 if (parent instanceof ElementNode) {
462 return ((ElementNode)parent).isValidChild(element);
464 else {
465 for (int i = 0; i < parent.getChildCount(); i++) {
466 final TreeNode child = parent.getChildAt(i);
467 final Object eachElement = getElementFor(child);
468 if (element.equals(eachElement)) return true;
472 return false;
475 private boolean isSameHierarchy(Object eachParent, DefaultMutableTreeNode eachParentNode) {
476 boolean valid = true;
477 while (true) {
478 if (eachParent == null) {
479 valid = eachParentNode == null;
480 break;
483 if (!eachParent.equals(getElementFor(eachParentNode))) {
484 valid = false;
485 break;
488 eachParent = getTreeStructure().getParentElement(eachParent);
489 eachParentNode = (DefaultMutableTreeNode)eachParentNode.getParent();
491 return valid;
494 public final DefaultMutableTreeNode getNodeForPath(Object[] path) {
495 DefaultMutableTreeNode node = null;
496 for (final Object pathElement : path) {
497 node = node == null ? getFirstNode(pathElement) : findNodeForChildElement(node, pathElement);
498 if (node == null) {
499 break;
502 return node;
505 public final void buildNodeForElement(Object element) {
506 getUpdater().performUpdate();
507 DefaultMutableTreeNode node = getNodeForElement(element, false);
508 if (node == null) {
509 final java.util.List<Object> elements = new ArrayList<Object>();
510 while (true) {
511 element = getTreeStructure().getParentElement(element);
512 if (element == null) {
513 break;
515 elements.add(0, element);
518 for (final Object element1 : elements) {
519 node = getNodeForElement(element1, false);
520 if (node != null) {
521 expand(node, true);
527 public final void buildNodeForPath(Object[] path) {
528 getUpdater().performUpdate();
529 DefaultMutableTreeNode node = null;
530 for (final Object pathElement : path) {
531 node = node == null ? getFirstNode(pathElement) : findNodeForChildElement(node, pathElement);
532 if (node != null && node != path[path.length - 1]) {
533 expand(node, true);
538 public final void setNodeDescriptorComparator(Comparator<NodeDescriptor> nodeDescriptorComparator) {
539 myNodeDescriptorComparator = nodeDescriptorComparator;
540 myLastComparatorStamp = -1;
541 getBuilder().queueUpdateFrom(getTreeStructure().getRootElement(), true);
544 protected AbstractTreeBuilder getBuilder() {
545 return myBuilder;
548 protected final void initRootNode() {
549 if (myUpdateIfInactive) {
550 activate(false);
552 else {
553 myUpdateFromRootRequested = true;
557 private boolean initRootNodeNowIfNeeded(final TreeUpdatePass pass) {
558 boolean wasCleanedUp = false;
559 if (myRootNodeWasInitialized) {
560 Object root = getTreeStructure().getRootElement();
561 assert root != null : "Root element cannot be null";
563 Object currentRoot = getElementFor(myRootNode);
565 if (Comparing.equal(root, currentRoot)) return false;
567 Object rootAgain = getTreeStructure().getRootElement();
568 if (root != rootAgain && !root.equals(rootAgain)) {
569 assert false : "getRootElement() if called twice must return either root1 == root2 or root1.equals(root2)";
572 cleanUpNow();
573 wasCleanedUp = true;
576 myRootNodeWasInitialized = true;
578 final Object rootElement = getTreeStructure().getRootElement();
579 addNodeAction(rootElement, new NodeAction() {
580 public void onReady(final DefaultMutableTreeNode node) {
581 processDeferredActions();
583 }, false);
586 final Ref<NodeDescriptor> rootDescriptor = new Ref<NodeDescriptor>(null);
587 final boolean bgLoading = getTreeStructure().isToBuildChildrenInBackground(rootElement);
589 Runnable build = new Runnable() {
590 public void run() {
591 rootDescriptor.set(getTreeStructure().createDescriptor(rootElement, null));
592 getRootNode().setUserObject(rootDescriptor.get());
593 update(rootDescriptor.get(), true);
598 Runnable update = new Runnable() {
599 public void run() {
600 if (getElementFromDescriptor(rootDescriptor.get()) != null) {
601 createMapping(getElementFromDescriptor(rootDescriptor.get()), getRootNode());
605 insertLoadingNode(getRootNode(), true);
607 boolean willUpdate = false;
608 if (isAutoExpand(rootDescriptor.get())) {
609 willUpdate = myUnbuiltNodes.contains(getRootNode());
610 expand(getRootNode(), true);
612 if (!willUpdate) {
613 updateNodeChildren(getRootNode(), pass, null, false, false, false, true);
615 if (getRootNode().getChildCount() == 0) {
616 myTreeModel.nodeChanged(getRootNode());
621 if (bgLoading) {
622 queueToBackground(build, update);
624 else {
625 build.run();
626 update.run();
629 return wasCleanedUp;
632 private boolean isAutoExpand(NodeDescriptor descriptor) {
633 return isAutoExpand(descriptor, true);
636 private boolean isAutoExpand(NodeDescriptor descriptor, boolean validate) {
637 boolean autoExpand = false;
639 if (descriptor != null) {
640 autoExpand = getBuilder().isAutoExpandNode(descriptor);
643 Object element = getElementFromDescriptor(descriptor);
644 if (validate) {
645 autoExpand = validateAutoExpand(autoExpand, element);
648 if (!autoExpand && !myTree.isRootVisible()) {
649 if (element != null && element.equals(getTreeStructure().getRootElement())) return true;
652 return autoExpand;
655 private boolean validateAutoExpand(boolean autoExpand, Object element) {
656 if (autoExpand) {
657 int distance = getDistanceToAutoExpandRoot(element);
658 if (distance < 0) {
659 myAutoExpandRoots.add(element);
660 } else {
661 if (distance >= myAutoExpandDepth.asInteger() - 1) {
662 autoExpand = false;
666 if (autoExpand) {
667 DefaultMutableTreeNode node = getNodeForElement(element, false);
668 if (isInVisibleAutoExpandChain(node)) {
669 autoExpand = true;
670 } else {
671 autoExpand = false;
675 return autoExpand;
678 private boolean isInVisibleAutoExpandChain(DefaultMutableTreeNode child) {
679 TreeNode eachParent = child;
680 while (eachParent != null) {
682 if (myRootNode == eachParent) return true;
684 NodeDescriptor eachDescriptor = getDescriptorFrom((DefaultMutableTreeNode)eachParent);
685 if (!isAutoExpand(eachDescriptor, false)) {
686 TreePath path = getPathFor(eachParent);
687 if (myWillBeExpaned.contains(path.getLastPathComponent()) || (myTree.isExpanded(path) && myTree.isVisible(path))) {
688 return true;
689 } else {
690 return false;
693 eachParent = eachParent.getParent();
696 return false;
699 private int getDistanceToAutoExpandRoot(Object element) {
700 int distance = 0;
702 Object eachParent = element;
703 while (eachParent != null) {
704 if (myAutoExpandRoots.contains(eachParent)) break;
705 eachParent = getTreeStructure().getParentElement(eachParent);
706 distance++;
709 return eachParent != null ? distance : -1;
712 private boolean isAutoExpand(DefaultMutableTreeNode node) {
713 return isAutoExpand(getDescriptorFrom(node));
716 private AsyncResult<Boolean> update(final NodeDescriptor nodeDescriptor, boolean now) {
717 final AsyncResult<Boolean> result = new AsyncResult<Boolean>();
719 if (now || isPassthroughMode()) {
720 return new AsyncResult<Boolean>().setDone(_update(nodeDescriptor));
723 Object element = getElementFromDescriptor(nodeDescriptor);
724 boolean bgLoading = getTreeStructure().isToBuildChildrenInBackground(element);
726 boolean edt = isEdt();
727 if (bgLoading) {
728 if (edt) {
729 final Ref<Boolean> changes = new Ref<Boolean>(false);
730 queueToBackground(new Runnable() {
731 public void run() {
732 changes.set(_update(nodeDescriptor));
734 }, new Runnable() {
735 public void run() {
736 result.setDone(changes.get());
740 else {
741 result.setDone(_update(nodeDescriptor));
744 else {
745 if (edt || !myWasEverShown) {
746 result.setDone(_update(nodeDescriptor));
748 else {
749 UIUtil.invokeLaterIfNeeded(new Runnable() {
750 public void run() {
751 if (!validateReleaseRequested()) {
752 result.setDone(_update(nodeDescriptor));
754 else {
755 result.setRejected();
762 result.doWhenDone(new AsyncResult.Handler<Boolean>() {
763 public void run(Boolean changes) {
764 if (changes) {
765 final long updateStamp = nodeDescriptor.getUpdateCount();
766 UIUtil.invokeLaterIfNeeded(new Runnable() {
767 public void run() {
768 Object element = nodeDescriptor.getElement();
769 DefaultMutableTreeNode node = getNodeForElement(element, false);
770 if (node != null) {
771 TreePath path = getPathFor(node);
772 if (path != null && myTree.isVisible(path)) {
773 updateNodeImageAndPosition(node, false);
783 return result;
786 private boolean _update(NodeDescriptor nodeDescriptor) {
787 nodeDescriptor.setUpdateCount(nodeDescriptor.getUpdateCount() + 1);
788 return getBuilder().updateNodeDescriptor(nodeDescriptor);
791 private void assertIsDispatchThread() {
792 if (isPassthroughMode()) return;
794 if (isTreeShowing() && !isEdt()) {
795 LOG.error("Must be in event-dispatch thread");
799 private boolean isEdt() {
800 return SwingUtilities.isEventDispatchThread();
803 private boolean isTreeShowing() {
804 return myShowing;
807 private void assertNotDispatchThread() {
808 if (isPassthroughMode()) return;
810 if (isEdt()) {
811 LOG.error("Must not be in event-dispatch thread");
815 private void processDeferredActions() {
816 processDeferredActions(myDeferredSelections);
817 processDeferredActions(myDeferredExpansions);
820 private void processDeferredActions(Set<Runnable> actions) {
821 final Runnable[] runnables = actions.toArray(new Runnable[actions.size()]);
822 actions.clear();
823 for (Runnable runnable : runnables) {
824 runnable.run();
828 //todo: to make real callback
829 public ActionCallback queueUpdate(Object element) {
830 AbstractTreeUpdater updater = getUpdater();
831 if (updater == null) {
832 return new ActionCallback.Rejected();
835 final ActionCallback result = new ActionCallback();
836 DefaultMutableTreeNode node = getNodeForElement(element, false);
837 if (node != null) {
838 addSubtreeToUpdate(node);
840 else {
841 addSubtreeToUpdate(getRootNode());
844 updater.runAfterUpdate(new Runnable() {
845 public void run() {
846 result.setDone();
849 return result;
852 public void doUpdateFromRoot() {
853 updateSubtree(getRootNode(), false);
856 public ActionCallback doUpdateFromRootCB() {
857 final ActionCallback cb = new ActionCallback();
858 getUpdater().runAfterUpdate(new Runnable() {
859 public void run() {
860 cb.setDone();
863 updateSubtree(getRootNode(), false);
864 return cb;
867 public final void updateSubtree(DefaultMutableTreeNode node, boolean canSmartExpand) {
868 updateSubtree(new TreeUpdatePass(node), canSmartExpand);
871 public final void updateSubtree(TreeUpdatePass pass, boolean canSmartExpand) {
872 if (getUpdater() != null) {
873 getUpdater().addSubtreeToUpdate(pass);
875 else {
876 updateSubtreeNow(pass, canSmartExpand);
880 final void updateSubtreeNow(TreeUpdatePass pass, boolean canSmartExpand) {
881 maybeSetBusyAndScheduleWaiterForReady(true);
883 boolean consumed = initRootNodeNowIfNeeded(pass);
884 if (consumed) return;
886 final DefaultMutableTreeNode node = pass.getNode();
888 if (!(node.getUserObject() instanceof NodeDescriptor)) return;
890 setUpdaterState(new UpdaterTreeState(this)).beforeSubtreeUpdate();
892 boolean forceUpdate = true;
893 TreePath path = getPathFor(node);
894 boolean invisible = !myTree.isExpanded(path) && (path.getParentPath() == null || !myTree.isExpanded(path.getParentPath()));
896 if (invisible && myUnbuiltNodes.contains(node)) {
897 forceUpdate = false;
900 updateNodeChildren(node, pass, null, false, canSmartExpand, forceUpdate, false);
903 private boolean isToBuildInBackground(NodeDescriptor descriptor) {
904 return getTreeStructure().isToBuildChildrenInBackground(getBuilder().getTreeStructureElement(descriptor));
907 @NotNull
908 UpdaterTreeState setUpdaterState(UpdaterTreeState state) {
909 if (myUpdaterState != null && myUpdaterState.equals(state)) return state;
911 final UpdaterTreeState oldState = myUpdaterState;
912 if (oldState == null) {
913 myUpdaterState = state;
914 return state;
916 else {
917 oldState.addAll(state);
918 return oldState;
922 protected void doUpdateNode(final DefaultMutableTreeNode node) {
923 if (!(node.getUserObject() instanceof NodeDescriptor)) return;
924 final NodeDescriptor descriptor = getDescriptorFrom(node);
925 final Object prevElement = getElementFromDescriptor(descriptor);
926 if (prevElement == null) return;
927 update(descriptor, false).doWhenDone(new AsyncResult.Handler<Boolean>() {
928 public void run(Boolean changes) {
929 if (!isValid(descriptor)) {
930 if (isInStructure(prevElement)) {
931 getUpdater().addSubtreeToUpdateByElement(getTreeStructure().getParentElement(prevElement));
932 return;
935 if (changes) {
936 updateNodeImageAndPosition(node, true);
942 public Object getElementFromDescriptor(NodeDescriptor descriptor) {
943 return getBuilder().getTreeStructureElement(descriptor);
946 private void updateNodeChildren(final DefaultMutableTreeNode node,
947 final TreeUpdatePass pass,
948 @Nullable LoadedChildren loadedChildren,
949 boolean forcedNow,
950 final boolean toSmartExpand,
951 boolean forceUpdate,
952 final boolean descriptorIsUpToDate) {
953 try {
954 getTreeStructure().commit();
957 final NodeDescriptor descriptor = getDescriptorFrom(node);
958 if (descriptor == null) {
959 removeLoading(node, true);
960 return;
963 final boolean wasExpanded = myTree.isExpanded(new TreePath(node.getPath())) || isAutoExpand(node);
964 final boolean wasLeaf = node.getChildCount() == 0;
967 boolean bgBuild = isToBuildInBackground(descriptor);
968 boolean notRequiredToUpdateChildren = !forcedNow && !wasExpanded;
970 if (notRequiredToUpdateChildren && forceUpdate && !wasExpanded) {
971 boolean alwaysPlus = getBuilder().isAlwaysShowPlus(descriptor);
972 if (alwaysPlus && wasLeaf) {
973 notRequiredToUpdateChildren = false;
974 } else {
975 notRequiredToUpdateChildren = alwaysPlus;
979 final Ref<LoadedChildren> preloaded = new Ref<LoadedChildren>(loadedChildren);
980 boolean descriptorWasUpdated = descriptorIsUpToDate;
982 if (notRequiredToUpdateChildren) {
983 if (myUnbuiltNodes.contains(node) && node.getChildCount() == 0) {
984 insertLoadingNode(node, true);
986 return;
989 if (!forcedNow) {
990 if (!bgBuild) {
991 if (myUnbuiltNodes.contains(node)) {
992 if (!descriptorWasUpdated) {
993 update(descriptor, true);
994 descriptorWasUpdated = true;
997 if (processAlwaysLeaf(node)) return;
999 Pair<Boolean, LoadedChildren> unbuilt = processUnbuilt(node, descriptor, pass, wasExpanded, null);
1000 if (unbuilt.getFirst()) return;
1001 preloaded.set(unbuilt.getSecond());
1007 final boolean childForceUpdate = isChildNodeForceUpdate(node, forceUpdate, wasExpanded);
1009 if (!forcedNow && isToBuildInBackground(descriptor)) {
1010 if (processAlwaysLeaf(node)) return;
1012 queueBackgroundUpdate(
1013 new UpdateInfo(descriptor, pass, canSmartExpand(node, toSmartExpand), wasExpanded, childForceUpdate, descriptorWasUpdated), node);
1014 return;
1016 else {
1017 if (!descriptorWasUpdated) {
1018 update(descriptor, false).doWhenDone(new Runnable() {
1019 public void run() {
1020 if (processAlwaysLeaf(node)) return;
1021 updateNodeChildrenNow(node, pass, preloaded.get(), toSmartExpand, wasExpanded, wasLeaf, childForceUpdate);
1025 else {
1026 if (processAlwaysLeaf(node)) return;
1028 updateNodeChildrenNow(node, pass, preloaded.get(), toSmartExpand, wasExpanded, wasLeaf, childForceUpdate);
1032 finally {
1033 processNodeActionsIfReady(node);
1037 private boolean processAlwaysLeaf(DefaultMutableTreeNode node) {
1038 Object element = getElementFor(node);
1039 NodeDescriptor desc = getDescriptorFrom(node);
1041 if (desc == null) return false;
1043 if (getTreeStructure().isAlwaysLeaf(element)) {
1044 removeLoading(node, true);
1046 if (node.getChildCount() > 0) {
1047 final TreeNode[] children = new TreeNode[node.getChildCount()];
1048 for (int i = 0; i < node.getChildCount(); i++) {
1049 children[i] = node.getChildAt(i);
1052 if (isSelectionInside(node)) {
1053 addSelectionPath(getPathFor(node), true, Condition.TRUE, null);
1056 processInnerChange(new Runnable() {
1057 public void run() {
1058 for (TreeNode each : children) {
1059 removeNodeFromParent((MutableTreeNode)each, true);
1060 disposeNode((DefaultMutableTreeNode)each);
1066 removeFromUnbuilt(node);
1067 desc.setWasDeclaredAlwaysLeaf(true);
1068 processNodeActionsIfReady(node);
1069 return true;
1070 } else {
1071 boolean wasLeaf = desc.isWasDeclaredAlwaysLeaf();
1072 desc.setWasDeclaredAlwaysLeaf(false);
1074 if (wasLeaf) {
1075 insertLoadingNode(node, true);
1078 return false;
1082 private boolean isChildNodeForceUpdate(DefaultMutableTreeNode node, boolean parentForceUpdate, boolean parentExpanded) {
1083 TreePath path = getPathFor(node);
1084 return parentForceUpdate && (parentExpanded || myTree.isExpanded(path));
1087 private void updateNodeChildrenNow(final DefaultMutableTreeNode node,
1088 final TreeUpdatePass pass,
1089 final LoadedChildren preloadedChildren,
1090 final boolean toSmartExpand,
1091 final boolean wasExpanded,
1092 final boolean wasLeaf,
1093 final boolean forceUpdate) {
1094 final NodeDescriptor descriptor = getDescriptorFrom(node);
1096 final MutualMap<Object, Integer> elementToIndexMap = loadElementsFromStructure(descriptor, preloadedChildren);
1097 final LoadedChildren loadedChildren =
1098 preloadedChildren != null ? preloadedChildren : new LoadedChildren(elementToIndexMap.getKeys().toArray());
1101 addToUpdating(node);
1102 pass.setCurrentNode(node);
1104 final boolean canSmartExpand = canSmartExpand(node, toSmartExpand);
1106 processExistingNodes(node, elementToIndexMap, pass, canSmartExpand(node, toSmartExpand), forceUpdate, wasExpanded, preloadedChildren)
1107 .doWhenDone(new Runnable() {
1108 public void run() {
1109 if (isDisposed(node)) {
1110 removeFromUpdating(node);
1111 return;
1114 removeLoading(node, false);
1116 final boolean expanded = isExpanded(node, wasExpanded);
1118 if (expanded) {
1119 myWillBeExpaned.add(node);
1120 } else {
1121 myWillBeExpaned.remove(node);
1124 collectNodesToInsert(descriptor, elementToIndexMap, node, expanded, loadedChildren)
1125 .doWhenDone(new AsyncResult.Handler<ArrayList<TreeNode>>() {
1126 public void run(ArrayList<TreeNode> nodesToInsert) {
1127 insertNodesInto(nodesToInsert, node);
1128 updateNodesToInsert(nodesToInsert, pass, canSmartExpand, isChildNodeForceUpdate(node, forceUpdate, expanded));
1129 removeLoading(node, true);
1130 removeFromUpdating(node);
1132 if (node.getChildCount() > 0) {
1133 if (expanded ) {
1134 expand(node, canSmartExpand);
1138 final Object element = getElementFor(node);
1139 addNodeAction(element, new NodeAction() {
1140 public void onReady(final DefaultMutableTreeNode node) {
1141 removeLoading(node, false);
1143 }, false);
1145 processNodeActionsIfReady(node);
1147 }).doWhenProcessed(new Runnable() {
1148 public void run() {
1149 myWillBeExpaned.remove(node);
1150 removeFromUpdating(node);
1151 processNodeActionsIfReady(node);
1155 }).doWhenRejected(new Runnable() {
1156 public void run() {
1157 removeFromUpdating(node);
1158 processNodeActionsIfReady(node);
1163 private boolean isDisposed(DefaultMutableTreeNode node) {
1164 return !node.isNodeAncestor((DefaultMutableTreeNode)myTree.getModel().getRoot());
1167 private void expand(DefaultMutableTreeNode node, boolean canSmartExpand) {
1168 expand(new TreePath(node.getPath()), canSmartExpand);
1171 private void expand(final TreePath path, boolean canSmartExpand) {
1172 if (path == null) return;
1175 final Object last = path.getLastPathComponent();
1176 boolean isLeaf = myTree.getModel().isLeaf(path.getLastPathComponent());
1177 final boolean isRoot = last == myTree.getModel().getRoot();
1178 final TreePath parent = path.getParentPath();
1179 if (isRoot && !myTree.isExpanded(path)) {
1180 if (myTree.isRootVisible() || myUnbuiltNodes.contains(last)) {
1181 insertLoadingNode((DefaultMutableTreeNode)last, false);
1183 expandPath(path, canSmartExpand);
1185 else if (myTree.isExpanded(path) || (isLeaf && parent != null && myTree.isExpanded(parent) && !myUnbuiltNodes.contains(last))) {
1186 if (last instanceof DefaultMutableTreeNode) {
1187 processNodeActionsIfReady((DefaultMutableTreeNode)last);
1190 else {
1191 if (isLeaf && myUnbuiltNodes.contains(last)) {
1192 insertLoadingNode((DefaultMutableTreeNode)last, true);
1193 expandPath(path, canSmartExpand);
1195 else if (isLeaf && parent != null) {
1196 final DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode)parent.getLastPathComponent();
1197 if (parentNode != null) {
1198 addToUnbuilt(parentNode);
1200 expandPath(parent, canSmartExpand);
1202 else {
1203 expandPath(path, canSmartExpand);
1208 private void addToUnbuilt(DefaultMutableTreeNode node) {
1209 myUnbuiltNodes.add(node);
1212 private void removeFromUnbuilt(DefaultMutableTreeNode node) {
1213 myUnbuiltNodes.remove(node);
1216 private Pair<Boolean, LoadedChildren> processUnbuilt(final DefaultMutableTreeNode node,
1217 final NodeDescriptor descriptor,
1218 final TreeUpdatePass pass,
1219 boolean isExpanded,
1220 final LoadedChildren loadedChildren) {
1221 if (!isExpanded && getBuilder().isAlwaysShowPlus(descriptor)) {
1222 return new Pair<Boolean, LoadedChildren>(true, null);
1225 final Object element = getElementFor(node);
1227 final LoadedChildren children = loadedChildren != null ? loadedChildren : new LoadedChildren(getChildrenFor(element));
1229 boolean processed;
1231 if (children.getElements().size() == 0) {
1232 removeLoading(node, true);
1233 processed = true;
1235 else {
1236 if (isAutoExpand(node)) {
1237 addNodeAction(getElementFor(node), new NodeAction() {
1238 public void onReady(final DefaultMutableTreeNode node) {
1239 final TreePath path = new TreePath(node.getPath());
1240 if (getTree().isExpanded(path) || children.getElements().size() == 0) {
1241 removeLoading(node, false);
1243 else {
1244 maybeYeild(new ActiveRunnable() {
1245 public ActionCallback run() {
1246 expand(element, null);
1247 return new ActionCallback.Done();
1249 }, pass, node);
1252 }, false);
1254 processed = false;
1257 processNodeActionsIfReady(node);
1259 return new Pair<Boolean, LoadedChildren>(processed, children);
1262 private boolean removeIfLoading(TreeNode node) {
1263 if (isLoadingNode(node)) {
1264 moveSelectionToParentIfNeeded(node);
1265 removeNodeFromParent((MutableTreeNode)node, false);
1266 return true;
1269 return false;
1272 private void moveSelectionToParentIfNeeded(TreeNode node) {
1273 TreePath path = getPathFor(node);
1274 if (myTree.getSelectionModel().isPathSelected(path)) {
1275 TreePath parentPath = path.getParentPath();
1276 myTree.getSelectionModel().removeSelectionPath(path);
1277 if (parentPath != null) {
1278 myTree.getSelectionModel().addSelectionPath(parentPath);
1283 //todo [kirillk] temporary consistency check
1284 private Object[] getChildrenFor(final Object element) {
1285 final Object[] passOne;
1286 try {
1287 passOne = getTreeStructure().getChildElements(element);
1289 catch (IndexNotReadyException e) {
1290 if (!myWasEverIndexNotReady) {
1291 myWasEverIndexNotReady = true;
1292 LOG.warn("Tree is not dumb-mode-aware; treeBuilder=" + getBuilder() + " treeStructure=" + getTreeStructure());
1294 return ArrayUtil.EMPTY_OBJECT_ARRAY;
1297 if (!myCheckStructure) return passOne;
1299 final Object[] passTwo = getTreeStructure().getChildElements(element);
1301 final HashSet two = new HashSet(Arrays.asList(passTwo));
1303 if (passOne.length != passTwo.length) {
1304 LOG.error(
1305 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
1306 element);
1308 else {
1309 for (Object eachInOne : passOne) {
1310 if (!two.contains(eachInOne)) {
1311 LOG.error(
1312 "AbstractTreeStructure.getChildren() must either provide same objects or new objects but with correct hashCode() and equals() methods. Wrong parent element=" +
1313 element);
1314 break;
1319 return passOne;
1322 private void updateNodesToInsert(final ArrayList<TreeNode> nodesToInsert,
1323 TreeUpdatePass pass,
1324 boolean canSmartExpand,
1325 boolean forceUpdate) {
1326 for (TreeNode aNodesToInsert : nodesToInsert) {
1327 DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)aNodesToInsert;
1328 updateNodeChildren(childNode, pass, null, false, canSmartExpand, forceUpdate, true);
1332 private ActionCallback processExistingNodes(final DefaultMutableTreeNode node,
1333 final MutualMap<Object, Integer> elementToIndexMap,
1334 final TreeUpdatePass pass,
1335 final boolean canSmartExpand,
1336 final boolean forceUpdate,
1337 final boolean wasExpaned,
1338 final LoadedChildren preloaded) {
1340 final ArrayList<TreeNode> childNodes = TreeUtil.childrenToArray(node);
1341 return maybeYeild(new ActiveRunnable() {
1342 public ActionCallback run() {
1343 if (pass.isExpired()) return new ActionCallback.Rejected();
1344 if (childNodes.size() == 0) return new ActionCallback.Done();
1347 final ActionCallback result = new ActionCallback(childNodes.size());
1349 for (TreeNode each : childNodes) {
1350 final DefaultMutableTreeNode eachChild = (DefaultMutableTreeNode)each;
1351 if (isLoadingNode(eachChild)) {
1352 result.setDone();
1353 continue;
1356 final boolean childForceUpdate = isChildNodeForceUpdate(eachChild, forceUpdate, wasExpaned);
1358 maybeYeild(new ActiveRunnable() {
1359 @Override
1360 public ActionCallback run() {
1361 return processExistingNode(eachChild, getDescriptorFrom(eachChild), node, elementToIndexMap, pass, canSmartExpand,
1362 childForceUpdate, preloaded);
1364 }, pass, node).notify(result);
1366 if (result.isRejected()) {
1367 break;
1371 return result;
1373 }, pass, node);
1376 private boolean isRerunNeeded(TreeUpdatePass pass) {
1377 if (pass.isExpired()) return false;
1379 final boolean rerunBecauseTreeIsHidden = !pass.isExpired() && !isTreeShowing() && getUpdater().isInPostponeMode();
1381 return rerunBecauseTreeIsHidden || getUpdater().isRerunNeededFor(pass);
1384 private ActionCallback maybeYeild(final ActiveRunnable processRunnable, final TreeUpdatePass pass, final DefaultMutableTreeNode node) {
1385 final ActionCallback result = new ActionCallback();
1387 if (isRerunNeeded(pass)) {
1388 getUpdater().addSubtreeToUpdate(pass);
1389 result.setRejected();
1391 else {
1392 if (isToYieldUpdateFor(node)) {
1393 pass.setCurrentNode(node);
1394 boolean wasRun = yieldAndRun(new Runnable() {
1395 public void run() {
1396 if (validateReleaseRequested()) {
1397 result.setRejected();
1398 return;
1401 if (pass.isExpired()) {
1402 result.setRejected();
1403 return;
1406 if (isRerunNeeded(pass)) {
1407 runDone(new Runnable() {
1408 public void run() {
1409 if (!pass.isExpired()) {
1410 getUpdater().addSubtreeToUpdate(pass);
1414 result.setRejected();
1416 else {
1417 try {
1418 processRunnable.run().notify(result);
1420 catch (ProcessCanceledException e) {
1421 pass.expire();
1422 result.setRejected();
1426 }, pass);
1427 if (!wasRun) {
1428 result.setRejected();
1431 else {
1432 try {
1433 processRunnable.run().notify(result);
1435 catch (ProcessCanceledException e) {
1436 pass.expire();
1437 result.setRejected();
1442 return result;
1445 private boolean yieldAndRun(final Runnable runnable, final TreeUpdatePass pass) {
1446 if (validateReleaseRequested()) return false;
1448 myYeildingPasses.add(pass);
1449 myYeildingNow = true;
1450 yield(new Runnable() {
1451 public void run() {
1452 runOnYieldingDone(new Runnable() {
1453 public void run() {
1454 executeYieldingRequest(runnable, pass);
1460 return true;
1463 public boolean isYeildingNow() {
1464 return myYeildingNow;
1467 private boolean hasSheduledUpdates() {
1468 return getUpdater().hasNodesToUpdate() || isLoadingInBackgroundNow();
1471 public boolean isReady() {
1472 return isIdle() && !hasPendingWork() && !isNodeActionsPending();
1475 public boolean hasPendingWork() {
1476 return hasNodesToUpdate() || (myUpdaterState != null && myUpdaterState.isProcessingNow());
1479 public boolean isIdle() {
1480 return !isYeildingNow() && !isWorkerBusy() && (!hasSheduledUpdates() || getUpdater().isInPostponeMode());
1483 private void executeYieldingRequest(Runnable runnable, TreeUpdatePass pass) {
1484 try {
1485 myYeildingPasses.remove(pass);
1486 runnable.run();
1488 finally {
1489 maybeYeildingFinished();
1493 private void maybeYeildingFinished() {
1494 if (myYeildingPasses.size() == 0) {
1495 myYeildingNow = false;
1496 flushPendingNodeActions();
1500 void maybeReady() {
1501 if (isReleased()) return;
1503 if (isReady()) {
1504 if (isReleaseRequested()) {
1505 releaseNow();
1506 return;
1509 if (myTree.isShowing() || myUpdateIfInactive) {
1510 myInitialized.setDone();
1514 if (myUpdaterState != null && !myUpdaterState.isProcessingNow()) {
1515 UpdaterTreeState oldState = myUpdaterState;
1516 if (!myUpdaterState.restore(null)) {
1517 setUpdaterState(oldState);
1520 if (!isReady()) {
1521 return;
1525 if (myTree.isShowing()) {
1526 if (getBuilder().isToEnsureSelectionOnFocusGained() && Registry.is("ide.tree.ensureSelectionOnFocusGained")) {
1527 TreeUtil.ensureSelection(myTree);
1531 if (myInitialized.isDone()) {
1532 for (ActionCallback each : getReadyCallbacks(true)) {
1533 each.setDone();
1539 private void flushPendingNodeActions() {
1540 final DefaultMutableTreeNode[] nodes = myPendingNodeActions.toArray(new DefaultMutableTreeNode[myPendingNodeActions.size()]);
1541 myPendingNodeActions.clear();
1543 for (DefaultMutableTreeNode each : nodes) {
1544 processNodeActionsIfReady(each);
1547 final Runnable[] actions = myYeildingDoneRunnables.toArray(new Runnable[myYeildingDoneRunnables.size()]);
1548 for (Runnable each : actions) {
1549 if (!isYeildingNow()) {
1550 myYeildingDoneRunnables.remove(each);
1551 each.run();
1555 maybeReady();
1558 protected void runOnYieldingDone(Runnable onDone) {
1559 getBuilder().runOnYeildingDone(onDone);
1562 protected void yield(Runnable runnable) {
1563 getBuilder().yield(runnable);
1566 private boolean isToYieldUpdateFor(final DefaultMutableTreeNode node) {
1567 if (!canYield()) return false;
1568 return getBuilder().isToYieldUpdateFor(node);
1571 private MutualMap<Object, Integer> loadElementsFromStructure(final NodeDescriptor descriptor,
1572 @Nullable LoadedChildren preloadedChildren) {
1573 MutualMap<Object, Integer> elementToIndexMap = new MutualMap<Object, Integer>(true);
1574 List children = preloadedChildren != null
1575 ? preloadedChildren.getElements()
1576 : Arrays.asList(getChildrenFor(getBuilder().getTreeStructureElement(descriptor)));
1577 int index = 0;
1578 for (Object child : children) {
1579 if (!isValid(child)) continue;
1580 elementToIndexMap.put(child, Integer.valueOf(index));
1581 index++;
1583 return elementToIndexMap;
1586 private void expand(final DefaultMutableTreeNode node,
1587 final NodeDescriptor descriptor,
1588 final boolean wasLeaf,
1589 final boolean canSmartExpand) {
1590 final Alarm alarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
1591 alarm.addRequest(new Runnable() {
1592 public void run() {
1593 myTree.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1595 }, WAIT_CURSOR_DELAY);
1597 if (wasLeaf && isAutoExpand(descriptor)) {
1598 expand(node, canSmartExpand);
1601 ArrayList<TreeNode> nodes = TreeUtil.childrenToArray(node);
1602 for (TreeNode node1 : nodes) {
1603 final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)node1;
1604 if (isLoadingNode(childNode)) continue;
1605 NodeDescriptor childDescr = getDescriptorFrom(childNode);
1606 if (isAutoExpand(childDescr)) {
1607 addNodeAction(getElementFor(childNode), new NodeAction() {
1608 public void onReady(DefaultMutableTreeNode node) {
1609 expand(childNode, canSmartExpand);
1611 }, false);
1612 addSubtreeToUpdate(childNode);
1616 int n = alarm.cancelAllRequests();
1617 if (n == 0) {
1618 myTree.setCursor(Cursor.getDefaultCursor());
1622 public static boolean isLoadingNode(final Object node) {
1623 return node instanceof LoadingNode;
1626 private AsyncResult<ArrayList<TreeNode>> collectNodesToInsert(final NodeDescriptor descriptor,
1627 final MutualMap<Object, Integer> elementToIndexMap,
1628 final DefaultMutableTreeNode parent,
1629 final boolean addLoadingNode,
1630 @NotNull final LoadedChildren loadedChildren) {
1631 final AsyncResult<ArrayList<TreeNode>> result = new AsyncResult<ArrayList<TreeNode>>();
1633 final ArrayList<TreeNode> nodesToInsert = new ArrayList<TreeNode>();
1634 final Collection<Object> allElements = elementToIndexMap.getKeys();
1636 final ActionCallback processingDone = new ActionCallback(allElements.size());
1638 for (final Object child : allElements) {
1639 Integer index = elementToIndexMap.getValue(child);
1640 final Ref<NodeDescriptor> childDescr = new Ref<NodeDescriptor>(loadedChildren.getDescriptor(child));
1641 boolean needToUpdate = false;
1642 if (childDescr.get() == null) {
1643 childDescr.set(getTreeStructure().createDescriptor(child, descriptor));
1644 needToUpdate = true;
1647 if (childDescr.get() == null) {
1648 processingDone.setDone();
1649 continue;
1651 childDescr.get().setIndex(index.intValue());
1653 final ActionCallback update = new ActionCallback();
1654 if (needToUpdate) {
1655 update(childDescr.get(), false).doWhenDone(new AsyncResult.Handler<Boolean>() {
1656 public void run(Boolean changes) {
1657 loadedChildren.putDescriptor(child, childDescr.get(), changes);
1658 update.setDone();
1662 else {
1663 update.setDone();
1666 update.doWhenDone(new Runnable() {
1667 public void run() {
1668 Object element = getElementFromDescriptor(childDescr.get());
1669 if (element == null) {
1670 processingDone.setDone();
1672 else {
1673 DefaultMutableTreeNode node = getNodeForElement(element, false);
1674 if (node == null || node.getParent() != parent) {
1675 final DefaultMutableTreeNode childNode = createChildNode(childDescr.get());
1676 if (addLoadingNode || getBuilder().isAlwaysShowPlus(childDescr.get())) {
1677 insertLoadingNode(childNode, true);
1679 else {
1680 addToUnbuilt(childNode);
1682 nodesToInsert.add(childNode);
1683 createMapping(element, childNode);
1685 processingDone.setDone();
1691 processingDone.doWhenDone(new Runnable() {
1692 public void run() {
1693 result.setDone(nodesToInsert);
1697 return result;
1700 protected DefaultMutableTreeNode createChildNode(final NodeDescriptor descriptor) {
1701 return new ElementNode(this, descriptor);
1704 protected boolean canYield() {
1705 return myCanYield && myYeildingUpdate.asBoolean();
1708 public long getClearOnHideDelay() {
1709 return myClearOnHideDelay > 0 ? myClearOnHideDelay : Registry.intValue("ide.tree.clearOnHideTime");
1712 public ActionCallback getInitialized() {
1713 return myInitialized;
1716 public ActionCallback getReady(Object requestor) {
1717 if (isReady()) {
1718 return new ActionCallback.Done();
1720 else {
1721 return addReadyCallback(requestor);
1725 private void addToUpdating(DefaultMutableTreeNode node) {
1726 synchronized (myUpdatingChildren) {
1727 myUpdatingChildren.add(node);
1731 private void removeFromUpdating(DefaultMutableTreeNode node) {
1732 synchronized (myUpdatingChildren) {
1733 myUpdatingChildren.remove(node);
1737 public boolean isUpdatingNow(DefaultMutableTreeNode node) {
1738 synchronized (myUpdatingChildren) {
1739 return myUpdatingChildren.contains(node);
1743 boolean hasUpdatingNow() {
1744 synchronized (myUpdatingChildren) {
1745 return myUpdatingChildren.size() > 0;
1749 public Map getNodeActions() {
1750 return myNodeActions;
1753 public List<Object> getLoadedChildrenFor(Object element) {
1754 List<Object> result = new ArrayList<Object>();
1756 DefaultMutableTreeNode node = (DefaultMutableTreeNode)getNodeForElement(element, false);
1757 if (node != null) {
1758 for (int i = 0; i < node.getChildCount(); i++) {
1759 TreeNode each = node.getChildAt(i);
1760 if (isLoadingNode(each)) continue;
1762 result.add(getElementFor(each));
1766 return result;
1769 public boolean hasNodesToUpdate() {
1770 return getUpdater().hasNodesToUpdate() || hasUpdatingNow() || isLoadingInBackgroundNow();
1773 public List<Object> getExpandedElements() {
1774 List<Object> result = new ArrayList<Object>();
1775 Enumeration<TreePath> enumeration = myTree.getExpandedDescendants(getPathFor(getRootNode()));
1776 while (enumeration.hasMoreElements()) {
1777 TreePath each = enumeration.nextElement();
1778 Object eachElement = getElementFor(each.getLastPathComponent());
1779 if (eachElement != null) {
1780 result.add(eachElement);
1784 return result;
1787 public ProgressIndicator getProgressIndicator() {
1788 return myProgress;
1791 static class ElementNode extends DefaultMutableTreeNode {
1793 Set<Object> myElements = new HashSet<Object>();
1794 AbstractTreeUi myUi;
1796 ElementNode(AbstractTreeUi ui, NodeDescriptor descriptor) {
1797 super(descriptor);
1798 myUi = ui;
1801 @Override
1802 public void insert(final MutableTreeNode newChild, final int childIndex) {
1803 super.insert(newChild, childIndex);
1804 final Object element = myUi.getElementFor(newChild);
1805 if (element != null) {
1806 myElements.add(element);
1810 @Override
1811 public void remove(final int childIndex) {
1812 final TreeNode node = getChildAt(childIndex);
1813 super.remove(childIndex);
1814 final Object element = myUi.getElementFor(node);
1815 if (element != null) {
1816 myElements.remove(element);
1820 boolean isValidChild(Object childElement) {
1821 return myElements.contains(childElement);
1824 @Override
1825 public String toString() {
1826 return String.valueOf(getUserObject());
1830 private boolean isUpdatingParent(DefaultMutableTreeNode kid) {
1831 return getUpdatingParent(kid) != null;
1834 private DefaultMutableTreeNode getUpdatingParent(DefaultMutableTreeNode kid) {
1835 DefaultMutableTreeNode eachParent = kid;
1836 while (eachParent != null) {
1837 if (isUpdatingNow(eachParent)) return eachParent;
1838 eachParent = (DefaultMutableTreeNode)eachParent.getParent();
1841 return null;
1844 private boolean isLoadedInBackground(Object element) {
1845 return getLoadedInBackground(element) != null;
1848 private UpdateInfo getLoadedInBackground(Object element) {
1849 synchronized (myLoadedInBackground) {
1850 return myLoadedInBackground.get(element);
1854 private void addToLoadedInBackground(Object element, UpdateInfo info) {
1855 synchronized (myLoadedInBackground) {
1856 myLoadedInBackground.put(element, info);
1860 private void removeFromLoadedInBackground(final Object element) {
1861 synchronized (myLoadedInBackground) {
1862 myLoadedInBackground.remove(element);
1866 private boolean isLoadingInBackgroundNow() {
1867 synchronized (myLoadedInBackground) {
1868 return myLoadedInBackground.size() > 0;
1872 private boolean queueBackgroundUpdate(final UpdateInfo updateInfo, final DefaultMutableTreeNode node) {
1873 assertIsDispatchThread();
1875 if (validateReleaseRequested()) return false;
1877 final Object oldElementFromDescriptor = getElementFromDescriptor(updateInfo.getDescriptor());
1879 UpdateInfo loaded = getLoadedInBackground(oldElementFromDescriptor);
1880 if (loaded != null) {
1881 loaded.apply(updateInfo);
1882 return false;
1885 addToLoadedInBackground(oldElementFromDescriptor, updateInfo);
1887 if (!isNodeBeingBuilt(node)) {
1888 LoadingNode loadingNode = new LoadingNode(getLoadingNodeText());
1889 myTreeModel.insertNodeInto(loadingNode, node, node.getChildCount());
1892 final Ref<LoadedChildren> children = new Ref<LoadedChildren>();
1893 final Ref<Object> elementFromDescriptor = new Ref<Object>();
1895 final DefaultMutableTreeNode[] nodeToProcessActions = new DefaultMutableTreeNode[1];
1897 final Runnable finalizeRunnable = new Runnable() {
1898 public void run() {
1899 removeLoading(node, true);
1900 removeFromLoadedInBackground(elementFromDescriptor.get());
1901 removeFromLoadedInBackground(oldElementFromDescriptor);
1903 if (nodeToProcessActions[0] != null) {
1904 processNodeActionsIfReady(nodeToProcessActions[0]);
1910 Runnable buildRunnable = new Runnable() {
1911 public void run() {
1912 if (updateInfo.getPass().isExpired()) {
1913 finalizeRunnable.run();
1914 return;
1918 if (!updateInfo.isDescriptorIsUpToDate()) {
1919 update(updateInfo.getDescriptor(), true);
1922 Object element = getElementFromDescriptor(updateInfo.getDescriptor());
1923 if (element == null) {
1924 removeFromLoadedInBackground(oldElementFromDescriptor);
1925 finalizeRunnable.run();
1926 return;
1929 elementFromDescriptor.set(element);
1931 Object[] loadedElements = getChildrenFor(getBuilder().getTreeStructureElement(updateInfo.getDescriptor()));
1932 LoadedChildren loaded = new LoadedChildren(loadedElements);
1933 for (Object each : loadedElements) {
1934 NodeDescriptor eachChildDescriptor = getTreeStructure().createDescriptor(each, updateInfo.getDescriptor());
1935 loaded.putDescriptor(each, eachChildDescriptor, update(eachChildDescriptor, true).getResult());
1938 children.set(loaded);
1942 Runnable updateRunnable = new Runnable() {
1943 public void run() {
1944 if (updateInfo.getPass().isExpired()) {
1945 finalizeRunnable.run();
1946 return;
1949 if (children.get() == null) {
1950 finalizeRunnable.run();
1951 return;
1954 if (isRerunNeeded(updateInfo.getPass())) {
1955 removeFromLoadedInBackground(elementFromDescriptor.get());
1956 getUpdater().addSubtreeToUpdate(updateInfo.getPass());
1957 return;
1960 removeFromLoadedInBackground(elementFromDescriptor.get());
1962 if (myUnbuiltNodes.contains(node)) {
1963 Pair<Boolean, LoadedChildren> unbuilt =
1964 processUnbuilt(node, updateInfo.getDescriptor(), updateInfo.getPass(), isExpanded(node, updateInfo.isWasExpanded()),
1965 children.get());
1966 if (unbuilt.getFirst()) {
1967 nodeToProcessActions[0] = node;
1968 return;
1972 updateNodeChildren(node, updateInfo.getPass(), children.get(), true, updateInfo.isCanSmartExpand(), updateInfo.isForceUpdate(),
1973 true);
1976 if (isRerunNeeded(updateInfo.getPass())) {
1977 getUpdater().addSubtreeToUpdate(updateInfo.getPass());
1978 return;
1981 Object element = elementFromDescriptor.get();
1983 if (element != null) {
1984 removeLoading(node, true);
1985 nodeToProcessActions[0] = node;
1989 queueToBackground(buildRunnable, updateRunnable).doWhenProcessed(finalizeRunnable).doWhenRejected(new Runnable() {
1990 public void run() {
1991 updateInfo.getPass().expire();
1995 return true;
1998 private boolean isExpanded(DefaultMutableTreeNode node, boolean isExpanded) {
1999 return isExpanded || myTree.isExpanded(getPathFor(node));
2002 private void removeLoading(DefaultMutableTreeNode parent, boolean removeFromUnbuilt) {
2003 for (int i = 0; i < parent.getChildCount(); i++) {
2004 TreeNode child = parent.getChildAt(i);
2005 if (removeIfLoading(child)) {
2006 i--;
2010 if (removeFromUnbuilt) {
2011 removeFromUnbuilt(parent);
2014 if (parent == getRootNode() && !myTree.isRootVisible() && parent.getChildCount() == 0) {
2015 insertLoadingNode(parent, false);
2018 maybeReady();
2021 private void processNodeActionsIfReady(final DefaultMutableTreeNode node) {
2022 assertIsDispatchThread();
2024 if (isNodeBeingBuilt(node)) return;
2026 final Object o = node.getUserObject();
2027 if (!(o instanceof NodeDescriptor)) return;
2030 if (isYeildingNow()) {
2031 myPendingNodeActions.add(node);
2032 return;
2035 final Object element = getBuilder().getTreeStructureElement((NodeDescriptor)o);
2037 boolean childrenReady = !isLoadedInBackground(element);
2039 processActions(node, element, myNodeActions, childrenReady ? myNodeChildrenActions : null);
2040 if (childrenReady) {
2041 processActions(node, element, myNodeChildrenActions, null);
2044 if (!isUpdatingParent(node) && !isWorkerBusy()) {
2045 final UpdaterTreeState state = myUpdaterState;
2046 if (myNodeActions.size() == 0 && state != null && !state.isProcessingNow()) {
2047 if (!state.restore(childrenReady ? node : null)) {
2048 setUpdaterState(state);
2053 maybeReady();
2057 private void processActions(DefaultMutableTreeNode node, Object element, final Map<Object, List<NodeAction>> nodeActions, @Nullable final Map<Object, List<NodeAction>> secondaryNodeAction) {
2058 final List<NodeAction> actions = nodeActions.get(element);
2059 if (actions != null) {
2060 nodeActions.remove(element);
2062 List<NodeAction> secondary = secondaryNodeAction != null ? secondaryNodeAction.get(element) : null;
2063 for (NodeAction each : actions) {
2064 if (secondary != null && secondary.contains(each)) {
2065 secondary.remove(each);
2067 each.onReady(node);
2073 private boolean canSmartExpand(DefaultMutableTreeNode node, boolean canSmartExpand) {
2074 if (!getBuilder().isSmartExpand()) return false;
2076 boolean smartExpand = !myNotForSmartExpand.contains(node) && canSmartExpand;
2077 return smartExpand ? validateAutoExpand(smartExpand, getElementFor(node)) : false;
2080 private void processSmartExpand(final DefaultMutableTreeNode node, final boolean canSmartExpand, boolean forced) {
2081 if (!getBuilder().isSmartExpand()) return;
2083 boolean can = canSmartExpand(node, canSmartExpand);
2085 if (!can && !forced) return;
2087 if (isNodeBeingBuilt(node) && !forced) {
2088 addNodeAction(getElementFor(node), new NodeAction() {
2089 public void onReady(DefaultMutableTreeNode node) {
2090 processSmartExpand(node, canSmartExpand, true);
2092 }, true);
2094 else {
2095 TreeNode child = getChildForSmartExpand(node);
2096 if (child != null) {
2097 final TreePath childPath = new TreePath(node.getPath()).pathByAddingChild(child);
2098 processInnerChange(new Runnable() {
2099 public void run() {
2100 myTree.expandPath(childPath);
2107 @Nullable
2108 private TreeNode getChildForSmartExpand(DefaultMutableTreeNode node) {
2109 int realChildCount = 0;
2110 TreeNode nodeToExpand = null;
2112 for (int i = 0; i < node.getChildCount(); i++) {
2113 TreeNode eachChild = node.getChildAt(i);
2115 if (!isLoadingNode(eachChild)) {
2116 realChildCount++;
2117 if (nodeToExpand == null) {
2118 nodeToExpand = eachChild;
2122 if (realChildCount > 1) {
2123 nodeToExpand = null;
2124 break;
2128 return nodeToExpand;
2131 public boolean isLoadingChildrenFor(final Object nodeObject) {
2132 if (!(nodeObject instanceof DefaultMutableTreeNode)) return false;
2134 DefaultMutableTreeNode node = (DefaultMutableTreeNode)nodeObject;
2136 int loadingNodes = 0;
2137 for (int i = 0; i < Math.min(node.getChildCount(), 2); i++) {
2138 TreeNode child = node.getChildAt(i);
2139 if (isLoadingNode(child)) {
2140 loadingNodes++;
2143 return loadingNodes > 0 && loadingNodes == node.getChildCount();
2146 private boolean isParentLoading(Object nodeObject) {
2147 return getParentLoading(nodeObject) != null;
2150 private DefaultMutableTreeNode getParentLoading(Object nodeObject) {
2151 if (!(nodeObject instanceof DefaultMutableTreeNode)) return null;
2153 DefaultMutableTreeNode node = (DefaultMutableTreeNode)nodeObject;
2155 TreeNode eachParent = node.getParent();
2157 while (eachParent != null) {
2158 eachParent = eachParent.getParent();
2159 if (eachParent instanceof DefaultMutableTreeNode) {
2160 final Object eachElement = getElementFor((DefaultMutableTreeNode)eachParent);
2161 if (isLoadedInBackground(eachElement)) return (DefaultMutableTreeNode)eachParent;
2165 return null;
2168 protected String getLoadingNodeText() {
2169 return IdeBundle.message("progress.searching");
2172 private ActionCallback processExistingNode(final DefaultMutableTreeNode childNode,
2173 final NodeDescriptor childDescriptor,
2174 final DefaultMutableTreeNode parentNode,
2175 final MutualMap<Object, Integer> elementToIndexMap,
2176 final TreeUpdatePass pass,
2177 final boolean canSmartExpand,
2178 final boolean forceUpdate,
2179 LoadedChildren parentPreloadedChildren) {
2181 final ActionCallback result = new ActionCallback();
2183 if (pass.isExpired()) {
2184 return new ActionCallback.Rejected();
2187 final Ref<NodeDescriptor> childDesc = new Ref<NodeDescriptor>(childDescriptor);
2189 if (childDesc.get() == null) {
2190 pass.expire();
2191 return new ActionCallback.Rejected();
2193 final Object oldElement = getElementFromDescriptor(childDesc.get());
2194 if (oldElement == null) {
2195 pass.expire();
2196 return new ActionCallback.Rejected();
2199 AsyncResult<Boolean> update = new AsyncResult<Boolean>();
2200 if (parentPreloadedChildren != null && parentPreloadedChildren.getDescriptor(oldElement) != null) {
2201 update.setDone(parentPreloadedChildren.isUpdated(oldElement));
2203 else {
2204 update = update(childDesc.get(), false);
2207 update.doWhenDone(new AsyncResult.Handler<Boolean>() {
2208 public void run(Boolean isChanged) {
2209 final Ref<Boolean> changes = new Ref<Boolean>(isChanged);
2211 final Ref<Boolean> forceRemapping = new Ref<Boolean>(false);
2212 final Ref<Object> newElement = new Ref<Object>(getElementFromDescriptor(childDesc.get()));
2214 final Integer index = newElement.get() != null ? elementToIndexMap.getValue(getBuilder().getTreeStructureElement(childDesc.get())) : null;
2215 final AsyncResult<Boolean> updateIndexDone = new AsyncResult<Boolean>();
2216 final ActionCallback indexReady = new ActionCallback();
2217 if (index != null) {
2218 final Object elementFromMap = elementToIndexMap.getKey(index);
2219 if (elementFromMap != newElement.get() && elementFromMap.equals(newElement.get())) {
2220 if (isInStructure(elementFromMap) && isInStructure(newElement.get())) {
2221 if (parentNode.getUserObject() instanceof NodeDescriptor) {
2222 final NodeDescriptor parentDescriptor = getDescriptorFrom(parentNode);
2223 childDesc.set(getTreeStructure().createDescriptor(elementFromMap, parentDescriptor));
2224 childNode.setUserObject(childDesc.get());
2225 newElement.set(elementFromMap);
2226 forceRemapping.set(true);
2227 update(childDesc.get(), false).doWhenDone(new AsyncResult.Handler<Boolean>() {
2228 public void run(Boolean isChanged) {
2229 changes.set(isChanged);
2230 updateIndexDone.setDone(isChanged);
2235 else {
2236 updateIndexDone.setDone(changes.get());
2238 } else {
2239 updateIndexDone.setDone(changes.get());
2242 updateIndexDone.doWhenDone(new Runnable() {
2243 public void run() {
2244 if (childDesc.get().getIndex() != index.intValue()) {
2245 changes.set(true);
2247 childDesc.get().setIndex(index.intValue());
2248 indexReady.setDone();
2252 else {
2253 updateIndexDone.setDone();
2256 updateIndexDone.doWhenDone(new Runnable() {
2257 public void run() {
2258 if (index != null && changes.get()) {
2259 updateNodeImageAndPosition(childNode, false);
2261 if (!oldElement.equals(newElement.get()) | forceRemapping.get()) {
2262 removeMapping(oldElement, childNode, newElement.get());
2263 if (newElement.get() != null) {
2264 createMapping(newElement.get(), childNode);
2266 NodeDescriptor parentDescriptor = getDescriptorFrom(parentNode);
2267 if (parentDescriptor != null) {
2268 parentDescriptor.setChildrenSortingStamp(-1);
2272 if (index == null) {
2273 int selectedIndex = -1;
2274 if (TreeBuilderUtil.isNodeOrChildSelected(myTree, childNode)) {
2275 selectedIndex = parentNode.getIndex(childNode);
2278 if (childNode.getParent() instanceof DefaultMutableTreeNode) {
2279 final DefaultMutableTreeNode parent = (DefaultMutableTreeNode)childNode.getParent();
2280 if (myTree.isExpanded(new TreePath(parent.getPath()))) {
2281 if (parent.getChildCount() == 1 && parent.getChildAt(0) == childNode) {
2282 insertLoadingNode(parent, false);
2287 Object disposedElement = getElementFor(childNode);
2289 removeNodeFromParent(childNode, selectedIndex >= 0);
2290 disposeNode(childNode);
2292 adjustSelectionOnChildRemove(parentNode, selectedIndex, disposedElement);
2294 else {
2295 elementToIndexMap.remove(getBuilder().getTreeStructureElement(childDesc.get()));
2296 updateNodeChildren(childNode, pass, null, false, canSmartExpand, forceUpdate, true);
2299 if (parentNode.equals(getRootNode())) {
2300 myTreeModel.nodeChanged(getRootNode());
2303 result.setDone();
2310 return result;
2313 private void adjustSelectionOnChildRemove(DefaultMutableTreeNode parentNode, int selectedIndex, Object disposedElement) {
2314 DefaultMutableTreeNode node = getNodeForElement(disposedElement, false);
2315 if (node != null && isValidForSelectionAdjusting(node)) {
2316 Object newElement = getElementFor(node);
2317 addSelectionPath(getPathFor(node), true, getExpiredElementCondition(newElement), disposedElement);
2318 return;
2322 if (selectedIndex >= 0) {
2323 if (parentNode.getChildCount() > 0) {
2324 if (parentNode.getChildCount() > selectedIndex) {
2325 TreeNode newChildNode = parentNode.getChildAt(selectedIndex);
2326 if (isValidForSelectionAdjusting(newChildNode)) {
2327 addSelectionPath(new TreePath(myTreeModel.getPathToRoot(newChildNode)), true, getExpiredElementCondition(disposedElement), disposedElement);
2330 else {
2331 TreeNode newChild = parentNode.getChildAt(parentNode.getChildCount() - 1);
2332 if (isValidForSelectionAdjusting(newChild)) {
2333 addSelectionPath(new TreePath(myTreeModel.getPathToRoot(newChild)), true, getExpiredElementCondition(disposedElement), disposedElement);
2337 else {
2338 addSelectionPath(new TreePath(myTreeModel.getPathToRoot(parentNode)), true, getExpiredElementCondition(disposedElement), disposedElement);
2343 private boolean isValidForSelectionAdjusting(TreeNode node) {
2344 if (!myTree.isRootVisible() && getRootNode() == node) return false;
2346 if (isLoadingNode(node)) return true;
2348 final Object elementInTree = getElementFor(node);
2349 if (elementInTree == null) return false;
2351 final TreeNode parentNode = node.getParent();
2352 final Object parentElementInTree = getElementFor(parentNode);
2353 if (parentElementInTree == null) return false;
2355 final Object parentElement = getTreeStructure().getParentElement(elementInTree);
2357 return parentElementInTree.equals(parentElement);
2360 public Condition getExpiredElementCondition(final Object element) {
2361 return new Condition() {
2362 public boolean value(final Object o) {
2363 return isInStructure(element);
2368 private void addSelectionPath(final TreePath path, final boolean isAdjustedSelection, final Condition isExpiredAdjustement, @Nullable final Object adjustmentCause) {
2369 processInnerChange(new Runnable() {
2370 public void run() {
2371 TreePath toSelect = null;
2373 if (isLoadingNode(path.getLastPathComponent())) {
2374 final TreePath parentPath = path.getParentPath();
2375 if (parentPath != null) {
2376 if (isValidForSelectionAdjusting((TreeNode)parentPath.getLastPathComponent())) {
2377 toSelect = parentPath;
2379 else {
2380 toSelect = null;
2384 else {
2385 toSelect = path;
2388 if (toSelect != null) {
2389 myTree.addSelectionPath(toSelect);
2391 if (isAdjustedSelection && myUpdaterState != null) {
2392 final Object toSelectElement = getElementFor(toSelect.getLastPathComponent());
2393 myUpdaterState.addAdjustedSelection(toSelectElement, isExpiredAdjustement, adjustmentCause);
2400 private static TreePath getPathFor(TreeNode node) {
2401 if (node instanceof DefaultMutableTreeNode) {
2402 return new TreePath(((DefaultMutableTreeNode)node).getPath());
2404 else {
2405 ArrayList nodes = new ArrayList();
2406 TreeNode eachParent = node;
2407 while (eachParent != null) {
2408 nodes.add(eachParent);
2409 eachParent = eachParent.getParent();
2412 return new TreePath(ArrayUtil.toObjectArray(nodes));
2417 private void removeNodeFromParent(final MutableTreeNode node, final boolean willAdjustSelection) {
2418 processInnerChange(new Runnable() {
2419 public void run() {
2420 if (willAdjustSelection) {
2421 final TreePath path = getPathFor(node);
2422 if (myTree.isPathSelected(path)) {
2423 myTree.removeSelectionPath(path);
2427 if (node.getParent() != null) {
2428 myTreeModel.removeNodeFromParent(node);
2434 private void expandPath(final TreePath path, final boolean canSmartExpand) {
2435 processInnerChange(new Runnable() {
2436 public void run() {
2437 if (path.getLastPathComponent() instanceof DefaultMutableTreeNode) {
2438 DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
2439 if (node.getChildCount() > 0 && !myTree.isExpanded(path)) {
2440 if (!canSmartExpand) {
2441 myNotForSmartExpand.add(node);
2443 try {
2444 myRequestedExpand = path;
2445 myTree.expandPath(path);
2446 processSmartExpand(node, canSmartExpand, false);
2448 finally {
2449 myNotForSmartExpand.remove(node);
2450 myRequestedExpand = null;
2453 else {
2454 processNodeActionsIfReady(node);
2461 private void processInnerChange(Runnable runnable) {
2462 if (myUpdaterState == null) {
2463 setUpdaterState(new UpdaterTreeState(this));
2466 myUpdaterState.process(runnable);
2469 private boolean isInnerChange() {
2470 return myUpdaterState != null && myUpdaterState.isProcessingNow();
2473 protected boolean doUpdateNodeDescriptor(final NodeDescriptor descriptor) {
2474 return descriptor.update();
2477 private void makeLoadingOrLeafIfNoChildren(final DefaultMutableTreeNode node) {
2478 TreePath path = getPathFor(node);
2479 if (path == null) return;
2481 insertLoadingNode(node, true);
2483 final NodeDescriptor descriptor = getDescriptorFrom(node);
2484 if (descriptor == null) return;
2486 descriptor.setChildrenSortingStamp(-1);
2488 if (getBuilder().isAlwaysShowPlus(descriptor)) return;
2491 TreePath parentPath = path.getParentPath();
2492 if (myTree.isVisible(path) || (parentPath != null && myTree.isExpanded(parentPath))) {
2493 if (myTree.isExpanded(path)) {
2494 addSubtreeToUpdate(node);
2496 else {
2497 insertLoadingNode(node, false);
2503 private boolean isValid(DefaultMutableTreeNode node) {
2504 if (node == null) return false;
2505 final Object object = node.getUserObject();
2506 if (object instanceof NodeDescriptor) {
2507 return isValid((NodeDescriptor)object);
2510 return false;
2513 private boolean isValid(NodeDescriptor descriptor) {
2514 if (descriptor == null) return false;
2515 return isValid(getElementFromDescriptor(descriptor));
2518 private boolean isValid(Object element) {
2519 if (element instanceof ValidateableNode) {
2520 if (!((ValidateableNode)element).isValid()) return false;
2522 return getBuilder().validateNode(element);
2525 private void insertLoadingNode(final DefaultMutableTreeNode node, boolean addToUnbuilt) {
2526 if (!isLoadingChildrenFor(node)) {
2527 myTreeModel.insertNodeInto(new LoadingNode(), node, 0);
2530 if (addToUnbuilt) {
2531 addToUnbuilt(node);
2536 protected ActionCallback queueToBackground(@NotNull final Runnable bgBuildAction,
2537 @Nullable final Runnable edtPostRunnable) {
2538 if (validateReleaseRequested()) return new ActionCallback.Rejected();
2540 final ActionCallback result = new ActionCallback();
2542 final Ref<Boolean> fail = new Ref<Boolean>(false);
2543 final Runnable finalizer = new Runnable() {
2544 public void run() {
2545 if (fail.get()) {
2546 result.setRejected();
2547 } else {
2548 result.setDone();
2553 registerWorkerTask(bgBuildAction);
2555 final Runnable pooledThreadWithProgressRunnable = new Runnable() {
2556 public void run() {
2557 final AbstractTreeBuilder builder = getBuilder();
2559 builder.runBackgroundLoading(new Runnable() {
2560 public void run() {
2561 assertNotDispatchThread();
2563 try {
2564 bgBuildAction.run();
2566 if (edtPostRunnable != null) {
2567 builder.updateAfterLoadedInBackground(new Runnable() {
2568 public void run() {
2569 try {
2570 assertIsDispatchThread();
2572 edtPostRunnable.run();
2573 } catch (ProcessCanceledException e) {
2574 fail.set(true);
2576 finally {
2577 unregisterWorkerTask(bgBuildAction, finalizer);
2582 else {
2583 unregisterWorkerTask(bgBuildAction, finalizer);
2586 catch (ProcessCanceledException e) {
2587 fail.set(true);
2588 unregisterWorkerTask(bgBuildAction, finalizer);
2590 catch (Throwable t) {
2591 unregisterWorkerTask(bgBuildAction, finalizer);
2592 throw new RuntimeException(t);
2599 Runnable pooledThreadRunnable = new Runnable() {
2600 public void run() {
2601 try {
2602 if (myProgress != null) {
2603 ProgressManager.getInstance().runProcess(pooledThreadWithProgressRunnable, myProgress);
2605 else {
2606 pooledThreadWithProgressRunnable.run();
2609 catch (ProcessCanceledException e) {
2610 fail.set(true);
2611 unregisterWorkerTask(bgBuildAction, finalizer);
2616 if (isPassthroughMode()) {
2618 } else {
2619 if (myWorker == null || myWorker.isDisposed()) {
2620 myWorker = new WorkerThread("AbstractTreeBuilder.Worker", 1);
2621 myWorker.start();
2622 myWorker.addTaskFirst(pooledThreadRunnable);
2623 myWorker.dispose(false);
2625 else {
2626 myWorker.addTaskFirst(pooledThreadRunnable);
2630 return result;
2633 private void registerWorkerTask(Runnable runnable) {
2634 synchronized (myActiveWorkerTasks) {
2635 myActiveWorkerTasks.add(runnable);
2639 private void unregisterWorkerTask(Runnable runnable, @Nullable Runnable finalizeRunnable) {
2640 boolean wasRemoved;
2641 synchronized (myActiveWorkerTasks) {
2642 wasRemoved = myActiveWorkerTasks.remove(runnable);
2645 if (wasRemoved && finalizeRunnable != null) {
2646 finalizeRunnable.run();
2649 maybeReady();
2652 public boolean isWorkerBusy() {
2653 synchronized (myActiveWorkerTasks) {
2654 return myActiveWorkerTasks.size() > 0;
2658 private void clearWorkerTasks() {
2659 synchronized (myActiveWorkerTasks) {
2660 myActiveWorkerTasks.clear();
2664 private void updateNodeImageAndPosition(final DefaultMutableTreeNode node, boolean updatePosition) {
2665 if (!(node.getUserObject() instanceof NodeDescriptor)) return;
2666 NodeDescriptor descriptor = getDescriptorFrom(node);
2667 if (getElementFromDescriptor(descriptor) == null) return;
2669 boolean notified = false;
2670 if (updatePosition) {
2671 DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode)node.getParent();
2672 if (parentNode != null) {
2673 int oldIndex = parentNode.getIndex(node);
2674 int newIndex = oldIndex;
2675 if (isLoadingChildrenFor(node.getParent()) || getBuilder().isChildrenResortingNeeded(descriptor)) {
2676 final ArrayList<TreeNode> children = new ArrayList<TreeNode>(parentNode.getChildCount());
2677 for (int i = 0; i < parentNode.getChildCount(); i++) {
2678 children.add(parentNode.getChildAt(i));
2680 sortChildren(node, children, true, false);
2681 newIndex = children.indexOf(node);
2684 if (oldIndex != newIndex) {
2685 List<Object> pathsToExpand = new ArrayList<Object>();
2686 List<Object> selectionPaths = new ArrayList<Object>();
2687 TreeBuilderUtil.storePaths(getBuilder(), node, pathsToExpand, selectionPaths, false);
2688 removeNodeFromParent(node, false);
2689 myTreeModel.insertNodeInto(node, parentNode, newIndex);
2690 TreeBuilderUtil.restorePaths(getBuilder(), pathsToExpand, selectionPaths, false);
2691 notified = true;
2693 else {
2694 myTreeModel.nodeChanged(node);
2695 notified = true;
2698 else {
2699 myTreeModel.nodeChanged(node);
2700 notified = true;
2704 if (!notified) {
2705 myTreeModel.nodeChanged(node);
2710 public DefaultTreeModel getTreeModel() {
2711 return myTreeModel;
2714 private void insertNodesInto(final ArrayList<TreeNode> toInsert, final DefaultMutableTreeNode parentNode) {
2715 sortChildren(parentNode, toInsert, false, true);
2716 final ArrayList<TreeNode> all = new ArrayList<TreeNode>(toInsert.size() + parentNode.getChildCount());
2717 all.addAll(toInsert);
2718 all.addAll(TreeUtil.childrenToArray(parentNode));
2720 if (toInsert.size() > 0) {
2721 sortChildren(parentNode, all, true, true);
2723 int[] newNodeIndices = new int[toInsert.size()];
2724 int eachNewNodeIndex = 0;
2725 TreeMap<Integer, TreeNode> insertSet = new TreeMap<Integer, TreeNode>();
2726 for (int i = 0; i < toInsert.size(); i++) {
2727 TreeNode eachNewNode = toInsert.get(i);
2728 while (all.get(eachNewNodeIndex) != eachNewNode) {
2729 eachNewNodeIndex++;
2731 newNodeIndices[i] = eachNewNodeIndex;
2732 insertSet.put(eachNewNodeIndex, eachNewNode);
2735 Iterator<Integer> indices = insertSet.keySet().iterator();
2736 while (indices.hasNext()) {
2737 Integer eachIndex = indices.next();
2738 TreeNode eachNode = insertSet.get(eachIndex);
2739 parentNode.insert((MutableTreeNode)eachNode, eachIndex);
2742 myTreeModel.nodesWereInserted(parentNode, newNodeIndices);
2744 else {
2745 ArrayList<TreeNode> before = new ArrayList<TreeNode>();
2746 before.addAll(all);
2748 sortChildren(parentNode, all, true, false);
2749 if (!before.equals(all)) {
2750 processInnerChange(new Runnable() {
2751 public void run() {
2752 parentNode.removeAllChildren();
2753 for (TreeNode each : all) {
2754 parentNode.add((MutableTreeNode)each);
2756 myTreeModel.nodeStructureChanged(parentNode);
2763 private void sortChildren(DefaultMutableTreeNode node, ArrayList<TreeNode> children, boolean updateStamp, boolean forceSort) {
2764 NodeDescriptor descriptor = getDescriptorFrom(node);
2765 assert descriptor != null;
2767 if (descriptor.getChildrenSortingStamp() >= getComparatorStamp() && !forceSort) return;
2768 if (children.size() > 0) {
2769 getBuilder().sortChildren(myNodeComparator, node, children);
2772 if (updateStamp) {
2773 descriptor.setChildrenSortingStamp(getComparatorStamp());
2777 private void disposeNode(DefaultMutableTreeNode node) {
2778 TreeNode parent = node.getParent();
2779 if (parent instanceof DefaultMutableTreeNode) {
2780 addToUnbuilt((DefaultMutableTreeNode)parent);
2783 if (node.getChildCount() > 0) {
2784 for (DefaultMutableTreeNode _node = (DefaultMutableTreeNode)node.getFirstChild(); _node != null; _node = _node.getNextSibling()) {
2785 disposeNode(_node);
2789 removeFromUpdating(node);
2790 removeFromUnbuilt(node);
2792 if (isLoadingNode(node)) return;
2793 NodeDescriptor descriptor = getDescriptorFrom(node);
2794 if (descriptor == null) return;
2795 final Object element = getElementFromDescriptor(descriptor);
2796 removeMapping(element, node, null);
2797 myAutoExpandRoots.remove(element);
2798 node.setUserObject(null);
2799 node.removeAllChildren();
2802 public boolean addSubtreeToUpdate(final DefaultMutableTreeNode root) {
2803 return addSubtreeToUpdate(root, null);
2806 public boolean addSubtreeToUpdate(final DefaultMutableTreeNode root, Runnable runAfterUpdate) {
2807 Object element = getElementFor(root);
2808 if (getTreeStructure().isAlwaysLeaf(element)) {
2809 removeLoading(root, true);
2811 if (runAfterUpdate != null) {
2812 getReady(this).doWhenDone(runAfterUpdate);
2814 return false;
2817 if (isReleaseRequested()) {
2818 processNodeActionsIfReady(root);
2819 } else {
2820 getUpdater().runAfterUpdate(runAfterUpdate);
2821 getUpdater().addSubtreeToUpdate(root);
2824 return true;
2827 public boolean wasRootNodeInitialized() {
2828 return myRootNodeWasInitialized;
2831 private boolean isRootNodeBuilt() {
2832 return myRootNodeWasInitialized && isNodeBeingBuilt(myRootNode);
2835 public void select(final Object[] elements, @Nullable final Runnable onDone) {
2836 select(elements, onDone, false);
2839 public void select(final Object[] elements, @Nullable final Runnable onDone, boolean addToSelection) {
2840 select(elements, onDone, addToSelection, false);
2843 public void select(final Object[] elements, @Nullable final Runnable onDone, boolean addToSelection, boolean deferred) {
2844 _select(elements, onDone, addToSelection, true, false, true, deferred, false, false);
2847 void _select(final Object[] elements,
2848 final Runnable onDone,
2849 final boolean addToSelection,
2850 final boolean checkCurrentSelection,
2851 final boolean checkIfInStructure) {
2853 _select(elements, onDone, addToSelection, checkCurrentSelection, checkIfInStructure, true, false, false, false);
2856 void _select(final Object[] elements,
2857 final Runnable onDone,
2858 final boolean addToSelection,
2859 final boolean checkCurrentSelection,
2860 final boolean checkIfInStructure,
2861 final boolean scrollToVisible) {
2863 _select(elements, onDone, addToSelection, checkCurrentSelection, checkIfInStructure, scrollToVisible, false, false, false);
2866 public void userSelect(final Object[] elements,
2867 final Runnable onDone,
2868 final boolean addToSelection,
2869 boolean scroll) {
2870 _select(elements, onDone, addToSelection, true, false, scroll, false, true, true);
2873 void _select(final Object[] elements,
2874 final Runnable onDone,
2875 final boolean addToSelection,
2876 final boolean checkCurrentSelection,
2877 final boolean checkIfInStructure,
2878 final boolean scrollToVisible,
2879 final boolean deferred,
2880 final boolean canSmartExpand,
2881 final boolean mayQueue) {
2883 AbstractTreeUpdater updater = getUpdater();
2884 if (mayQueue && updater != null) {
2885 updater.queueSelection(new SelectionRequest(elements, onDone, addToSelection, checkCurrentSelection, checkIfInStructure, scrollToVisible, deferred, canSmartExpand));
2886 return;
2889 boolean willAffectSelection = elements.length > 0 || (elements.length == 0 && addToSelection);
2890 if (!willAffectSelection) {
2891 runDone(onDone);
2892 return;
2895 final boolean oldCanProcessDeferredSelection = myCanProcessDeferredSelections;
2897 if (!deferred && wasRootNodeInitialized() && willAffectSelection) {
2898 myCanProcessDeferredSelections = false;
2901 if (!checkDeferred(deferred, onDone)) return;
2903 if (!deferred && oldCanProcessDeferredSelection && !myCanProcessDeferredSelections) {
2904 getTree().clearSelection();
2908 runDone(new Runnable() {
2909 public void run() {
2910 if (!checkDeferred(deferred, onDone)) return;
2912 final Set<Object> currentElements = getSelectedElements();
2914 if (checkCurrentSelection && currentElements.size() > 0 && elements.length == currentElements.size()) {
2915 boolean runSelection = false;
2916 for (Object eachToSelect : elements) {
2917 if (!currentElements.contains(eachToSelect)) {
2918 runSelection = true;
2919 break;
2923 if (!runSelection) {
2924 if (elements.length > 0) {
2925 selectVisible(elements[0], onDone, true, true, scrollToVisible);
2927 return;
2931 Set<Object> toSelect = new HashSet<Object>();
2932 myTree.clearSelection();
2933 toSelect.addAll(Arrays.asList(elements));
2934 if (addToSelection) {
2935 toSelect.addAll(currentElements);
2938 if (checkIfInStructure) {
2939 final Iterator<Object> allToSelect = toSelect.iterator();
2940 while (allToSelect.hasNext()) {
2941 Object each = allToSelect.next();
2942 if (!isInStructure(each)) {
2943 allToSelect.remove();
2948 final Object[] elementsToSelect = ArrayUtil.toObjectArray(toSelect);
2950 if (wasRootNodeInitialized()) {
2951 final int[] originalRows = myTree.getSelectionRows();
2952 if (!addToSelection) {
2953 myTree.clearSelection();
2955 addNext(elementsToSelect, 0, new Runnable() {
2956 public void run() {
2957 if (getTree().isSelectionEmpty()) {
2958 processInnerChange(new Runnable() {
2959 public void run() {
2960 restoreSelection(currentElements);
2964 runDone(onDone);
2966 }, originalRows, deferred, scrollToVisible, canSmartExpand);
2968 else {
2969 addToDeferred(elementsToSelect, onDone);
2975 private void restoreSelection(Set<Object> selection) {
2976 for (Object each : selection) {
2977 DefaultMutableTreeNode node = getNodeForElement(each, false);
2978 if (node != null && isValidForSelectionAdjusting(node)) {
2979 addSelectionPath(getPathFor(node), false, null, null);
2985 private void addToDeferred(final Object[] elementsToSelect, final Runnable onDone) {
2986 myDeferredSelections.clear();
2987 myDeferredSelections.add(new Runnable() {
2988 public void run() {
2989 select(elementsToSelect, onDone, false, true);
2994 private boolean checkDeferred(boolean isDeferred, @Nullable Runnable onDone) {
2995 if (!isDeferred || myCanProcessDeferredSelections || !wasRootNodeInitialized()) {
2996 return true;
2998 else {
2999 runDone(onDone);
3000 return false;
3004 @NotNull
3005 final Set<Object> getSelectedElements() {
3006 final TreePath[] paths = myTree.getSelectionPaths();
3008 Set<Object> result = new HashSet<Object>();
3009 if (paths != null) {
3010 for (TreePath eachPath : paths) {
3011 if (eachPath.getLastPathComponent() instanceof DefaultMutableTreeNode) {
3012 final DefaultMutableTreeNode eachNode = (DefaultMutableTreeNode)eachPath.getLastPathComponent();
3013 final Object eachElement = getElementFor(eachNode);
3014 if (eachElement != null) {
3015 result.add(eachElement);
3020 return result;
3024 private void addNext(final Object[] elements,
3025 final int i,
3026 @Nullable final Runnable onDone,
3027 final int[] originalRows,
3028 final boolean deferred,
3029 final boolean scrollToVisible,
3030 final boolean canSmartExpand) {
3031 if (i >= elements.length) {
3032 if (myTree.isSelectionEmpty()) {
3033 myTree.setSelectionRows(originalRows);
3035 runDone(onDone);
3037 else {
3038 if (!checkDeferred(deferred, onDone)) {
3039 return;
3042 doSelect(elements[i], new Runnable() {
3043 public void run() {
3044 if (!checkDeferred(deferred, onDone)) return;
3046 addNext(elements, i + 1, onDone, originalRows, deferred, scrollToVisible, canSmartExpand);
3048 }, true, deferred, i == 0, scrollToVisible, canSmartExpand);
3052 public void select(final Object element, @Nullable final Runnable onDone) {
3053 select(element, onDone, false);
3056 public void select(final Object element, @Nullable final Runnable onDone, boolean addToSelection) {
3057 _select(new Object[]{element}, onDone, addToSelection, true, false);
3060 private void doSelect(final Object element,
3061 final Runnable onDone,
3062 final boolean addToSelection,
3063 final boolean deferred,
3064 final boolean canBeCentered,
3065 final boolean scrollToVisible,
3066 boolean canSmartExpand) {
3067 final Runnable _onDone = new Runnable() {
3068 public void run() {
3069 if (!checkDeferred(deferred, onDone)) return;
3070 selectVisible(element, onDone, addToSelection, canBeCentered, scrollToVisible);
3073 _expand(element, _onDone, true, false, canSmartExpand);
3076 public void scrollSelectionToVisible(@Nullable Runnable onDone, boolean shouldBeCentered) {
3077 int[] rows = myTree.getSelectionRows();
3078 if (rows == null || rows.length == 0) {
3079 runDone(onDone);
3080 return;
3084 Object toSelect = null;
3085 for (int eachRow : rows) {
3086 TreePath path = myTree.getPathForRow(eachRow);
3087 toSelect = getElementFor(path.getLastPathComponent());
3088 if (toSelect != null) break;
3091 if (toSelect != null) {
3092 selectVisible(toSelect, onDone, true, shouldBeCentered, true);
3096 private void selectVisible(Object element, final Runnable onDone, boolean addToSelection, boolean canBeCentered, final boolean scroll) {
3097 final DefaultMutableTreeNode toSelect = getNodeForElement(element, false);
3099 if (toSelect == null) {
3100 runDone(onDone);
3101 return;
3104 if (getRootNode() == toSelect && !myTree.isRootVisible()) {
3105 runDone(onDone);
3106 return;
3109 final int row = myTree.getRowForPath(new TreePath(toSelect.getPath()));
3111 if (myUpdaterState != null) {
3112 myUpdaterState.addSelection(element);
3115 if (Registry.is("ide.tree.autoscrollToVCenter") && canBeCentered) {
3116 runDone(new Runnable() {
3117 public void run() {
3118 TreeUtil.showRowCentered(myTree, row, false, scroll).doWhenDone(new Runnable() {
3119 public void run() {
3120 runDone(onDone);
3126 else {
3127 TreeUtil.showAndSelect(myTree, row - 2, row + 2, row, -1, addToSelection, scroll).doWhenDone(new Runnable() {
3128 public void run() {
3129 runDone(onDone);
3135 public void expand(final Object element, @Nullable final Runnable onDone) {
3136 expand(new Object[]{element}, onDone);
3139 public void expand(final Object[] element, @Nullable final Runnable onDone) {
3140 expand(element, onDone, false);
3144 void expand(final Object element, @Nullable final Runnable onDone, boolean checkIfInStructure) {
3145 _expand(new Object[]{element}, onDone == null ? new EmptyRunnable() : onDone, false, checkIfInStructure, false);
3148 void expand(final Object[] element, @Nullable final Runnable onDone, boolean checkIfInStructure) {
3149 _expand(element, onDone == null ? new EmptyRunnable() : onDone, false, checkIfInStructure, false);
3152 void _expand(final Object[] element,
3153 @NotNull final Runnable onDone,
3154 final boolean parentsOnly,
3155 final boolean checkIfInStructure,
3156 final boolean canSmartExpand) {
3158 runDone(new Runnable() {
3159 public void run() {
3160 if (element.length == 0) {
3161 runDone(onDone);
3162 return;
3165 if (myUpdaterState != null) {
3166 myUpdaterState.clearExpansion();
3170 final ActionCallback done = new ActionCallback(element.length);
3171 done.doWhenDone(new Runnable() {
3172 public void run() {
3173 runDone(onDone);
3175 }).doWhenRejected(new Runnable() {
3176 public void run() {
3177 runDone(onDone);
3181 expandNext(element, 0, parentsOnly, checkIfInStructure, canSmartExpand, done);
3186 private void expandNext(final Object[] elements, final int index, final boolean parentsOnly, final boolean checkIfInStricture, final boolean canSmartExpand, final ActionCallback done) {
3187 if (elements.length <= 0) {
3188 done.setDone();
3189 return;
3192 if (index >= elements.length) {
3193 return;
3196 _expand(elements[index], new Runnable() {
3197 public void run() {
3198 done.setDone();
3199 expandNext(elements, index + 1, parentsOnly, checkIfInStricture, canSmartExpand, done);
3201 }, parentsOnly, checkIfInStricture, canSmartExpand);
3204 public void collapseChildren(final Object element, @Nullable final Runnable onDone) {
3205 runDone(new Runnable() {
3206 public void run() {
3207 final DefaultMutableTreeNode node = getNodeForElement(element, false);
3208 if (node != null) {
3209 getTree().collapsePath(new TreePath(node.getPath()));
3210 runDone(onDone);
3216 private void runDone(@Nullable Runnable done) {
3217 if (done == null) return;
3219 if (isYeildingNow()) {
3220 if (!myYeildingDoneRunnables.contains(done)) {
3221 myYeildingDoneRunnables.add(done);
3224 else {
3225 done.run();
3229 private void _expand(final Object element,
3230 @NotNull final Runnable onDone,
3231 final boolean parentsOnly,
3232 boolean checkIfInStructure,
3233 boolean canSmartExpand) {
3235 if (checkIfInStructure && !isInStructure(element)) {
3236 runDone(onDone);
3237 return;
3240 if (wasRootNodeInitialized()) {
3241 List<Object> kidsToExpand = new ArrayList<Object>();
3242 Object eachElement = element;
3243 DefaultMutableTreeNode firstVisible = null;
3244 while (true) {
3245 if (!isValid(eachElement)) break;
3247 firstVisible = getNodeForElement(eachElement, true);
3248 if (eachElement != element || !parentsOnly) {
3249 assert !kidsToExpand.contains(eachElement) :
3250 "Not a valid tree structure, walking up the structure gives many entries for element=" +
3251 eachElement +
3252 ", root=" +
3253 getTreeStructure().getRootElement();
3254 kidsToExpand.add(eachElement);
3256 if (firstVisible != null) break;
3257 eachElement = getTreeStructure().getParentElement(eachElement);
3258 if (eachElement == null) {
3259 firstVisible = null;
3260 break;
3265 if (firstVisible == null) {
3266 runDone(onDone);
3268 else if (kidsToExpand.size() == 0) {
3269 final DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode)firstVisible.getParent();
3270 if (parentNode != null) {
3271 final TreePath parentPath = new TreePath(parentNode.getPath());
3272 if (!myTree.isExpanded(parentPath)) {
3273 expand(parentPath, canSmartExpand);
3276 runDone(onDone);
3278 else {
3279 processExpand(firstVisible, kidsToExpand, kidsToExpand.size() - 1, onDone, canSmartExpand);
3282 else {
3283 deferExpansion(element, onDone, parentsOnly, canSmartExpand);
3287 private void deferExpansion(final Object element, final Runnable onDone, final boolean parentsOnly, final boolean canSmartExpand) {
3288 myDeferredExpansions.add(new Runnable() {
3289 public void run() {
3290 _expand(element, onDone, parentsOnly, false, canSmartExpand);
3295 private void processExpand(final DefaultMutableTreeNode toExpand,
3296 final List kidsToExpand,
3297 final int expandIndex,
3298 @NotNull final Runnable onDone,
3299 final boolean canSmartExpand) {
3301 final Object element = getElementFor(toExpand);
3302 if (element == null) {
3303 runDone(onDone);
3304 return;
3307 addNodeAction(element, new NodeAction() {
3308 public void onReady(final DefaultMutableTreeNode node) {
3309 if (node.getChildCount() > 0 && !myTree.isExpanded(new TreePath(node.getPath()))) {
3310 if (!isAutoExpand(node)) {
3311 expand(node, canSmartExpand);
3315 if (expandIndex <= 0) {
3316 runDone(onDone);
3317 return;
3320 final DefaultMutableTreeNode nextNode = getNodeForElement(kidsToExpand.get(expandIndex - 1), false);
3321 if (nextNode != null) {
3322 processExpand(nextNode, kidsToExpand, expandIndex - 1, onDone, canSmartExpand);
3324 else {
3325 runDone(onDone);
3328 }, true);
3331 boolean childrenToUpdate = areChildrenToBeUpdated(toExpand);
3332 boolean expanded = myTree.isExpanded(getPathFor(toExpand));
3333 boolean unbuilt = myUnbuiltNodes.contains(toExpand);
3335 if (expanded) {
3336 if (unbuilt && !childrenToUpdate) {
3337 addSubtreeToUpdate(toExpand);
3339 } else {
3340 expand(toExpand, canSmartExpand);
3343 if (!unbuilt && !childrenToUpdate) {
3344 processNodeActionsIfReady(toExpand);
3348 private boolean areChildrenToBeUpdated(DefaultMutableTreeNode node) {
3349 return getUpdater().isEnqueuedToUpdate(node) || isUpdatingParent(node);
3352 private String asString(DefaultMutableTreeNode node) {
3353 if (node == null) return null;
3355 StringBuffer children = new StringBuffer(node.toString());
3356 children.append(" [");
3357 for (int i = 0; i < node.getChildCount(); i++) {
3358 children.append(node.getChildAt(i));
3359 if (i < node.getChildCount() - 1) {
3360 children.append(",");
3363 children.append("]");
3365 return children.toString();
3368 @Nullable
3369 public Object getElementFor(Object node) {
3370 if (!(node instanceof DefaultMutableTreeNode)) return null;
3371 return getElementFor((DefaultMutableTreeNode)node);
3374 @Nullable
3375 Object getElementFor(DefaultMutableTreeNode node) {
3376 if (node != null) {
3377 final Object o = node.getUserObject();
3378 if (o instanceof NodeDescriptor) {
3379 return getElementFromDescriptor(((NodeDescriptor)o));
3383 return null;
3386 public final boolean isNodeBeingBuilt(final TreePath path) {
3387 return isNodeBeingBuilt(path.getLastPathComponent());
3390 public final boolean isNodeBeingBuilt(Object node) {
3391 if (isReleaseRequested()) return false;
3393 return getParentBuiltNode(node) != null;
3396 public final DefaultMutableTreeNode getParentBuiltNode(Object node) {
3397 DefaultMutableTreeNode parent = getParentLoading(node);
3398 if (parent != null) return parent;
3400 if (isLoadingParent(node)) return (DefaultMutableTreeNode)node;
3402 final boolean childrenAreNoLoadedYet = myUnbuiltNodes.contains(node);
3403 if (childrenAreNoLoadedYet) {
3404 if (node instanceof DefaultMutableTreeNode) {
3405 final TreePath nodePath = new TreePath(((DefaultMutableTreeNode)node).getPath());
3406 if (!myTree.isExpanded(nodePath)) return null;
3409 return (DefaultMutableTreeNode)node;
3413 return null;
3416 private boolean isLoadingParent(Object node) {
3417 if (!(node instanceof DefaultMutableTreeNode)) return false;
3418 return isLoadedInBackground(getElementFor((DefaultMutableTreeNode)node));
3421 public void setTreeStructure(final AbstractTreeStructure treeStructure) {
3422 myTreeStructure = treeStructure;
3423 clearUpdaterState();
3426 public AbstractTreeUpdater getUpdater() {
3427 return myUpdater;
3430 public void setUpdater(final AbstractTreeUpdater updater) {
3431 myUpdater = updater;
3432 if (updater != null && myUpdateIfInactive) {
3433 updater.showNotify();
3436 if (myUpdater != null) {
3437 myUpdater.setPassThroughMode(myPassthroughMode);
3441 public DefaultMutableTreeNode getRootNode() {
3442 return myRootNode;
3445 public void setRootNode(@NotNull final DefaultMutableTreeNode rootNode) {
3446 myRootNode = rootNode;
3449 private void dropUpdaterStateIfExternalChange() {
3450 if (!isInnerChange()) {
3451 clearUpdaterState();
3452 myAutoExpandRoots.clear();
3456 void clearUpdaterState() {
3457 myUpdaterState = null;
3460 private void createMapping(Object element, DefaultMutableTreeNode node) {
3461 if (!myElementToNodeMap.containsKey(element)) {
3462 myElementToNodeMap.put(element, node);
3464 else {
3465 final Object value = myElementToNodeMap.get(element);
3466 final List<DefaultMutableTreeNode> nodes;
3467 if (value instanceof DefaultMutableTreeNode) {
3468 nodes = new ArrayList<DefaultMutableTreeNode>();
3469 nodes.add((DefaultMutableTreeNode)value);
3470 myElementToNodeMap.put(element, nodes);
3472 else {
3473 nodes = (List<DefaultMutableTreeNode>)value;
3475 nodes.add(node);
3479 private void removeMapping(Object element, DefaultMutableTreeNode node, @Nullable Object elementToPutNodeActionsFor) {
3480 final Object value = myElementToNodeMap.get(element);
3481 if (value != null) {
3482 if (value instanceof DefaultMutableTreeNode) {
3483 if (value.equals(node)) {
3484 myElementToNodeMap.remove(element);
3487 else {
3488 List<DefaultMutableTreeNode> nodes = (List<DefaultMutableTreeNode>)value;
3489 final boolean reallyRemoved = nodes.remove(node);
3490 if (reallyRemoved) {
3491 if (nodes.isEmpty()) {
3492 myElementToNodeMap.remove(element);
3498 remapNodeActions(element, elementToPutNodeActionsFor);
3501 private void remapNodeActions(Object element, Object elementToPutNodeActionsFor) {
3502 _remapNodeActions(element, elementToPutNodeActionsFor, myNodeActions);
3503 _remapNodeActions(element, elementToPutNodeActionsFor, myNodeChildrenActions);
3506 private void _remapNodeActions(Object element, Object elementToPutNodeActionsFor, final Map<Object, List<NodeAction>> nodeActions) {
3507 final List<NodeAction> actions = nodeActions.get(element);
3508 nodeActions.remove(element);
3510 if (elementToPutNodeActionsFor != null && actions != null) {
3511 nodeActions.put(elementToPutNodeActionsFor, actions);
3515 private DefaultMutableTreeNode getFirstNode(Object element) {
3516 return findNode(element, 0);
3519 private DefaultMutableTreeNode findNode(final Object element, int startIndex) {
3520 final Object value = getBuilder().findNodeByElement(element);
3521 if (value == null) {
3522 return null;
3524 if (value instanceof DefaultMutableTreeNode) {
3525 return startIndex == 0 ? (DefaultMutableTreeNode)value : null;
3527 final List<DefaultMutableTreeNode> nodes = (List<DefaultMutableTreeNode>)value;
3528 return startIndex < nodes.size() ? nodes.get(startIndex) : null;
3531 protected Object findNodeByElement(Object element) {
3532 if (myElementToNodeMap.containsKey(element)) {
3533 return myElementToNodeMap.get(element);
3536 try {
3537 TREE_NODE_WRAPPER.setValue(element);
3538 return myElementToNodeMap.get(TREE_NODE_WRAPPER);
3540 finally {
3541 TREE_NODE_WRAPPER.setValue(null);
3545 private DefaultMutableTreeNode findNodeForChildElement(DefaultMutableTreeNode parentNode, Object element) {
3546 final Object value = myElementToNodeMap.get(element);
3547 if (value == null) {
3548 return null;
3551 if (value instanceof DefaultMutableTreeNode) {
3552 final DefaultMutableTreeNode elementNode = (DefaultMutableTreeNode)value;
3553 return parentNode.equals(elementNode.getParent()) ? elementNode : null;
3556 final List<DefaultMutableTreeNode> allNodesForElement = (List<DefaultMutableTreeNode>)value;
3557 for (final DefaultMutableTreeNode elementNode : allNodesForElement) {
3558 if (parentNode.equals(elementNode.getParent())) {
3559 return elementNode;
3563 return null;
3566 public void cancelBackgroundLoading() {
3567 if (myWorker != null) {
3568 myWorker.cancelTasks();
3569 clearWorkerTasks();
3572 clearNodeActions();
3575 private void addNodeAction(Object element, NodeAction action, boolean shouldChildrenBeReady) {
3576 _addNodeAction(element, action, myNodeActions);
3577 if (shouldChildrenBeReady) {
3578 _addNodeAction(element, action, myNodeChildrenActions);
3583 private void _addNodeAction(Object element, NodeAction action, Map<Object, List<NodeAction>> map) {
3584 maybeSetBusyAndScheduleWaiterForReady(true);
3585 List<NodeAction> list = map.get(element);
3586 if (list == null) {
3587 list = new ArrayList<NodeAction>();
3588 map.put(element, list);
3590 list.add(action);
3594 private void cleanUpNow() {
3595 if (isReleaseRequested()) return;
3597 final UpdaterTreeState state = new UpdaterTreeState(this);
3599 myTree.collapsePath(new TreePath(myTree.getModel().getRoot()));
3600 myTree.clearSelection();
3601 getRootNode().removeAllChildren();
3603 myRootNodeWasInitialized = false;
3604 clearNodeActions();
3605 myElementToNodeMap.clear();
3606 myDeferredSelections.clear();
3607 myDeferredExpansions.clear();
3608 myLoadedInBackground.clear();
3609 myUnbuiltNodes.clear();
3610 myUpdateFromRootRequested = true;
3612 if (myWorker != null) {
3613 Disposer.dispose(myWorker);
3614 myWorker = null;
3617 myTree.invalidate();
3619 state.restore(null);
3622 public AbstractTreeUi setClearOnHideDelay(final long clearOnHideDelay) {
3623 myClearOnHideDelay = clearOnHideDelay;
3624 return this;
3627 public void setJantorPollPeriod(final long time) {
3628 myJanitorPollPeriod = time;
3631 public void setCheckStructure(final boolean checkStructure) {
3632 myCheckStructure = checkStructure;
3635 private class MySelectionListener implements TreeSelectionListener {
3636 public void valueChanged(final TreeSelectionEvent e) {
3637 dropUpdaterStateIfExternalChange();
3642 private class MyExpansionListener implements TreeExpansionListener {
3643 public void treeExpanded(TreeExpansionEvent event) {
3644 dropUpdaterStateIfExternalChange();
3646 TreePath path = event.getPath();
3648 if (myRequestedExpand != null && !myRequestedExpand.equals(path)) return;
3650 final DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
3652 if (!myUnbuiltNodes.contains(node)) {
3653 removeLoading(node, false);
3655 Set<DefaultMutableTreeNode> childrenToUpdate = new HashSet<DefaultMutableTreeNode>();
3656 for (int i = 0; i < node.getChildCount(); i++) {
3657 DefaultMutableTreeNode each = (DefaultMutableTreeNode)node.getChildAt(i);
3658 if (myUnbuiltNodes.contains(each)) {
3659 makeLoadingOrLeafIfNoChildren(each);
3660 childrenToUpdate.add(each);
3664 if (childrenToUpdate.size() > 0) {
3665 for (DefaultMutableTreeNode each : childrenToUpdate) {
3666 maybeUpdateSubtreeToUpdate(each);
3670 else {
3671 getBuilder().expandNodeChildren(node);
3674 processSmartExpand(node, canSmartExpand(node, true), false);
3675 processNodeActionsIfReady(node);
3678 public void treeCollapsed(TreeExpansionEvent e) {
3679 dropUpdaterStateIfExternalChange();
3681 final TreePath path = e.getPath();
3682 final DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
3683 if (!(node.getUserObject() instanceof NodeDescriptor)) return;
3686 TreePath pathToSelect = null;
3687 if (isSelectionInside(node)) {
3688 pathToSelect = new TreePath(node.getPath());
3692 NodeDescriptor descriptor = getDescriptorFrom(node);
3693 if (getBuilder().isDisposeOnCollapsing(descriptor)) {
3694 runDone(new Runnable() {
3695 public void run() {
3696 if (isDisposed(node)) return;
3698 TreePath nodePath = new TreePath(node.getPath());
3699 if (myTree.isExpanded(nodePath)) return;
3701 removeChildren(node);
3702 makeLoadingOrLeafIfNoChildren(node);
3705 if (node.equals(getRootNode())) {
3706 if (myTree.isRootVisible()) {
3707 //todo kirillk to investigate -- should be done by standard selction move
3708 //addSelectionPath(new TreePath(getRootNode().getPath()), true, Condition.FALSE);
3711 else {
3712 myTreeModel.reload(node);
3716 if (pathToSelect != null && myTree.isSelectionEmpty()) {
3717 addSelectionPath(pathToSelect, true, Condition.FALSE, null);
3721 private void removeChildren(DefaultMutableTreeNode node) {
3722 EnumerationCopy copy = new EnumerationCopy(node.children());
3723 while (copy.hasMoreElements()) {
3724 disposeNode((DefaultMutableTreeNode)copy.nextElement());
3726 node.removeAllChildren();
3727 myTreeModel.nodeStructureChanged(node);
3731 private void maybeUpdateSubtreeToUpdate(final DefaultMutableTreeNode subtreeRoot) {
3732 if (!myUnbuiltNodes.contains(subtreeRoot)) return;
3733 TreePath path = getPathFor(subtreeRoot);
3735 if (myTree.getRowForPath(path) == -1) return;
3737 DefaultMutableTreeNode parent = getParentBuiltNode(subtreeRoot);
3738 if (parent == null) {
3739 addSubtreeToUpdate(subtreeRoot);
3740 } else if (parent != subtreeRoot) {
3741 addNodeAction(getElementFor(subtreeRoot), new NodeAction() {
3742 public void onReady(DefaultMutableTreeNode parent) {
3743 maybeUpdateSubtreeToUpdate(subtreeRoot);
3745 }, true);
3749 private boolean isSelectionInside(DefaultMutableTreeNode parent) {
3750 TreePath path = new TreePath(myTreeModel.getPathToRoot(parent));
3751 TreePath[] paths = myTree.getSelectionPaths();
3752 if (paths == null) return false;
3753 for (TreePath path1 : paths) {
3754 if (path.isDescendant(path1)) return true;
3756 return false;
3759 public boolean isInStructure(@Nullable Object element) {
3760 Object eachParent = element;
3761 while (eachParent != null) {
3762 if (getTreeStructure().getRootElement().equals(eachParent)) return true;
3763 eachParent = getTreeStructure().getParentElement(eachParent);
3766 return false;
3769 interface NodeAction {
3770 void onReady(DefaultMutableTreeNode node);
3773 public void setCanYield(final boolean canYield) {
3774 myCanYield = canYield;
3777 public Collection<TreeUpdatePass> getYeildingPasses() {
3778 return myYeildingPasses;
3781 public boolean isBuilt(Object element) {
3782 if (!myElementToNodeMap.containsKey(element)) return false;
3783 final Object node = myElementToNodeMap.get(element);
3784 return !myUnbuiltNodes.contains(node);
3787 static class LoadedChildren {
3789 private List myElements;
3790 private Map<Object, NodeDescriptor> myDescriptors = new HashMap<Object, NodeDescriptor>();
3791 private Map<NodeDescriptor, Boolean> myChanges = new HashMap<NodeDescriptor, Boolean>();
3793 LoadedChildren(Object[] elements) {
3794 myElements = Arrays.asList(elements != null ? elements : new Object[0]);
3797 void putDescriptor(Object element, NodeDescriptor descriptor, boolean isChanged) {
3798 assert myElements.contains(element);
3799 myDescriptors.put(element, descriptor);
3800 myChanges.put(descriptor, isChanged);
3803 List getElements() {
3804 return myElements;
3807 NodeDescriptor getDescriptor(Object element) {
3808 return myDescriptors.get(element);
3811 @Override
3812 public String toString() {
3813 return Arrays.asList(myElements) + "->" + myChanges;
3816 public boolean isUpdated(Object element) {
3817 NodeDescriptor desc = getDescriptor(element);
3818 return myChanges.get(desc);
3822 UpdaterTreeState getUpdaterState() {
3823 return myUpdaterState;
3826 private ActionCallback addReadyCallback(Object requestor) {
3827 synchronized (myReadyCallbacks) {
3828 ActionCallback cb = myReadyCallbacks.get(requestor);
3829 if (cb == null) {
3830 cb = new ActionCallback();
3831 myReadyCallbacks.put(requestor, cb);
3834 return cb;
3838 private ActionCallback[] getReadyCallbacks(boolean clear) {
3839 synchronized (myReadyCallbacks) {
3840 ActionCallback[] result = myReadyCallbacks.values().toArray(new ActionCallback[myReadyCallbacks.size()]);
3841 if (clear) {
3842 myReadyCallbacks.clear();
3844 return result;
3848 private long getComparatorStamp() {
3849 if (myNodeDescriptorComparator instanceof NodeDescriptor.NodeComparator) {
3850 long currentComparatorStamp = ((NodeDescriptor.NodeComparator)myNodeDescriptorComparator).getStamp();
3851 if (currentComparatorStamp > myLastComparatorStamp) {
3852 myOwnComparatorStamp = Math.max(myOwnComparatorStamp, currentComparatorStamp) + 1;
3854 myLastComparatorStamp = currentComparatorStamp;
3856 return Math.max(currentComparatorStamp, myOwnComparatorStamp);
3858 else {
3859 return myOwnComparatorStamp;
3863 public void incComparatorStamp() {
3864 myOwnComparatorStamp = getComparatorStamp() + 1;
3867 public static class UpdateInfo {
3868 NodeDescriptor myDescriptor;
3869 TreeUpdatePass myPass;
3870 boolean myCanSmartExpand;
3871 boolean myWasExpanded;
3872 boolean myForceUpdate;
3873 boolean myDescriptorIsUpToDate;
3875 public UpdateInfo(NodeDescriptor descriptor,
3876 TreeUpdatePass pass,
3877 boolean canSmartExpand,
3878 boolean wasExpanded,
3879 boolean forceUpdate,
3880 boolean descriptorIsUpToDate) {
3881 myDescriptor = descriptor;
3882 myPass = pass;
3883 myCanSmartExpand = canSmartExpand;
3884 myWasExpanded = wasExpanded;
3885 myForceUpdate = forceUpdate;
3886 myDescriptorIsUpToDate = descriptorIsUpToDate;
3889 synchronized NodeDescriptor getDescriptor() {
3890 return myDescriptor;
3893 synchronized TreeUpdatePass getPass() {
3894 return myPass;
3897 synchronized boolean isCanSmartExpand() {
3898 return myCanSmartExpand;
3901 synchronized boolean isWasExpanded() {
3902 return myWasExpanded;
3905 synchronized boolean isForceUpdate() {
3906 return myForceUpdate;
3909 synchronized boolean isDescriptorIsUpToDate() {
3910 return myDescriptorIsUpToDate;
3913 public synchronized void apply(UpdateInfo updateInfo) {
3914 myDescriptor = updateInfo.myDescriptor;
3915 myPass = updateInfo.myPass;
3916 myCanSmartExpand = updateInfo.myCanSmartExpand;
3917 myWasExpanded = updateInfo.myWasExpanded;
3918 myForceUpdate = updateInfo.myForceUpdate;
3919 myDescriptorIsUpToDate = updateInfo.myDescriptorIsUpToDate;
3922 public String toString() {
3923 return "UpdateInfo: desc=" + myDescriptor + " pass=" + myPass + " canSmartExpand=" + myCanSmartExpand + " wasExpanded=" + myWasExpanded + " forceUpdate=" + myForceUpdate + " descriptorUpToDate=" + myDescriptorIsUpToDate;
3928 public void setPassthroughMode(boolean passthrough) {
3929 myPassthroughMode = passthrough;
3930 AbstractTreeUpdater updater = getUpdater();
3932 if (updater != null) {
3933 updater.setPassThroughMode(myPassthroughMode);
3936 if (!isUnitTestingMode() && passthrough) {
3937 // TODO: this assertion should be restored back as soon as possible [JamTreeTableView should be rewritten, etc]
3938 //LOG.error("Pass-through mode for TreeUi is allowed only for unit test mode");
3942 public boolean isPassthroughMode() {
3943 return myPassthroughMode;
3946 private boolean isUnitTestingMode() {
3947 Application app = ApplicationManager.getApplication();
3948 return app != null && app.isUnitTestMode();