diff --git a/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyTreeStructure.java b/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyTreeStructure.java index dc8d8709ce97..38fe83e4303c 100644 --- a/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyTreeStructure.java +++ b/platform/lang-impl/src/com/intellij/ide/hierarchy/HierarchyTreeStructure.java @@ -20,6 +20,7 @@ import com.intellij.ide.util.treeView.AbstractTreeStructure; import com.intellij.ide.util.treeView.NodeDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; @@ -130,6 +131,11 @@ public abstract class HierarchyTreeStructure extends AbstractTreeStructure { public final boolean hasSomethingToCommit() { return PsiDocumentManager.getInstance(myProject).hasUncommitedDocuments(); } + @NotNull + @Override + public ActionCallback asyncCommit() { + return asyncCommitDocuments(myProject); + } protected abstract Object[] buildChildren(HierarchyNodeDescriptor descriptor); diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectTreeStructure.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectTreeStructure.java index cfc7169d7c78..39dc6e1931f2 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectTreeStructure.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectTreeStructure.java @@ -21,7 +21,9 @@ import com.intellij.ide.projectView.ViewSettings; import com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode; import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.ActionCallback; import com.intellij.psi.PsiDocumentManager; +import org.jetbrains.annotations.NotNull; public abstract class AbstractProjectTreeStructure extends ProjectAbstractTreeStructureBase implements ViewSettings { private final AbstractTreeNode myRoot; @@ -48,6 +50,12 @@ public abstract class AbstractProjectTreeStructure extends ProjectAbstractTreeSt PsiDocumentManager.getInstance(myProject).commitAllDocuments(); } + @NotNull + @Override + public ActionCallback asyncCommit() { + return asyncCommitDocuments(myProject); + } + @Override public final boolean hasSomethingToCommit() { return !myProject.isDisposed() diff --git a/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureTreeBuilder.java b/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureTreeBuilder.java index 624bf1eef27b..7933323760c9 100644 --- a/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureTreeBuilder.java +++ b/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureTreeBuilder.java @@ -62,6 +62,7 @@ public class StructureTreeBuilder extends AbstractTreeBuilder { myPsiTreeChangeListener = new MyPsiTreeChangeListener(); myModelListener = new ModelListener() { + @Override public void onModelChanged() { addRootToUpdate(); } @@ -76,6 +77,7 @@ public class StructureTreeBuilder extends AbstractTreeBuilder { setCanYieldUpdate(!ApplicationManager.getApplication().isUnitTestMode()); } + @Override public final void dispose() { PsiManager.getInstance(myProject).removePsiTreeChangeListener(myPsiTreeChangeListener); CopyPasteManager.getInstance().removeContentChangedListener(myCopyPasteListener); @@ -83,10 +85,12 @@ public class StructureTreeBuilder extends AbstractTreeBuilder { super.dispose(); } + @Override protected final boolean isAlwaysShowPlus(NodeDescriptor nodeDescriptor) { return ((AbstractTreeNode)nodeDescriptor).isAlwaysShowPlus(); } + @Override protected final boolean isAutoExpandNode(NodeDescriptor nodeDescriptor) { StructureViewModel model = myStructureModel; if (model instanceof TreeModelWrapper) { @@ -111,6 +115,7 @@ public class StructureTreeBuilder extends AbstractTreeBuilder { return super.isAutoExpandNode(parent == null ? nodeDescriptor : parent); } + @Override protected final boolean isSmartExpand() { StructureViewModel model = myStructureModel; if (model instanceof TreeModelWrapper) { @@ -122,6 +127,7 @@ public class StructureTreeBuilder extends AbstractTreeBuilder { return false; } + @Override @NotNull protected final ProgressIndicator createProgressIndicator() { return new StatusBarProgress(); @@ -136,6 +142,7 @@ public class StructureTreeBuilder extends AbstractTreeBuilder { myOutOfCodeBlockModificationCount = myModificationTracker.getOutOfCodeBlockModificationCount(); } + @Override public void childRemoved(@NotNull PsiTreeChangeEvent event) { PsiElement child = event.getOldChild(); if (child instanceof PsiWhiteSpace) return; //optimization @@ -143,12 +150,14 @@ public class StructureTreeBuilder extends AbstractTreeBuilder { childrenChanged(); } + @Override public void childAdded(@NotNull PsiTreeChangeEvent event) { PsiElement child = event.getNewChild(); if (child instanceof PsiWhiteSpace) return; //optimization childrenChanged(); } + @Override public void childReplaced(@NotNull PsiTreeChangeEvent event) { /** Test comment */ PsiElement oldChild = event.getOldChild(); @@ -157,10 +166,12 @@ public class StructureTreeBuilder extends AbstractTreeBuilder { childrenChanged(); } + @Override public void childMoved(@NotNull PsiTreeChangeEvent event) { childrenChanged(); } + @Override public void childrenChanged(@NotNull PsiTreeChangeEvent event) { childrenChanged(); } @@ -172,6 +183,7 @@ public class StructureTreeBuilder extends AbstractTreeBuilder { setupUpdateAlarm(); } + @Override public void propertyChanged(@NotNull PsiTreeChangeEvent event) { childrenChanged(); } @@ -180,6 +192,7 @@ public class StructureTreeBuilder extends AbstractTreeBuilder { private void setupUpdateAlarm() { myUpdateAlarm.cancelAllRequests(); myUpdateAlarm.addRequest(new Runnable() { + @Override public void run() { if (!isDisposed() && !myProject.isDisposed()) { addRootToUpdate(); @@ -189,14 +202,17 @@ public class StructureTreeBuilder extends AbstractTreeBuilder { } final void addRootToUpdate() { - AbstractTreeStructure structure = getTreeStructure(); - if (structure.hasSomethingToCommit()) { - structure.commit(); - } - ((SmartTreeStructure)structure).rebuildTree(); - getUpdater().addSubtreeToUpdate(getRootNode()); + final AbstractTreeStructure structure = getTreeStructure(); + structure.asyncCommit().doWhenDone(new Runnable() { + @Override + public void run() { + ((SmartTreeStructure)structure).rebuildTree(); + getUpdater().addSubtreeToUpdate(getRootNode()); + } + }); } + @Override @NotNull protected final AbstractTreeNode createSearchingTreeNodeWrapper() { return new StructureViewComponent.StructureViewTreeElementWrapper(null,null, null); diff --git a/platform/lang-impl/src/com/intellij/ide/todo/TodoTreeStructure.java b/platform/lang-impl/src/com/intellij/ide/todo/TodoTreeStructure.java index 67687fc6da10..5d68288127c8 100644 --- a/platform/lang-impl/src/com/intellij/ide/todo/TodoTreeStructure.java +++ b/platform/lang-impl/src/com/intellij/ide/todo/TodoTreeStructure.java @@ -21,10 +21,12 @@ import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.ide.util.treeView.AbstractTreeStructureBase; import com.intellij.ide.util.treeView.NodeDescriptor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.ActionCallback; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.search.PsiTodoSearchHelper; import com.intellij.psi.search.TodoPattern; +import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.Iterator; @@ -123,14 +125,23 @@ public abstract class TodoTreeStructure extends AbstractTreeStructureBase implem return element == getRootElement() || element == mySummaryElement; } + @Override public final void commit() { PsiDocumentManager.getInstance(myProject).commitAllDocuments(); } + @Override public boolean hasSomethingToCommit() { return PsiDocumentManager.getInstance(myProject).hasUncommitedDocuments(); } + @NotNull + @Override + public ActionCallback asyncCommit() { + return asyncCommitDocuments(myProject); + } + + @Override public final Object getRootElement(){ return myRootElement; } @@ -147,6 +158,7 @@ public abstract class TodoTreeStructure extends AbstractTreeStructureBase implem return myTodoFilter; } + @Override public List getProviders() { return Collections.emptyList(); } @@ -155,6 +167,7 @@ public abstract class TodoTreeStructure extends AbstractTreeStructureBase implem myAreModulesShown = state; } + @Override public boolean isModulesShown() { return myAreModulesShown; } diff --git a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeBuilder.java b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeBuilder.java index 3d2678e4c621..3c8b22af5096 100644 --- a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeBuilder.java +++ b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeBuilder.java @@ -44,15 +44,15 @@ public class AbstractTreeBuilder implements Disposable { @NonNls private static final String TREE_BUILDER = "TreeBuilder"; public static final boolean DEFAULT_UPDATE_INACTIVE = true; - public AbstractTreeBuilder(JTree tree, - DefaultTreeModel treeModel, + public AbstractTreeBuilder(@NotNull JTree tree, + @NotNull DefaultTreeModel treeModel, AbstractTreeStructure treeStructure, @Nullable Comparator comparator) { this(tree, treeModel, treeStructure, comparator, DEFAULT_UPDATE_INACTIVE); } - public AbstractTreeBuilder(JTree tree, - DefaultTreeModel treeModel, + public AbstractTreeBuilder(@NotNull JTree tree, + @NotNull DefaultTreeModel treeModel, AbstractTreeStructure treeStructure, @Nullable Comparator comparator, boolean updateIfInactive) { @@ -63,10 +63,9 @@ public class AbstractTreeBuilder implements Disposable { } - - protected void init(final JTree tree, - final DefaultTreeModel treeModel, - final AbstractTreeStructure treeStructure, + protected void init(@NotNull JTree tree, + @NotNull DefaultTreeModel treeModel, + AbstractTreeStructure treeStructure, @Nullable final Comparator comparator, final boolean updateIfInactive) { @@ -78,6 +77,7 @@ public class AbstractTreeBuilder implements Disposable { setPassthroughMode(isUnitTestingMode()); } + @NotNull protected AbstractTreeUi createUi() { return new AbstractTreeUi(); } @@ -131,10 +131,12 @@ public class AbstractTreeBuilder implements Disposable { } + @NotNull protected AbstractTreeNode createSearchingTreeNodeWrapper() { return new AbstractTreeNodeWrapper(); } + @NotNull public final AbstractTreeBuilder setClearOnHideDelay(final long clearOnHideDelay) { if (isDisposed()) return this; @@ -216,21 +218,19 @@ public class AbstractTreeBuilder implements Disposable { return true; } - @Nullable public final JTree getTree() { if (isDisposed()) return null; return getUi().getTree(); } - @Nullable public final AbstractTreeStructure getTreeStructure() { if (isDisposed()) return null; return getUi().getTreeStructure(); } - public final void setTreeStructure(final AbstractTreeStructure structure) { + public final void setTreeStructure(@NotNull AbstractTreeStructure structure) { if (isDisposed()) return; getUi().setTreeStructure(structure); @@ -286,21 +286,21 @@ public class AbstractTreeBuilder implements Disposable { final ActionCallback result = new ActionCallback(); - getUi().invokeLaterIfNeeded(new Runnable() { + getUi().invokeLaterIfNeeded(false, new Runnable() { @Override public void run() { - if (!updateStructure) { - getUi().queueUpdate(element, false).notify(result); - } - else { + if (updateStructure) { if (forceResort) { getUi().incComparatorStamp(); } getUi().queueUpdate(element, true).notify(result); } + else { + getUi().queueUpdate(element, false).notify(result); + } } - }, false); + }); return result; @@ -339,7 +339,7 @@ public class AbstractTreeBuilder implements Disposable { return null; } - protected void expandNodeChildren(final DefaultMutableTreeNode node) { + protected void expandNodeChildren(@NotNull DefaultMutableTreeNode node) { if (isDisposed()) return; getUi().doExpandNodeChildren(node); @@ -426,7 +426,7 @@ public class AbstractTreeBuilder implements Disposable { } } - protected void yield(Runnable runnable) { + protected void yield(@NotNull Runnable runnable) { if (isDisposed()) return; if (myUi.isPassthroughMode()) { @@ -445,7 +445,7 @@ public class AbstractTreeBuilder implements Disposable { return true; } - protected void runBackgroundLoading(final Runnable runnable) { + protected void runBackgroundLoading(@NotNull final Runnable runnable) { if (isDisposed()) return; final Application app = ApplicationManager.getApplication(); @@ -462,7 +462,7 @@ public class AbstractTreeBuilder implements Disposable { } } - protected void updateAfterLoadedInBackground(Runnable runnable) { + protected void updateAfterLoadedInBackground(@NotNull Runnable runnable) { if (isDisposed()) return; if (myUi.isPassthroughMode()) { @@ -473,12 +473,14 @@ public class AbstractTreeBuilder implements Disposable { } } + @NotNull public final ActionCallback getIntialized() { if (isDisposed()) return new ActionCallback.Rejected(); return myUi.getInitialized(); } + @NotNull public final ActionCallback getReady(Object requestor) { if (isDisposed()) return new ActionCallback.Rejected(); @@ -501,18 +503,21 @@ public class AbstractTreeBuilder implements Disposable { getUi().expandAll(onDone); } + @NotNull public ActionCallback cancelUpdate() { if (isDisposed()) return new ActionCallback.Rejected(); return getUi().cancelUpdate(); } - public ActionCallback batch(Progressive progressive) { + @NotNull + public ActionCallback batch(@NotNull Progressive progressive) { if (isDisposed()) return new ActionCallback.Rejected(); return getUi().batch(progressive); } + @NotNull public AsyncResult revalidateElement(Object element) { if (isDisposed()) return new AsyncResult.Rejected(); @@ -553,7 +558,7 @@ public class AbstractTreeBuilder implements Disposable { myUi = null; } - protected boolean updateNodeDescriptor(final NodeDescriptor descriptor) { + protected boolean updateNodeDescriptor(@NotNull NodeDescriptor descriptor) { if (isDisposed()) return false; AbstractTreeUi ui = getUi(); @@ -576,7 +581,7 @@ public class AbstractTreeBuilder implements Disposable { } @NotNull - public final Set getSelectedElements(Class elementClass) { + public final Set getSelectedElements(@NotNull Class elementClass) { Set result = new HashSet(); for (Object o : getSelectedElements()) { Object each = transformElement(o); @@ -599,18 +604,18 @@ public class AbstractTreeBuilder implements Disposable { } @Nullable - public static AbstractTreeBuilder getBuilderFor(JTree tree) { + public static AbstractTreeBuilder getBuilderFor(@NotNull JTree tree) { final WeakReference ref = (WeakReference)tree.getClientProperty(TREE_BUILDER); return ref != null ? (AbstractTreeBuilder)ref.get() : null; } @Nullable - public final Object accept(Class nodeClass, TreeVisitor visitor) { + public final Object accept(@NotNull Class nodeClass, @NotNull TreeVisitor visitor) { return accept(nodeClass, getRootElement(), visitor); } @Nullable - private Object accept(Class nodeClass, Object element, TreeVisitor visitor) { + private Object accept(@NotNull Class nodeClass, Object element, @NotNull TreeVisitor visitor) { if (element == null) return null; if (nodeClass.isAssignableFrom(element.getClass())) { @@ -626,7 +631,7 @@ public class AbstractTreeBuilder implements Disposable { return null; } - public boolean select(Class nodeClass, TreeVisitor visitor, @Nullable Runnable onDone, boolean addToSelection) { + public boolean select(@NotNull Class nodeClass, @NotNull TreeVisitor visitor, @Nullable Runnable onDone, boolean addToSelection) { final Object element = accept(nodeClass, visitor); if (element != null) { select(element, onDone, addToSelection); @@ -647,13 +652,12 @@ public class AbstractTreeBuilder implements Disposable { return app != null && app.isUnitTestMode(); } - public static boolean isToPaintSelection(JTree tree) { + public static boolean isToPaintSelection(@NotNull JTree tree) { AbstractTreeBuilder builder = getBuilderFor(tree); return builder == null || builder.getUi() == null || builder.getUi().isToPaintSelection(); } class UserRunnable implements Runnable { - private final Runnable myRunnable; public UserRunnable(Runnable runnable) { diff --git a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeStructure.java b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeStructure.java index 512d38cb0a6d..8cb7fb3f2a98 100644 --- a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeStructure.java +++ b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeStructure.java @@ -16,7 +16,10 @@ package com.intellij.ide.util.treeView; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.AsyncResult; +import com.intellij.psi.PsiDocumentManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,6 +35,30 @@ public abstract class AbstractTreeStructure { public abstract void commit(); public abstract boolean hasSomethingToCommit(); + @NotNull + public static ActionCallback asyncCommitDocuments(@NotNull Project project) { + PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); + if (!documentManager.hasUncommitedDocuments()) { + return new ActionCallback.Done(); + } + final ActionCallback callback = new ActionCallback(); + documentManager.performWhenAllCommitted(callback.createSetDoneRunnable()); + return callback; + } + + /** + * @return callback which is set to {@link ActionCallback#setDone()} when the tree structure is committed. + * By default it just calls {@link #commit()} synchronously but it is desirable to override it + * to provide asynchronous commit to the tree structure to make it more responsible. + * E.g. when you should commit all documents during the {@link #commit()}, + * you can use {@link #asyncCommitDocuments(Project)} to do it asynchronously. + */ + @NotNull + public ActionCallback asyncCommit() { + if (hasSomethingToCommit()) commit(); + return new ActionCallback.Done(); + } + public boolean isToBuildChildrenInBackground(Object element){ return false; } @@ -47,63 +74,4 @@ public abstract class AbstractTreeStructure { public AsyncResult revalidateElement(Object element) { return new AsyncResult.Done(element); } - - public static class Delegate extends AbstractTreeStructure { - private final AbstractTreeStructure myDelegee; - - public Delegate(AbstractTreeStructure delegee) { - myDelegee = delegee; - } - - @Override - public Object getRootElement() { - return myDelegee.getRootElement(); - } - - @Override - public Object[] getChildElements(Object element) { - return myDelegee.getChildElements(element); - } - - @Override - public Object getParentElement(Object element) { - return myDelegee.getParentElement(element); - } - - @NotNull - @Override - public NodeDescriptor createDescriptor(Object element, NodeDescriptor parentDescriptor) { - return myDelegee.createDescriptor(element, parentDescriptor); - } - - @Override - public void commit() { - myDelegee.commit(); - } - - @Override - public boolean hasSomethingToCommit() { - return myDelegee.hasSomethingToCommit(); - } - - @Override - public boolean isToBuildChildrenInBackground(Object element) { - return myDelegee.isToBuildChildrenInBackground(element); - } - - @Override - public boolean isAlwaysLeaf(Object element) { - return myDelegee.isAlwaysLeaf(element); - } - - @Override - public AsyncResult revalidateElement(Object element) { - return myDelegee.revalidateElement(element); - } - - public AbstractTreeStructure getOriginalStructure() { - return myDelegee; - } - } - } \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java index be73361dec88..3faecccb6f28 100644 --- a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java +++ b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java @@ -39,6 +39,7 @@ import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; import com.intellij.util.ui.update.Activatable; import com.intellij.util.ui.update.UiNotifyConnector; +import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -53,12 +54,14 @@ import java.util.*; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; public class AbstractTreeUi { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.treeView.AbstractTreeBuilder"); protected JTree myTree;// protected for TestNG - @SuppressWarnings({"WeakerAccess"}) protected DefaultTreeModel myTreeModel; + @SuppressWarnings({"WeakerAccess"}) + protected DefaultTreeModel myTreeModel; private AbstractTreeStructure myTreeStructure; private AbstractTreeUpdater myUpdater; @@ -67,8 +70,6 @@ public class AbstractTreeUi { @Override public int compare(TreeNode n1, TreeNode n2) { if (isLoadingNode(n1) || isLoadingNode(n2)) return 0; - //todo kirillk hard to undertstand why nodes may be null, just avoid NPEs - if (n1 == null || n2 == null) return 0; NodeDescriptor nodeDescriptor1 = getDescriptorFrom(n1); NodeDescriptor nodeDescriptor2 = getDescriptorFrom(n2); @@ -84,8 +85,8 @@ public class AbstractTreeUi { long myLastComparatorStamp; private DefaultMutableTreeNode myRootNode; - private final HashMap myElementToNodeMap = new HashMap(); - private final HashSet myUnbuiltNodes = new HashSet(); + private final Map myElementToNodeMap = new HashMap(); + private final Set myUnbuiltNodes = new HashSet(); private TreeExpansionListener myExpansionListener; private MySelectionListener mySelectionListener; @@ -117,7 +118,7 @@ public class AbstractTreeUi { private UpdaterTreeState myUpdaterState; private AbstractTreeBuilder myBuilder; - private final Set myUpdatingChildren = new HashSet(); + private final Set myUpdatingChildren = new THashSet(); private boolean myCanYield = false; @@ -204,7 +205,7 @@ public class AbstractTreeUi { private UiActivityMonitor myActivityMonitor; @NonNls private UiActivity myActivityId; - protected void init(AbstractTreeBuilder builder, + protected void init(@NotNull AbstractTreeBuilder builder, @NotNull JTree tree, @NotNull DefaultTreeModel treeModel, AbstractTreeStructure treeStructure, @@ -216,10 +217,10 @@ public class AbstractTreeUi { myActivityMonitor = UiActivityMonitor.getInstance(); myActivityId = new UiActivity.AsyncBgOperation("TreeUi" + this); addModelListenerToDianoseAccessOutsideEdt(); - TREE_NODE_WRAPPER = getBuilder().createSearchingTreeNodeWrapper(); + TREE_NODE_WRAPPER = builder.createSearchingTreeNodeWrapper(); myTree.setModel(myTreeModel); setRootNode((DefaultMutableTreeNode)treeModel.getRoot()); - setTreeStructure(treeStructure); + myTreeStructure = treeStructure; myNodeDescriptorComparator = comparator; myUpdateIfInactive = updateIfInactive; @@ -335,7 +336,7 @@ public class AbstractTreeUi { runnable.run(); } else { - invokeLaterIfNeeded(runnable, false); + invokeLaterIfNeeded(false, runnable); } } } @@ -355,12 +356,12 @@ public class AbstractTreeUi { cleanup.run(); } else { - invokeLaterIfNeeded(cleanup, false); + invokeLaterIfNeeded(false, cleanup); } } @NotNull - public ActionCallback invokeLaterIfNeeded(@NotNull final Runnable runnable, boolean forceEdt) { + ActionCallback invokeLaterIfNeeded(boolean forceEdt, @NotNull final Runnable runnable) { final ActionCallback result = new ActionCallback(); Runnable actual = new Runnable() { @@ -368,7 +369,8 @@ public class AbstractTreeUi { public void run() { if (isReleased()) { result.setRejected(); - } else { + } + else { runnable.run(); result.setDone(); } @@ -377,7 +379,8 @@ public class AbstractTreeUi { if (forceEdt) { UIUtil.invokeLaterIfNeeded(actual); - } else { + } + else { if (isPassthroughMode() || !isEdt() && !isTreeShowing() && !myWasEverShown) { actual.run(); } @@ -497,14 +500,19 @@ public class AbstractTreeUi { return myBuilder == null; } - protected void doExpandNodeChildren(@NotNull final DefaultMutableTreeNode node) { + void doExpandNodeChildren(@NotNull final DefaultMutableTreeNode node) { if (!myUnbuiltNodes.contains(node)) return; if (isLoadedInBackground(getElementFor(node))) return; AbstractTreeStructure structure = getTreeStructure(); - if (structure.hasSomethingToCommit()) structure.commit(); - addSubtreeToUpdate(node); - getUpdater().performUpdate(); + structure.asyncCommit().doWhenDone(new Runnable() { + @Override + public void run() { + addSubtreeToUpdate(node); + getUpdater().performUpdate(); + } + }); + //if (structure.hasSomethingToCommit()) structure.commit(); } public final AbstractTreeStructure getTreeStructure() { @@ -652,6 +660,7 @@ public class AbstractTreeUi { getBuilder().queueUpdateFrom(getTreeStructure().getRootElement(), true); } + @NotNull protected AbstractTreeBuilder getBuilder() { return myBuilder; } @@ -726,12 +735,21 @@ public class AbstractTreeUi { willUpdate = myUnbuiltNodes.contains(getRootNode()); expand(getRootNode(), true); } + ActionCallback callback; if (!willUpdate) { - updateNodeChildren(getRootNode(), pass, null, false, false, false, true, true); + callback = updateNodeChildren(getRootNode(), pass, null, false, false, false, true, true); } - if (getRootNode().getChildCount() == 0) { - myTreeModel.nodeChanged(getRootNode()); + else { + callback = new ActionCallback.Done(); } + callback.doWhenDone(new Runnable() { + @Override + public void run() { + if (getRootNode().getChildCount() == 0) { + myTreeModel.nodeChanged(getRootNode()); + } + } + }); } }; @@ -739,13 +757,13 @@ public class AbstractTreeUi { queueToBackground(build, update).doWhenProcessed(new Runnable() { @Override public void run() { - invokeLaterIfNeeded(new Runnable() { + invokeLaterIfNeeded(false, new Runnable() { @Override public void run() { myRootNodeInitialized = true; processNodeActionsIfReady(myRootNode); } - }, false); + }); } }); } @@ -843,19 +861,20 @@ public class AbstractTreeUi { final AsyncResult result = new AsyncResult(); if (now || isPassthroughMode()) { - result.setDone(_update(nodeDescriptor)); - } else { + result.setDone(update(nodeDescriptor)); + } + else { Object element = getElementFromDescriptor(nodeDescriptor); boolean bgLoading = getTreeStructure().isToBuildChildrenInBackground(element); boolean edt = isEdt(); if (bgLoading) { if (edt) { - final Ref changes = new Ref(false); + final AtomicBoolean changes = new AtomicBoolean(); queueToBackground(new Runnable() { @Override public void run() { - changes.set(_update(nodeDescriptor)); + changes.set(update(nodeDescriptor)); } }, new Runnable() { @Override @@ -866,25 +885,25 @@ public class AbstractTreeUi { ); } else { - result.setDone(_update(nodeDescriptor)); + result.setDone(update(nodeDescriptor)); } } else { if (edt || !myWasEverShown) { - result.setDone(_update(nodeDescriptor)); + result.setDone(update(nodeDescriptor)); } else { - invokeLaterIfNeeded(new Runnable() { + invokeLaterIfNeeded(false, new Runnable() { @Override public void run() { execute(new Runnable() { @Override public void run() { - result.setDone(_update(nodeDescriptor)); + result.setDone(update(nodeDescriptor)); } }); } - }, false); + }); } } } @@ -893,7 +912,7 @@ public class AbstractTreeUi { @Override public void run(final Boolean changes) { if (changes) { - invokeLaterIfNeeded(new Runnable() { + invokeLaterIfNeeded(false, new Runnable() { @Override public void run() { Object element = nodeDescriptor.getElement(); @@ -905,7 +924,7 @@ public class AbstractTreeUi { } } } - }, false); + }); } } }); @@ -914,9 +933,9 @@ public class AbstractTreeUi { return result; } - private boolean _update(@NotNull final NodeDescriptor nodeDescriptor) { + private boolean update(@NotNull final NodeDescriptor nodeDescriptor) { try { - final Ref update = new Ref(); + final AtomicBoolean update = new AtomicBoolean(); try { acquireLock(); execute(new Runnable() { @@ -1073,13 +1092,14 @@ public class AbstractTreeUi { } updateNodeChildren(node, pass, null, false, canSmartExpand, forceUpdate, false, pass.isUpdateChildren()); - } else { + } + else { updateRow(0, pass); } } private void updateRow(final int row, @NotNull final TreeUpdatePass pass) { - invokeLaterIfNeeded(new Runnable() { + invokeLaterIfNeeded(false, new Runnable() { @Override public void run() { if (row >= getTree().getRowCount()) return; @@ -1106,7 +1126,7 @@ public class AbstractTreeUi { } } } - }, false); + }); } private boolean isToBuildInBackground(NodeDescriptor descriptor) { @@ -1129,8 +1149,9 @@ public class AbstractTreeUi { } protected void doUpdateNode(@NotNull final DefaultMutableTreeNode node) { - if (!(node.getUserObject() instanceof NodeDescriptor)) return; - final NodeDescriptor descriptor = getDescriptorFrom(node); + Object userObject = node.getUserObject(); + if (!(userObject instanceof NodeDescriptor)) return; + final NodeDescriptor descriptor = (NodeDescriptor)userObject; final Object prevElement = getElementFromDescriptor(descriptor); if (prevElement == null) return; update(descriptor, false).doWhenDone(new AsyncResult.Handler() { @@ -1153,118 +1174,141 @@ public class AbstractTreeUi { return getBuilder().getTreeStructureElement(descriptor); } - private void updateNodeChildren(@NotNull final DefaultMutableTreeNode node, - @NotNull final TreeUpdatePass pass, - @Nullable final LoadedChildren loadedChildren, - final boolean forcedNow, - final boolean toSmartExpand, - final boolean forceUpdate, - final boolean descriptorIsUpToDate, final boolean updateChildren) { - - removeFromCancelled(node); - - execute(new Runnable() { + @NotNull + private ActionCallback updateNodeChildren(@NotNull final DefaultMutableTreeNode node, + @NotNull final TreeUpdatePass pass, + @Nullable final LoadedChildren loadedChildren, + final boolean forcedNow, + final boolean toSmartExpand, + final boolean forceUpdate, + final boolean descriptorIsUpToDate, + final boolean updateChildren) { + AbstractTreeStructure treeStructure = getTreeStructure(); + ActionCallback result = treeStructure.asyncCommit(); + result.doWhenDone(new Runnable() { @Override public void run() { try { - - AbstractTreeStructure treeStructure = getTreeStructure(); - if (treeStructure.hasSomethingToCommit()) treeStructure.commit(); - - final NodeDescriptor descriptor = getDescriptorFrom(node); - if (descriptor == null) { - removeFromUnbuilt(node); - removeLoading(node, true); - return; - } - - boolean descriptorIsReady = descriptorIsUpToDate || pass.isUpdated(descriptor); - - final boolean wasExpanded = myTree.isExpanded(new TreePath(node.getPath())) || isAutoExpand(node); - final boolean wasLeaf = node.getChildCount() == 0; - - - boolean bgBuild = isToBuildInBackground(descriptor); - boolean notRequiredToUpdateChildren = !forcedNow && !wasExpanded; - - if (notRequiredToUpdateChildren && forceUpdate && !wasExpanded) { - boolean alwaysPlus = getBuilder().isAlwaysShowPlus(descriptor); - if (alwaysPlus && wasLeaf) { - notRequiredToUpdateChildren = false; + removeFromCancelled(node); + execute(new Runnable() { + @Override + public void run() { + doUpdateChildren(node, pass, loadedChildren, forcedNow, toSmartExpand, forceUpdate, descriptorIsUpToDate, updateChildren); } - else { - notRequiredToUpdateChildren = alwaysPlus; - if (notRequiredToUpdateChildren && !wasExpanded && !myUnbuiltNodes.contains(node)) { - removeChildren(node); - } - } - } - - final Ref preloaded = new Ref(loadedChildren); - - if (notRequiredToUpdateChildren) { - if (myUnbuiltNodes.contains(node) && node.getChildCount() == 0) { - insertLoadingNode(node, true); - } - - if (!descriptorIsReady) { - update(descriptor, false); - } - - return; - } - - if (!forcedNow) { - if (!bgBuild) { - if (myUnbuiltNodes.contains(node)) { - if (!descriptorIsReady) { - update(descriptor, true); - descriptorIsReady = true; - } - - if (processAlwaysLeaf(node) || !updateChildren) return; - - Pair unbuilt = processUnbuilt(node, descriptor, pass, wasExpanded, null); - - if (unbuilt.getFirst()) return; - preloaded.set(unbuilt.getSecond()); - } - } - } - - - final boolean childForceUpdate = isChildNodeForceUpdate(node, forceUpdate, wasExpanded); - - if (!forcedNow && isToBuildInBackground(descriptor)) { - boolean alwaysLeaf = processAlwaysLeaf(node); - queueBackgroundUpdate( - new UpdateInfo(descriptor, pass, canSmartExpand(node, toSmartExpand), wasExpanded, childForceUpdate, descriptorIsReady, - !alwaysLeaf && updateChildren), node); - } - else { - if (!descriptorIsReady) { - update(descriptor, false).doWhenDone(new Runnable() { - @Override - public void run() { - if (processAlwaysLeaf(node) || !updateChildren) return; - updateNodeChildrenNow(node, pass, preloaded.get(), toSmartExpand, wasExpanded, childForceUpdate); - } - }); - } - else { - if (processAlwaysLeaf(node) || !updateChildren) return; - - updateNodeChildrenNow(node, pass, preloaded.get(), toSmartExpand, wasExpanded, childForceUpdate); - } - } + }); } - finally { - if (!isReleased()) { - processNodeActionsIfReady(node); + catch (ProcessCanceledException e) { + addToCancelled(node); + throw e; + } + } + }); + + return result; + } + + private void doUpdateChildren(@NotNull final DefaultMutableTreeNode node, + @NotNull final TreeUpdatePass pass, + @Nullable final LoadedChildren loadedChildren, + boolean forcedNow, + final boolean toSmartExpand, + boolean forceUpdate, + boolean descriptorIsUpToDate, + final boolean updateChildren) { + try { + + final NodeDescriptor descriptor = getDescriptorFrom(node); + if (descriptor == null) { + removeFromUnbuilt(node); + removeLoading(node, true); + return; + } + + boolean descriptorIsReady = descriptorIsUpToDate || pass.isUpdated(descriptor); + + final boolean wasExpanded = myTree.isExpanded(new TreePath(node.getPath())) || isAutoExpand(node); + final boolean wasLeaf = node.getChildCount() == 0; + + + boolean bgBuild = isToBuildInBackground(descriptor); + boolean notRequiredToUpdateChildren = !forcedNow && !wasExpanded; + + if (notRequiredToUpdateChildren && forceUpdate) { + boolean alwaysPlus = getBuilder().isAlwaysShowPlus(descriptor); + if (alwaysPlus && wasLeaf) { + notRequiredToUpdateChildren = false; + } + else { + notRequiredToUpdateChildren = alwaysPlus; + if (notRequiredToUpdateChildren && !myUnbuiltNodes.contains(node)) { + removeChildren(node); } } } - }, node); + + final AtomicReference preloaded = new AtomicReference(loadedChildren); + + if (notRequiredToUpdateChildren) { + if (myUnbuiltNodes.contains(node) && node.getChildCount() == 0) { + insertLoadingNode(node, true); + } + + if (!descriptorIsReady) { + update(descriptor, false); + } + + return; + } + + if (!forcedNow && !bgBuild && myUnbuiltNodes.contains(node)) { + if (!descriptorIsReady) { + update(descriptor, true); + descriptorIsReady = true; + } + + if (processAlwaysLeaf(node) || !updateChildren) { + return; + } + + Pair unbuilt = processUnbuilt(node, descriptor, pass, wasExpanded, null); + + if (unbuilt.getFirst()) { + return; + } + preloaded.set(unbuilt.getSecond()); + } + + + final boolean childForceUpdate = isChildNodeForceUpdate(node, forceUpdate, wasExpanded); + + if (!forcedNow && isToBuildInBackground(descriptor)) { + boolean alwaysLeaf = processAlwaysLeaf(node); + queueBackgroundUpdate( + new UpdateInfo(descriptor, pass, canSmartExpand(node, toSmartExpand), wasExpanded, childForceUpdate, descriptorIsReady, + !alwaysLeaf && updateChildren), node); + } + else { + if (!descriptorIsReady) { + update(descriptor, false).doWhenDone(new Runnable() { + @Override + public void run() { + if (processAlwaysLeaf(node) || !updateChildren) return; + updateNodeChildrenNow(node, pass, preloaded.get(), toSmartExpand, wasExpanded, childForceUpdate); + } + }); + } + else { + if (processAlwaysLeaf(node) || !updateChildren) return; + + updateNodeChildrenNow(node, pass, preloaded.get(), toSmartExpand, wasExpanded, childForceUpdate); + } + } + } + finally { + if (!isReleased()) { + processNodeActionsIfReady(node); + } + } } private boolean processAlwaysLeaf(@NotNull DefaultMutableTreeNode node) { @@ -1367,33 +1411,38 @@ public class AbstractTreeUi { } collectNodesToInsert(descriptor, elementToIndexMap, node, expanded, loadedChildren) - .doWhenDone(new AsyncResult.Handler>() { + .doWhenDone(new AsyncResult.Handler>() { @Override - public void run(@NotNull ArrayList nodesToInsert) { + public void run(@NotNull final List nodesToInsert) { insertNodesInto(nodesToInsert, node); - updateNodesToInsert(nodesToInsert, pass, canSmartExpand, isChildNodeForceUpdate(node, forceUpdate, expanded)); - removeLoading(node, false); - removeFromUpdatingChildren(node); - - if (node.getChildCount() > 0) { - if (expanded) { - expand(node, canSmartExpand); - } - } - - if (!canInitiateNewActivity()) { - throw new ProcessCanceledException(); - } - - final Object element = getElementFor(node); - addNodeAction(element, new NodeAction() { + ActionCallback callback = updateNodesToInsert(nodesToInsert, pass, canSmartExpand, isChildNodeForceUpdate(node, forceUpdate, expanded)); + callback.doWhenDone(new Runnable() { @Override - public void onReady(@NotNull final DefaultMutableTreeNode node) { + public void run() { removeLoading(node, false); - } - }, false); + removeFromUpdatingChildren(node); - processNodeActionsIfReady(node); + if (node.getChildCount() > 0) { + if (expanded) { + expand(node, canSmartExpand); + } + } + + if (!canInitiateNewActivity()) { + throw new ProcessCanceledException(); + } + + final Object element = getElementFor(node); + addNodeAction(element, new NodeAction() { + @Override + public void onReady(@NotNull final DefaultMutableTreeNode node) { + removeLoading(node, false); + } + }, false); + + processNodeActionsIfReady(node); + } + }); } }).doWhenProcessed(new Runnable() { @Override @@ -1630,14 +1679,20 @@ public class AbstractTreeUi { } } - private void updateNodesToInsert(@NotNull final ArrayList nodesToInsert, - @NotNull TreeUpdatePass pass, - boolean canSmartExpand, - boolean forceUpdate) { - for (TreeNode aNodesToInsert : nodesToInsert) { - DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)aNodesToInsert; - updateNodeChildren(childNode, pass, null, false, canSmartExpand, forceUpdate, true, true); + @NotNull + private ActionCallback updateNodesToInsert(@NotNull final List nodesToInsert, + @NotNull TreeUpdatePass pass, + boolean canSmartExpand, + boolean forceUpdate) { + ActionCallback.Chunk chunk = new ActionCallback.Chunk(); + for (TreeNode node : nodesToInsert) { + DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)node; + ActionCallback callback = updateNodeChildren(childNode, pass, null, false, canSmartExpand, forceUpdate, true, true); + if (!callback.isDone()) { + chunk.add(callback); + } } + return chunk.getWhenProcessed(); } @NotNull @@ -1649,7 +1704,7 @@ public class AbstractTreeUi { final boolean wasExpaned, @Nullable final LoadedChildren preloaded) { - final ArrayList childNodes = TreeUtil.childrenToArray(node); + final List childNodes = TreeUtil.childrenToArray(node); return maybeYeild(new ActiveRunnable() { @NotNull @Override @@ -1780,11 +1835,7 @@ public class AbstractTreeUi { return result; } - private void execute(@NotNull Runnable runnable) { - execute(runnable, null); - } - - private void execute(@NotNull Runnable runnable, @Nullable DefaultMutableTreeNode node) throws ProcessCanceledException { + private void execute(@NotNull Runnable runnable) throws ProcessCanceledException { try { if (!canInitiateNewActivity()) { throw new ProcessCanceledException(); @@ -1797,9 +1848,6 @@ public class AbstractTreeUi { } } catch (ProcessCanceledException e) { - if (node != null) { - addToCancelled(node); - } if (!isReleased()) { setCancelRequested(true); resetToReady(); @@ -1828,7 +1876,7 @@ public class AbstractTreeUi { myResettingToReadyNow.set(true); - invokeLaterIfNeeded(new Runnable() { + invokeLaterIfNeeded(false, new Runnable() { @Override public void run() { if (!myResettingToReadyNow.get()) { @@ -1844,17 +1892,21 @@ public class AbstractTreeUi { resetToReadyNow().notify(result); } - }, false); + }); return result; } + @NotNull private ActionCallback resetToReadyNow() { if (isReleased()) return new ActionCallback.Rejected(); assertIsDispatchThread(); - DefaultMutableTreeNode[] uc = myUpdatingChildren.toArray(new DefaultMutableTreeNode[myUpdatingChildren.size()]); + DefaultMutableTreeNode[] uc; + synchronized (myUpdatingChildren) { + uc = myUpdatingChildren.toArray(new DefaultMutableTreeNode[myUpdatingChildren.size()]); + } for (DefaultMutableTreeNode each : uc) { resetIncompleteNode(each); } @@ -1878,7 +1930,9 @@ public class AbstractTreeUi { myNodeActions.clear(); myNodeChildrenActions.clear(); - myUpdatingChildren.clear(); + synchronized (myUpdatingChildren) { + myUpdatingChildren.clear(); + } myLoadedInBackground.clear(); myDeferredExpansions.clear(); @@ -1898,11 +1952,11 @@ public class AbstractTreeUi { return result; } - public void addToCancelled(DefaultMutableTreeNode node) { + public void addToCancelled(@NotNull DefaultMutableTreeNode node) { myCancelledBuild.put(node, node); } - public void removeFromCancelled(DefaultMutableTreeNode node) { + public void removeFromCancelled(@NotNull DefaultMutableTreeNode node) { myCancelledBuild.remove(node); } @@ -1966,12 +2020,6 @@ public class AbstractTreeUi { } public boolean isReady(boolean attempt) { - Boolean ready = _isReady(attempt); - return ready != null && ready.booleanValue(); - } - - @Nullable - public Boolean _isReady(boolean attempt) { if (attempt && myStateLock.isLocked()) return false; Boolean ready = checkValue(new Computable() { @@ -1979,29 +2027,31 @@ public class AbstractTreeUi { public Boolean compute() { return Boolean.valueOf(isIdle() && !hasPendingWork() && !isNodeActionsPending()); } - }, attempt, null); + }, attempt); return ready != null && ready.booleanValue(); } @Nullable - private Boolean checkValue(@NotNull Computable computable, boolean attempt, @Nullable Boolean defaultValue) { + private Boolean checkValue(@NotNull Computable computable, boolean attempt) { boolean toRelease = true; try { if (attempt) { if (!attemptLock()) { toRelease = false; - return defaultValue != null ? defaultValue : computable.compute(); + return computable.compute(); } - } else { + } + else { acquireLock(); } return computable.compute(); } catch (InterruptedException e) { LOG.info(e); - return defaultValue; - } finally { + return null; + } + finally { if (toRelease) { releaseLock(); } @@ -2077,58 +2127,53 @@ public class AbstractTreeUi { if (isReleased()) return; - Boolean ready = _isReady(true); - if (ready != null && ready.booleanValue()) { - myRevalidatedObjects.clear(); + boolean ready = isReady(true); + if (!ready) return; + myRevalidatedObjects.clear(); - setCancelRequested(false); - myResettingToReadyNow.set(false); + setCancelRequested(false); + myResettingToReadyNow.set(false); - myInitialized.setDone(); + myInitialized.setDone(); - if (canInitiateNewActivity()) { - if (myUpdaterState != null && !myUpdaterState.isProcessingNow()) { - UpdaterTreeState oldState = myUpdaterState; - if (!myUpdaterState.restore(null)) { - setUpdaterState(oldState); - } + if (canInitiateNewActivity()) { + if (myUpdaterState != null && !myUpdaterState.isProcessingNow()) { + UpdaterTreeState oldState = myUpdaterState; + if (!myUpdaterState.restore(null)) { + setUpdaterState(oldState); + } - if (!isReady()) { - return; + if (!isReady()) return; + } + } + + setHoldSize(false); + + if (myTree.isShowing()) { + if (getBuilder().isToEnsureSelectionOnFocusGained() && Registry.is("ide.tree.ensureSelectionOnFocusGained")) { + TreeUtil.ensureSelection(myTree); + } + } + + if (myInitialized.isDone()) { + if (isReleaseRequested() || isCancelProcessed()) { + myBusyObject.onReady(this); + } else { + myBusyObject.onReady(); + } + } + + if (canInitiateNewActivity()) { + TreePath[] selection = getTree().getSelectionPaths(); + Rectangle visible = getTree().getVisibleRect(); + if (selection != null) { + for (TreePath each : selection) { + Rectangle bounds = getTree().getPathBounds(each); + if (bounds != null && (visible.contains(bounds) || visible.intersects(bounds))) { + getTree().repaint(bounds); } } } - - setHoldSize(false); - - if (myTree.isShowing()) { - if (getBuilder().isToEnsureSelectionOnFocusGained() && Registry.is("ide.tree.ensureSelectionOnFocusGained")) { - TreeUtil.ensureSelection(myTree); - } - } - - if (myInitialized.isDone()) { - if (isReleaseRequested() || isCancelProcessed()) { - myBusyObject.onReady(this); - } else { - myBusyObject.onReady(); - } - } - - if (canInitiateNewActivity()) { - TreePath[] selection = getTree().getSelectionPaths(); - Rectangle visible = getTree().getVisibleRect(); - if (selection != null) { - for (TreePath each : selection) { - Rectangle bounds = getTree().getPathBounds(each); - if (bounds != null && (visible.contains(bounds) || visible.intersects(bounds))) { - getTree().repaint(bounds); - } - } - } - } - } else if (ready == null) { - scheduleMaybeReady(); } } @@ -2193,30 +2238,29 @@ public class AbstractTreeUi { } @NotNull - private AsyncResult> collectNodesToInsert(final NodeDescriptor descriptor, - @NotNull final MutualMap elementToIndexMap, - final DefaultMutableTreeNode parent, - final boolean addLoadingNode, - @NotNull final LoadedChildren loadedChildren) { - final AsyncResult> result = new AsyncResult>(); + private AsyncResult> collectNodesToInsert(final NodeDescriptor descriptor, + @NotNull final MutualMap elementToIndexMap, + final DefaultMutableTreeNode parent, + final boolean addLoadingNode, + @NotNull final LoadedChildren loadedChildren) { + final AsyncResult> result = new AsyncResult>(); - final ArrayList nodesToInsert = new ArrayList(); - final Collection allElements = elementToIndexMap.getKeys(); + final List nodesToInsert = new ArrayList(); + Collection allElements = elementToIndexMap.getKeys(); final ActionCallback processingDone = new ActionCallback(allElements.size()); for (final Object child : allElements) { Integer index = elementToIndexMap.getValue(child); - final Ref childDescr = new Ref(loadedChildren.getDescriptor(child)); boolean needToUpdate = false; - if (childDescr.get() == null) { - childDescr.set(getTreeStructure().createDescriptor(child, descriptor)); + NodeDescriptor loadedDesc = loadedChildren.getDescriptor(child); + final NodeDescriptor childDescr; + if (loadedDesc == null) { + childDescr = getTreeStructure().createDescriptor(child, descriptor); needToUpdate = true; } - - if (childDescr.get() == null) { - processingDone.setDone(); - continue; + else { + childDescr = loadedDesc; } if (index == null) { @@ -2224,14 +2268,14 @@ public class AbstractTreeUi { needToUpdate = true; } - childDescr.get().setIndex(index.intValue()); + childDescr.setIndex(index.intValue()); final ActionCallback update = new ActionCallback(); if (needToUpdate) { - update(childDescr.get(), false).doWhenDone(new AsyncResult.Handler() { + update(childDescr, false).doWhenDone(new AsyncResult.Handler() { @Override public void run(Boolean changes) { - loadedChildren.putDescriptor(child, childDescr.get(), changes); + loadedChildren.putDescriptor(child, childDescr, changes); update.setDone(); } }); @@ -2243,15 +2287,12 @@ public class AbstractTreeUi { update.doWhenDone(new Runnable() { @Override public void run() { - Object element = getElementFromDescriptor(childDescr.get()); - if (element == null) { - processingDone.setDone(); - } - else { + Object element = getElementFromDescriptor(childDescr); + if (element != null) { DefaultMutableTreeNode node = getNodeForElement(element, false); if (node == null || node.getParent() != parent) { - final DefaultMutableTreeNode childNode = createChildNode(childDescr.get()); - if (addLoadingNode || getBuilder().isAlwaysShowPlus(childDescr.get())) { + final DefaultMutableTreeNode childNode = createChildNode(childDescr); + if (addLoadingNode || getBuilder().isAlwaysShowPlus(childDescr)) { insertLoadingNode(childNode, true); } else { @@ -2260,8 +2301,8 @@ public class AbstractTreeUi { nodesToInsert.add(childNode); createMapping(element, childNode); } - processingDone.setDone(); } + processingDone.setDone(); } }); } @@ -2276,6 +2317,7 @@ public class AbstractTreeUi { return result; } + @NotNull protected DefaultMutableTreeNode createChildNode(final NodeDescriptor descriptor) { return new ElementNode(this, descriptor); } @@ -2301,13 +2343,13 @@ public class AbstractTreeUi { return getReady(this); } - private void addToUpdatingChildren(DefaultMutableTreeNode node) { + private void addToUpdatingChildren(@NotNull DefaultMutableTreeNode node) { synchronized (myUpdatingChildren) { myUpdatingChildren.add(node); } } - private void removeFromUpdatingChildren(DefaultMutableTreeNode node) { + private void removeFromUpdatingChildren(@NotNull DefaultMutableTreeNode node) { synchronized (myUpdatingChildren) { myUpdatingChildren.remove(node); } @@ -2391,7 +2433,7 @@ public class AbstractTreeUi { final ActionCallback done = new ActionCallback(); - invokeLaterIfNeeded(new Runnable() { + invokeLaterIfNeeded(false, new Runnable() { @Override public void run() { if (isReleased()) { @@ -2415,7 +2457,7 @@ public class AbstractTreeUi { maybeReady(); } - }, false); + }); if (isEdt() || isPassthroughMode()) { maybeReady(); @@ -2503,15 +2545,7 @@ public class AbstractTreeUi { } public boolean isCancelProcessed() { - Computable computable = new Computable() { - @Override - public Boolean compute() { - return Boolean.valueOf(myCancelRequest.get() || myResettingToReadyNow.get()); - } - }; - //Boolean processed = checkValue(computable, true, null); - Boolean processed = computable.compute(); - return processed != null && processed.booleanValue(); + return myCancelRequest.get() || myResettingToReadyNow.get(); } public boolean isToPaintSelection() { @@ -2644,7 +2678,7 @@ public class AbstractTreeUi { final Runnable finalizeRunnable = new Runnable() { @Override public void run() { - invokeLaterIfNeeded(new Runnable() { + invokeLaterIfNeeded(false, new Runnable() { @Override public void run() { if (isReleased()) return; @@ -2657,7 +2691,7 @@ public class AbstractTreeUi { processNodeActionsIfReady(nodeToProcessActions[0]); } } - }, false); + }); } }; @@ -2700,7 +2734,6 @@ public class AbstractTreeUi { } children.set(loaded); - } @NotNull @@ -2742,21 +2775,24 @@ public class AbstractTreeUi { } } - updateNodeChildren(node, updateInfo.getPass(), children.get(), true, updateInfo.isCanSmartExpand(), updateInfo.isForceUpdate(), - true, true); + ActionCallback callback = updateNodeChildren(node, updateInfo.getPass(), children.get(), + true, updateInfo.isCanSmartExpand(), updateInfo.isForceUpdate(), true, true); + callback.doWhenDone(new Runnable() { + @Override + public void run() { + if (isRerunNeeded(updateInfo.getPass())) { + getUpdater().requeue(updateInfo.getPass()); + return; + } + Object element = elementFromDescriptor.get(); - if (isRerunNeeded(updateInfo.getPass())) { - getUpdater().requeue(updateInfo.getPass()); - return; - } - - Object element = elementFromDescriptor.get(); - - if (element != null) { - removeLoading(node, false); - nodeToProcessActions[0] = node; - } + if (element != null) { + removeLoading(node, false); + nodeToProcessActions[0] = node; + } + } + }); } }; queueToBackground(buildRunnable, updateRunnable).doWhenProcessed(finalizeRunnable).doWhenRejected(new Runnable() { @@ -2968,20 +3004,15 @@ public class AbstractTreeUi { final boolean canSmartExpand, final boolean forceUpdate, @Nullable LoadedChildren parentPreloadedChildren) { - - final ActionCallback result = new ActionCallback(); - if (pass.isExpired()) { return new ActionCallback.Rejected(); } - final Ref childDesc = new Ref(childDescriptor); - - if (childDesc.get() == null) { + if (childDescriptor == null) { pass.expire(); return new ActionCallback.Rejected(); } - final Object oldElement = getElementFromDescriptor(childDesc.get()); + final Object oldElement = getElementFromDescriptor(childDescriptor); if (oldElement == null) { pass.expire(); return new ActionCallback.Rejected(); @@ -2992,22 +3023,25 @@ public class AbstractTreeUi { update.setDone(parentPreloadedChildren.isUpdated(oldElement)); } else { - update = update(childDesc.get(), false); + update = update(childDescriptor, false); } + final ActionCallback result = new ActionCallback(); + final Ref childDesc = new Ref(childDescriptor); + update.doWhenDone(new AsyncResult.Handler() { @Override public void run(Boolean isChanged) { - final Ref changes = new Ref(isChanged); - - final Ref forceRemapping = new Ref(false); + final AtomicBoolean changes = new AtomicBoolean(isChanged); + final AtomicBoolean forceRemapping = new AtomicBoolean(); final Ref newElement = new Ref(getElementFromDescriptor(childDesc.get())); - final Integer index = - newElement.get() != null ? elementToIndexMap.getValue(getBuilder().getTreeStructureElement(childDesc.get())) : null; + final Integer index = newElement.get() == null ? null : elementToIndexMap.getValue(getBuilder().getTreeStructureElement(childDesc.get())); final AsyncResult updateIndexDone = new AsyncResult(); - final ActionCallback indexReady = new ActionCallback(); - if (index != null) { + if (index == null) { + updateIndexDone.setDone(); + } + else { final Object elementFromMap = elementToIndexMap.getKey(index); if (elementFromMap != newElement.get() && elementFromMap.equals(newElement.get())) { if (isInStructure(elementFromMap) && isInStructure(newElement.get())) { @@ -3046,20 +3080,13 @@ public class AbstractTreeUi { changes.set(true); } childDesc.get().setIndex(index.intValue()); - indexReady.setDone(); } }); } - else { - updateIndexDone.setDone(); - } updateIndexDone.doWhenDone(new Runnable() { @Override public void run() { - //if (index != null && changes.get()) { - // updateNodeImageAndPosition(childNode, false, changes.get()); - //} if (!oldElement.equals(newElement.get()) || forceRemapping.get()) { removeMapping(oldElement, childNode, newElement.get()); if (newElement.get() != null) { @@ -3092,17 +3119,13 @@ public class AbstractTreeUi { disposeNode(childNode); adjustSelectionOnChildRemove(parentNode, selectedIndex, disposedElement); + result.setDone(); } else { elementToIndexMap.remove(getBuilder().getTreeStructureElement(childDesc.get())); - updateNodeChildren(childNode, pass, null, false, canSmartExpand, forceUpdate, true, true); + ActionCallback callback = updateNodeChildren(childNode, pass, null, false, canSmartExpand, forceUpdate, true, true); + callback.doWhenDone(result.createSetDoneRunnable()); } - - //if (parentNode.equals(getRootNode())) { - // myTreeModel.nodeChanged(getRootNode()); - //} - - result.setDone(); } }); } @@ -3218,7 +3241,7 @@ public class AbstractTreeUi { return new TreePath(((DefaultMutableTreeNode)node).getPath()); } else { - ArrayList nodes = new ArrayList(); + List nodes = new ArrayList(); TreeNode eachParent = node; while (eachParent != null) { nodes.add(eachParent); @@ -3338,10 +3361,8 @@ public class AbstractTreeUi { @NotNull private ActionCallback queueToBackground(@NotNull final Runnable bgBuildAction, @Nullable final Runnable edtPostRunnable) { if (!canInitiateNewActivity()) return new ActionCallback.Rejected(); - final ActionCallback result = new ActionCallback(); - - final Ref fail = new Ref(false); + final AtomicBoolean fail = new AtomicBoolean(); final Runnable finalizer = new Runnable() { @Override public void run() { @@ -3369,7 +3390,6 @@ public class AbstractTreeUi { builder.runBackgroundLoading(new Runnable() { @Override public void run() { - assertNotDispatchThread(); try { if (!canInitiateNewActivity()) { @@ -3379,7 +3399,6 @@ public class AbstractTreeUi { execute(bgBuildAction); if (edtPostRunnable != null) { - builder.updateAfterLoadedInBackground(new Runnable() { @Override public void run() { @@ -3459,6 +3478,7 @@ public class AbstractTreeUi { } } + return result; } @@ -3479,12 +3499,12 @@ public class AbstractTreeUi { finalizeRunnable.run(); } - invokeLaterIfNeeded(new Runnable() { + invokeLaterIfNeeded(false, new Runnable() { @Override public void run() { maybeReady(); } - }, false); + }); } public boolean isWorkerBusy() { @@ -3500,6 +3520,7 @@ public class AbstractTreeUi { } private void updateNodeImageAndPosition(@NotNull final DefaultMutableTreeNode node, boolean updatePosition, boolean nodeChanged) { + ApplicationManager.getApplication().assertIsDispatchThread(); if (!(node.getUserObject() instanceof NodeDescriptor)) return; NodeDescriptor descriptor = getDescriptorFrom(node); if (getElementFromDescriptor(descriptor) == null) return; @@ -3510,9 +3531,11 @@ public class AbstractTreeUi { int oldIndex = parentNode.getIndex(node); int newIndex = oldIndex; if (isLoadingChildrenFor(node.getParent()) || getBuilder().isChildrenResortingNeeded(descriptor)) { - final ArrayList children = new ArrayList(parentNode.getChildCount()); + final List children = new ArrayList(parentNode.getChildCount()); for (int i = 0; i < parentNode.getChildCount(); i++) { - children.add(parentNode.getChildAt(i)); + TreeNode child = parentNode.getChildAt(i); + LOG.assertTrue(child != null); + children.add(child); } sortChildren(node, children, true, false); newIndex = children.indexOf(node); @@ -3539,21 +3562,21 @@ public class AbstractTreeUi { } private void nodeChanged(final DefaultMutableTreeNode node) { - invokeLaterIfNeeded(new Runnable() { + invokeLaterIfNeeded(true, new Runnable() { @Override public void run() { myTreeModel.nodeChanged(node); } - }, true); + }); } public DefaultTreeModel getTreeModel() { return myTreeModel; } - private void insertNodesInto(@NotNull final ArrayList toInsert, @NotNull final DefaultMutableTreeNode parentNode) { + private void insertNodesInto(@NotNull final List toInsert, @NotNull final DefaultMutableTreeNode parentNode) { sortChildren(parentNode, toInsert, false, true); - final ArrayList all = new ArrayList(toInsert.size() + parentNode.getChildCount()); + final List all = new ArrayList(toInsert.size() + parentNode.getChildCount()); all.addAll(toInsert); all.addAll(TreeUtil.childrenToArray(parentNode)); @@ -3580,7 +3603,7 @@ public class AbstractTreeUi { myTreeModel.nodesWereInserted(parentNode, newNodeIndices); } else { - ArrayList before = new ArrayList(); + List before = new ArrayList(); before.addAll(all); sortChildren(parentNode, all, true, false); @@ -3616,13 +3639,13 @@ public class AbstractTreeUi { } } - private void sortChildren(@NotNull DefaultMutableTreeNode node, @NotNull ArrayList children, boolean updateStamp, boolean forceSort) { + private void sortChildren(@NotNull DefaultMutableTreeNode node, @NotNull List children, boolean updateStamp, boolean forceSort) { NodeDescriptor descriptor = getDescriptorFrom(node); assert descriptor != null; if (descriptor.getChildrenSortingStamp() >= getComparatorStamp() && !forceSort) return; if (!children.isEmpty()) { - getBuilder().sortChildren(myNodeComparator, node, children); + getBuilder().sortChildren(myNodeComparator, node, (ArrayList)children); } if (updateStamp) { @@ -4004,12 +4027,12 @@ public class AbstractTreeUi { revalidated.doWhenDone(new AsyncResult.Handler() { @Override public void run(final Object o) { - invokeLaterIfNeeded(new Runnable() { + invokeLaterIfNeeded(false, new Runnable() { @Override public void run() { _expand(o, onDone, parentsOnly, checkIfInStructure, canSmartExpand); } - }, false); + }); } }).doWhenRejected(new Runnable() { @Override @@ -4105,7 +4128,8 @@ public class AbstractTreeUi { break; } } - } else if (myElementToNodeMap.get(element) instanceof ArrayList) { + } + else if (myElementToNodeMap.get(element) instanceof List) { final TreePath[] paths = getTree().getSelectionPaths(); if (paths != null && paths.length > 0) { Set selectedNodes = new HashSet(); @@ -4116,7 +4140,7 @@ public class AbstractTreeUi { } - final ArrayList nodes = (ArrayList)myElementToNodeMap.get(element); + final List nodes = (List)myElementToNodeMap.get(element); for (Object each : nodes) { DefaultMutableTreeNode eachNode = (DefaultMutableTreeNode)each; while (eachNode != null) { @@ -4521,7 +4545,7 @@ public class AbstractTreeUi { return node instanceof DefaultMutableTreeNode && isLoadedInBackground(getElementFor((DefaultMutableTreeNode)node)); } - public void setTreeStructure(final AbstractTreeStructure treeStructure) { + public void setTreeStructure(@NotNull AbstractTreeStructure treeStructure) { myTreeStructure = treeStructure; clearUpdaterState(); } diff --git a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUpdater.java b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUpdater.java index 260659d0d293..de4fc42fca0d 100644 --- a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUpdater.java +++ b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUpdater.java @@ -204,9 +204,14 @@ public class AbstractTreeUpdater implements Disposable, Activatable { @Override public void run() { - if (myTreeBuilder.getTreeStructure().hasSomethingToCommit()) { - myTreeBuilder.getTreeStructure().commit(); - reQueueViewUpdateIfNeeded(); + AbstractTreeStructure structure = myTreeBuilder.getTreeStructure(); + if (structure.hasSomethingToCommit()) { + structure.asyncCommit().doWhenDone(new Runnable() { + @Override + public void run() { + reQueueViewUpdateIfNeeded(); + } + }); return; } try { @@ -222,7 +227,7 @@ public class AbstractTreeUpdater implements Disposable, Activatable { }); } - private void queue(Update update) { + private void queue(@NotNull Update update) { if (isReleased()) return; myUpdateQueue.queue(update); diff --git a/platform/platform-api/src/com/intellij/ui/treeStructure/filtered/FilteringTreeStructure.java b/platform/platform-api/src/com/intellij/ui/treeStructure/filtered/FilteringTreeStructure.java index ae253df6c16c..10f6d7bf757e 100644 --- a/platform/platform-api/src/com/intellij/ui/treeStructure/filtered/FilteringTreeStructure.java +++ b/platform/platform-api/src/com/intellij/ui/treeStructure/filtered/FilteringTreeStructure.java @@ -18,6 +18,7 @@ package com.intellij.ui.treeStructure.filtered; import com.intellij.ide.util.treeView.AbstractTreeStructure; import com.intellij.ide.util.treeView.NodeDescriptor; import com.intellij.ide.util.treeView.PresentableNodeDescriptor; +import com.intellij.openapi.util.ActionCallback; import com.intellij.ui.speedSearch.ElementFilter; import com.intellij.ui.treeStructure.SimpleNode; import org.jetbrains.annotations.NotNull; @@ -154,6 +155,12 @@ public class FilteringTreeStructure extends AbstractTreeStructure { return myBaseStructure.hasSomethingToCommit(); } + @NotNull + @Override + public ActionCallback asyncCommit() { + return myBaseStructure.asyncCommit(); + } + public class FilteringNode extends SimpleNode { private Object myDelegate; private State state = State.VISIBLE; diff --git a/platform/platform-tests/testSrc/com/intellij/ide/util/treeView/AbstractTreeBuilderCrashTest.java b/platform/platform-tests/testSrc/com/intellij/ide/util/treeView/AbstractTreeBuilderCrashTest.java index f67d2fcfa354..c9a2ffdb0ae0 100644 --- a/platform/platform-tests/testSrc/com/intellij/ide/util/treeView/AbstractTreeBuilderCrashTest.java +++ b/platform/platform-tests/testSrc/com/intellij/ide/util/treeView/AbstractTreeBuilderCrashTest.java @@ -153,15 +153,6 @@ public abstract class AbstractTreeBuilderCrashTest extends BaseTreeTestCase { public NodeDescriptor doCreateDescriptor(final Object element, final NodeDescriptor parentDescriptor) { return (NodeDescriptor)element; } - - @Override - public void commit() { - } - - @Override - public boolean hasSomethingToCommit() { - return false; - } }; diff --git a/plugins/ant/src/com/intellij/lang/ant/config/explorer/AntExplorerTreeStructure.java b/plugins/ant/src/com/intellij/lang/ant/config/explorer/AntExplorerTreeStructure.java index 5a0b0c90fba6..615914598860 100644 --- a/plugins/ant/src/com/intellij/lang/ant/config/explorer/AntExplorerTreeStructure.java +++ b/plugins/ant/src/com/intellij/lang/ant/config/explorer/AntExplorerTreeStructure.java @@ -22,6 +22,7 @@ import com.intellij.lang.ant.config.*; import com.intellij.lang.ant.config.impl.MetaTarget; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.ActionCallback; import com.intellij.psi.PsiDocumentManager; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; @@ -37,6 +38,7 @@ final class AntExplorerTreeStructure extends AbstractTreeStructure { private final Object myRoot = new Object(); private boolean myFilteredTargets = false; private static final Comparator ourTargetComparator = new Comparator() { + @Override public int compare(final AntBuildTarget target1, final AntBuildTarget target2) { final String name1 = target1.getDisplayName(); if (name1 == null) return Integer.MIN_VALUE; @@ -55,6 +57,7 @@ final class AntExplorerTreeStructure extends AbstractTreeStructure { return true; } + @Override @NotNull public AntNodeDescriptor createDescriptor(Object element, NodeDescriptor parentDescriptor) { if (element == myRoot) { @@ -77,6 +80,7 @@ final class AntExplorerTreeStructure extends AbstractTreeStructure { return null; } + @Override public Object[] getChildElements(Object element) { final AntConfiguration configuration = AntConfiguration.getInstance(myProject); if (element == myRoot) { @@ -84,7 +88,7 @@ final class AntExplorerTreeStructure extends AbstractTreeStructure { return new Object[] {AntBundle.message("loading.ant.config.progress")}; } final AntBuildFile[] buildFiles = configuration.getBuildFiles(); - return (buildFiles.length != 0) ? buildFiles : new Object[]{AntBundle.message("ant.tree.structure.no.build.files.message")}; + return buildFiles.length != 0 ? buildFiles : new Object[]{AntBundle.message("ant.tree.structure.no.build.files.message")}; } if (element instanceof AntBuildFile) { @@ -102,13 +106,10 @@ final class AntExplorerTreeStructure extends AbstractTreeStructure { return targets.toArray(new AntBuildTarget[targets.size()]); } - if (element instanceof AntBuildTarget) { - return ArrayUtil.EMPTY_OBJECT_ARRAY; - } - return ArrayUtil.EMPTY_OBJECT_ARRAY; } + @Override @Nullable public Object getParentElement(Object element) { if (element instanceof AntBuildTarget) { @@ -125,14 +126,23 @@ final class AntExplorerTreeStructure extends AbstractTreeStructure { return null; } + @Override public void commit() { PsiDocumentManager.getInstance(myProject).commitAllDocuments(); } + @Override public boolean hasSomethingToCommit() { return PsiDocumentManager.getInstance(myProject).hasUncommitedDocuments(); } + @NotNull + @Override + public ActionCallback asyncCommit() { + return asyncCommitDocuments(myProject); + } + + @Override public Object getRootElement() { return myRoot; } @@ -146,14 +156,17 @@ final class AntExplorerTreeStructure extends AbstractTreeStructure { super(project, parentDescriptor); } + @Override public boolean isAutoExpand() { return true; } + @Override public Object getElement() { return myRoot; } + @Override public boolean update() { myName = ""; return false; @@ -167,14 +180,17 @@ final class AntExplorerTreeStructure extends AbstractTreeStructure { myColor = Color.blue; } + @Override public Object getElement() { return myName; } + @Override public boolean update() { return true; } + @Override public boolean isAutoExpand() { return true; }