From ee8375bca12e476601be23452530d376484eff2a Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Thu, 1 Oct 2009 15:06:04 +0400 Subject: [PATCH] Background loading query structure fixed --- .../util/treeView/AbstractTreeStructure.java | 5 +- .../ide/util/treeView/AbstractTreeUi.java | 172 +++++++++++++----- .../util/treeView/AbstractTreeUpdater.java | 88 +++++---- .../com/intellij/openapi/util/MutualMap.java | 5 + 4 files changed, 187 insertions(+), 83 deletions(-) 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 861a7aca1366..967cdc83b2ed 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,6 @@ package com.intellij.ide.util.treeView; -import com.intellij.ui.treeStructure.Tree; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,4 +35,8 @@ public abstract class AbstractTreeStructure { return false; } + public boolean isAlwaysLeaf() { + return false; + } + } \ 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 722c2b53925e..34bae64497c3 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 @@ -271,12 +271,12 @@ class AbstractTreeUi { myCanProcessDeferredSelections = true; ourUi2Countdown.remove(this); - getUpdater().showNotify(); - if (!myWasEverShown || myUpdateFromRootRequested || myUpdateIfInactive) { getBuilder().updateFromRoot(); } + getUpdater().showNotify(); + myWasEverShown |= byShowing; } @@ -501,7 +501,7 @@ class AbstractTreeUi { } protected final void initRootNode() { - if (myUpdateIfInactive || ApplicationManager.getApplication().isUnitTestMode()) { + if (myUpdateIfInactive) { activate(false); } else { myUpdateFromRootRequested = true; @@ -537,7 +537,7 @@ class AbstractTreeUi { expand(getRootNode(), true); } if (!willUpdate) { - updateNodeChildren(getRootNode(), pass, null, false, isAutoExpand(rootDescriptor), false); + updateNodeChildren(getRootNode(), pass, null, false, isAutoExpand(rootDescriptor), false, true); } if (getRootNode().getChildCount() == 0) { myTreeModel.nodeChanged(getRootNode()); @@ -565,7 +565,7 @@ class AbstractTreeUi { assertIsDispatchThread(); } - if (isEdt() || !myWasEverShown) { + if (isEdt() || !myWasEverShown || (!isEdt() && canBeNonEdt)) { return getBuilder().updateNodeDescriptor(nodeDescriptor); } else { @@ -652,12 +652,6 @@ class AbstractTreeUi { setUpdaterState(new UpdaterTreeState(this)).beforeSubtreeUpdate(); - final NodeDescriptor descriptor = getDescriptorFrom(node); - - if (!isToBuildInBackground(descriptor)) { - getBuilder().updateNode(node); - } - boolean forceUpdate = true; TreePath path = getPathFor(node); boolean invisible = !myTree.isExpanded(path) && (path.getParentPath() == null || !myTree.isExpanded(path.getParentPath())); @@ -666,7 +660,7 @@ class AbstractTreeUi { forceUpdate = false; } - updateNodeChildren(node, pass, null, false, canSmartExpand, forceUpdate); + updateNodeChildren(node, pass, null, false, canSmartExpand, forceUpdate, false); } private boolean isToBuildInBackground(NodeDescriptor descriptor) { @@ -709,10 +703,11 @@ class AbstractTreeUi { private void updateNodeChildren(final DefaultMutableTreeNode node, final TreeUpdatePass pass, - @Nullable Object[] preloadedChildren, + @Nullable LoadedChildren loadedChildren, boolean forcedNow, final boolean toSmartExpand, - boolean forceUpdate) { + boolean forceUpdate, + final boolean descriptorIsUpToDate) { getTreeStructure().commit(); final boolean wasExpanded = myTree.isExpanded(new TreePath(node.getPath())) || isAutoExpand(node); final boolean wasLeaf = node.getChildCount() == 0; @@ -726,7 +721,8 @@ class AbstractTreeUi { boolean bgBuild = isToBuildInBackground(descriptor); boolean notRequiredToUpdateChildren = !forcedNow && !wasExpanded && !forceUpdate; - Object[] preloaded = preloadedChildren; + LoadedChildren preloaded = loadedChildren; + boolean descriptorWasUpdated = descriptorIsUpToDate; if (notRequiredToUpdateChildren) { if (myUnbuiltNodes.contains(node) && node.getChildCount() == 0) { @@ -738,7 +734,11 @@ class AbstractTreeUi { if (!forcedNow) { if (!bgBuild) { if (myUnbuiltNodes.contains(node)) { - Pair unbuilt = processUnbuilt(node, descriptor, pass, wasExpanded, null); + if (!descriptorWasUpdated) { + update(descriptor, false); + descriptorWasUpdated = true; + } + Pair unbuilt = processUnbuilt(node, descriptor, pass, wasExpanded, null); if (unbuilt.getFirst()) return; preloaded = unbuilt.getSecond(); } @@ -749,9 +749,13 @@ class AbstractTreeUi { boolean childForceUpdate = isChildNodeForceUpdate(node, forceUpdate, wasExpanded); if (!forcedNow && isToBuildInBackground(descriptor)) { - queueBackgroundUpdate(node, descriptor, pass, canSmartExpand(node, toSmartExpand), wasExpanded, childForceUpdate); + queueBackgroundUpdate(node, descriptor, pass, canSmartExpand(node, toSmartExpand), wasExpanded, childForceUpdate, descriptorWasUpdated); return; } else { + if (!descriptorWasUpdated) { + update(descriptor, false); + } + updateNodeChildrenNow(node, pass, preloaded, toSmartExpand, wasExpanded, wasLeaf, childForceUpdate); } } @@ -766,7 +770,7 @@ class AbstractTreeUi { } private void updateNodeChildrenNow(final DefaultMutableTreeNode node, final TreeUpdatePass pass, - Object[] preloadedChildren, + final LoadedChildren preloadedChildren, final boolean toSmartExpand, final boolean wasExpanded, final boolean wasLeaf, @@ -774,8 +778,10 @@ class AbstractTreeUi { final NodeDescriptor descriptor = getDescriptorFrom(node); final MutualMap elementToIndexMap = loadElementsFromStructure(descriptor, preloadedChildren); + final LoadedChildren loadedChildren = preloadedChildren != null ? preloadedChildren : new LoadedChildren(elementToIndexMap.getKeys().toArray()); - myUpdatingChildren.add(node); + + addToUpdating(node); pass.setCurrentNode(node); final boolean canSmartExpand = canSmartExpand(node, toSmartExpand); @@ -790,7 +796,7 @@ class AbstractTreeUi { final boolean expanded = isExpanded(node, wasExpanded); - ArrayList nodesToInsert = collectNodesToInsert(descriptor, elementToIndexMap, node, expanded); + ArrayList nodesToInsert = collectNodesToInsert(descriptor, elementToIndexMap, node, expanded, loadedChildren); insertNodesInto(nodesToInsert, node); updateNodesToInsert(nodesToInsert, pass, canSmartExpand, isChildNodeForceUpdate(node, forceUpdate, expanded)); removeLoading(node, true); @@ -805,7 +811,7 @@ class AbstractTreeUi { } } - myUpdatingChildren.remove(node); + removeFromUpdating(node); final Object element = getElementFor(node); addNodeAction(element, new NodeAction() { @@ -872,21 +878,22 @@ class AbstractTreeUi { myUnbuiltNodes.remove(node); } - private Pair processUnbuilt(final DefaultMutableTreeNode node, + private Pair processUnbuilt(final DefaultMutableTreeNode node, final NodeDescriptor descriptor, final TreeUpdatePass pass, boolean isExpanded, - final Object[] preloadedChildren) { + final LoadedChildren loadedChildren) { if (!isExpanded && getBuilder().isAlwaysShowPlus(descriptor)) { - return new Pair(true, null); + return new Pair(true, null); } final Object element = getElementFor(node); - final Object[] children = preloadedChildren != null ? preloadedChildren : getChildrenFor(element); + + final LoadedChildren children = loadedChildren != null ? loadedChildren : new LoadedChildren(getChildrenFor(element)); boolean processed; - if (children.length == 0) { + if (children.getElements().length == 0) { removeLoading(node, true); processed = true; } @@ -895,7 +902,7 @@ class AbstractTreeUi { addNodeAction(getElementFor(node), new NodeAction() { public void onReady(final DefaultMutableTreeNode node) { final TreePath path = new TreePath(node.getPath()); - if (getTree().isExpanded(path) || children.length == 0) { + if (getTree().isExpanded(path) || children.getElements().length == 0) { removeLoading(node, false); } else { @@ -914,7 +921,7 @@ class AbstractTreeUi { processNodeActionsIfReady(node); - return new Pair(processed, children); + return new Pair(processed, children); } private boolean removeIfLoading(TreeNode node) { @@ -980,7 +987,7 @@ class AbstractTreeUi { private void updateNodesToInsert(final ArrayList nodesToInsert, TreeUpdatePass pass, boolean canSmartExpand, boolean forceUpdate) { for (TreeNode aNodesToInsert : nodesToInsert) { DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)aNodesToInsert; - updateNodeChildren(childNode, pass, null, false, canSmartExpand, forceUpdate); + updateNodeChildren(childNode, pass, null, false, canSmartExpand, forceUpdate, true); } } @@ -1178,9 +1185,9 @@ class AbstractTreeUi { return getBuilder().isToYieldUpdateFor(node); } - private MutualMap loadElementsFromStructure(final NodeDescriptor descriptor, @Nullable Object[] preloadedChildren) { + private MutualMap loadElementsFromStructure(final NodeDescriptor descriptor, @Nullable LoadedChildren preloadedChildren) { MutualMap elementToIndexMap = new MutualMap(true); - Object[] children = preloadedChildren != null ? preloadedChildren : getChildrenFor(getBuilder().getTreeStructureElement(descriptor)); + Object[] children = preloadedChildren != null ? preloadedChildren.getElements() : getChildrenFor(getBuilder().getTreeStructureElement(descriptor)); int index = 0; for (Object child : children) { if (!isValid(child)) continue; @@ -1230,19 +1237,31 @@ class AbstractTreeUi { return node instanceof LoadingNode; } - private ArrayList collectNodesToInsert(final NodeDescriptor descriptor, final MutualMap elementToIndexMap, DefaultMutableTreeNode parent, boolean addLoadingNode) { + private ArrayList collectNodesToInsert(final NodeDescriptor descriptor, final MutualMap elementToIndexMap, DefaultMutableTreeNode parent, boolean addLoadingNode, @NotNull LoadedChildren loadedChildren) { ArrayList nodesToInsert = new ArrayList(); final Collection allElements = elementToIndexMap.getKeys(); + for (Object child : allElements) { Integer index = elementToIndexMap.getValue(child); - final NodeDescriptor childDescr = getTreeStructure().createDescriptor(child, descriptor); + NodeDescriptor childDescr = loadedChildren.getDescriptor(child); + boolean needToUpdate = false; + if (childDescr == null) { + childDescr = getTreeStructure().createDescriptor(child, descriptor); + loadedChildren.putDescriptor(child, childDescr); + needToUpdate = true; + } + //noinspection ConstantConditions if (childDescr == null) { LOG.error("childDescr == null, treeStructure = " + getTreeStructure() + ", child = " + child); continue; } childDescr.setIndex(index.intValue()); - update(childDescr, false); + + if (needToUpdate) { + update(childDescr, false); + } + Object element = getElementFromDescriptor(childDescr); if (element == null) { LOG.error("childDescr.getElement() == null, child = " + child + ", builder = " + this); @@ -1281,6 +1300,24 @@ class AbstractTreeUi { return myInitialized; } + private void addToUpdating(DefaultMutableTreeNode node) { + synchronized (myUpdatingChildren) { + myUpdatingChildren.add(node); + } + } + + private void removeFromUpdating(DefaultMutableTreeNode node) { + synchronized (myUpdatingChildren) { + myUpdatingChildren.remove(node); + } + } + + public boolean isUpdatingNow(DefaultMutableTreeNode node) { + synchronized (myUpdatingChildren) { + return myUpdatingChildren.contains(node); + } + } + static class ElementNode extends DefaultMutableTreeNode { Set myElements = new HashSet(); @@ -1324,7 +1361,7 @@ class AbstractTreeUi { private boolean isUpdatingParent(DefaultMutableTreeNode kid) { DefaultMutableTreeNode eachParent = kid; while (eachParent != null) { - if (myUpdatingChildren.contains(eachParent)) return true; + if (isUpdatingNow(eachParent)) return true; eachParent = (DefaultMutableTreeNode)eachParent.getParent(); } @@ -1360,7 +1397,8 @@ class AbstractTreeUi { final TreeUpdatePass pass, final boolean canSmartExpand, final boolean wasExpanded, - final boolean forceUpdate) { + final boolean forceUpdate, + final boolean descriptorIsUpToDate) { assertIsDispatchThread(); final Object oldElementFromDescriptor = getElementFromDescriptor(descriptor); @@ -1374,7 +1412,7 @@ class AbstractTreeUi { myTreeModel.insertNodeInto(loadingNode, node, node.getChildCount()); } - final Ref children = new Ref(); + final Ref children = new Ref(); final Ref elementFromDescriptor = new Ref(); Runnable buildRunnable = new Runnable() { public void run() { @@ -1382,7 +1420,10 @@ class AbstractTreeUi { return; } - update(descriptor, true); + if (!descriptorIsUpToDate) { + update(descriptor, true); + } + Object element = getElementFromDescriptor(descriptor); if (element == null) { removeFromLoadedInBackground(oldElementFromDescriptor); @@ -1390,7 +1431,16 @@ class AbstractTreeUi { } elementFromDescriptor.set(element); - children.set(getChildrenFor(getBuilder().getTreeStructureElement(descriptor))); // load children + + Object[] loadedElements = getChildrenFor(getBuilder().getTreeStructureElement(descriptor)); + LoadedChildren loaded = new LoadedChildren(loadedElements); + for (Object each : loadedElements) { + NodeDescriptor eachChildDescriptor = getTreeStructure().createDescriptor(each, descriptor); + eachChildDescriptor.update(); + loaded.putDescriptor(each, eachChildDescriptor); + } + + children.set(loaded); } }; @@ -1409,11 +1459,14 @@ class AbstractTreeUi { removeFromLoadedInBackground(elementFromDescriptor.get()); if (myUnbuiltNodes.contains(node)) { - Pair unbuilt = processUnbuilt(node, descriptor, pass, isExpanded(node, wasExpanded), children.get()); - if (unbuilt.getFirst()) return; + Pair unbuilt = processUnbuilt(node, descriptor, pass, isExpanded(node, wasExpanded), children.get()); + if (unbuilt.getFirst()) { + nodeToProcessActions[0] = node; + return; + } } - updateNodeChildren(node, pass, children.get(), true, canSmartExpand, forceUpdate); + updateNodeChildren(node, pass, children.get(), true, canSmartExpand, forceUpdate, true); if (isRerunNeeded(pass)) { @@ -1425,7 +1478,6 @@ class AbstractTreeUi { if (element != null) { removeLoading(node, true); - nodeToProcessActions[0] = node; } } @@ -1693,7 +1745,7 @@ class AbstractTreeUi { } else { elementToIndexMap.remove(getBuilder().getTreeStructureElement(childDesc)); - updateNodeChildren(childNode, pass, null, false, canSmartExpand, forceUpdate); + updateNodeChildren(childNode, pass, null, false, canSmartExpand, forceUpdate, true); } if (parentNode.equals(getRootNode())) { @@ -2110,7 +2162,7 @@ class AbstractTreeUi { } private void disposeNode(DefaultMutableTreeNode node) { - myUpdatingChildren.remove(node); + removeFromUpdating(node); removeFromUnbuilt(node); if (node.getChildCount() > 0) { @@ -2868,6 +2920,7 @@ class AbstractTreeUi { final DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent(); if (!(node.getUserObject() instanceof NodeDescriptor)) return; + TreePath pathToSelect = null; if (isSelectionInside(node)) { pathToSelect = new TreePath(node.getPath()); @@ -2889,7 +2942,8 @@ class AbstractTreeUi { }); if (node.equals(getRootNode())) { if (myTree.isRootVisible()) { - addSelectionPath(new TreePath(getRootNode().getPath()), true, Condition.FALSE); + //todo kirillk to investigate -- should be done by standard selction move + //addSelectionPath(new TreePath(getRootNode().getPath()), true, Condition.FALSE); } } else { @@ -2949,4 +3003,30 @@ class AbstractTreeUi { final Object node = myElementToNodeMap.get(element); return !myUnbuiltNodes.contains(node); } + + static class LoadedChildren { + private Object[] myElements; + private Map myDescriptors = new HashMap(); + + LoadedChildren(Object[] elements) { + myElements = elements != null ? elements : new Object[0]; + } + + void putDescriptor(Object element, NodeDescriptor descriptor) { + myDescriptors.put(element, descriptor); + } + + Object[] getElements() { + return myElements; + } + + NodeDescriptor getDescriptor(Object element) { + return myDescriptors.get(element); + } + + @Override + public String toString() { + return Arrays.asList(myElements) + "->" + myDescriptors; + } + } } 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 2804909fe690..cd60a412aef1 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 @@ -50,7 +50,7 @@ public class AbstractTreeUpdater implements Disposable, Activatable { public AbstractTreeUpdater(AbstractTreeBuilder treeBuilder) { myTreeBuilder = treeBuilder; final JTree tree = myTreeBuilder.getTree(); - final JComponent component = tree instanceof TreeTableTree? ((TreeTableTree)tree).getTreeTable() : tree; + final JComponent component = tree instanceof TreeTableTree ? ((TreeTableTree)tree).getTreeTable() : tree; myUpdateQueue = new MergingUpdateQueue("UpdateQueue", 300, component.isShowing(), component) { @Override protected Alarm createAlarm(Alarm.ThreadToUse thread, Disposable parent) { @@ -79,7 +79,7 @@ public class AbstractTreeUpdater implements Disposable, Activatable { } public void setModalityStateComponent(JComponent c) { - myUpdateQueue.setModalityStateComponent(c); + myUpdateQueue.setModalityStateComponent(c); } public boolean hasNodesToUpdate() { @@ -99,43 +99,57 @@ public class AbstractTreeUpdater implements Disposable, Activatable { } - assert !toAdd.isExpired(); - - for (Iterator iterator = myNodeQueue.iterator(); iterator.hasNext();) { - final TreeUpdatePass passInQueue = iterator.next(); + assert !toAdd.isExpired(); - if (passInQueue == toAdd) { - return; - } else if (passInQueue.getNode() == toAdd.getNode()) { - toAdd.expire(); - return; - } else if (toAdd.getNode().isNodeAncestor(passInQueue.getNode())) { - toAdd.expire(); - return; - } else if (passInQueue.getNode().isNodeAncestor(toAdd.getNode())) { - iterator.remove(); - passInQueue.expire(); + final AbstractTreeUi ui = myTreeBuilder.getUi(); + + if (ui.isUpdatingNow(toAdd.getNode())) { + toAdd.expire(); + } + else { + for (Iterator iterator = myNodeQueue.iterator(); iterator.hasNext();) { + final TreeUpdatePass passInQueue = iterator.next(); + + + if (passInQueue == toAdd) { + return; + } + else if (passInQueue.getNode() == toAdd.getNode()) { + toAdd.expire(); + return; + } + else if (toAdd.getNode().isNodeAncestor(passInQueue.getNode())) { + toAdd.expire(); + return; + } + else if (passInQueue.getNode().isNodeAncestor(toAdd.getNode())) { + iterator.remove(); + passInQueue.expire(); + } } } long newUpdateCount = toAdd.getUpdateStamp() == -1 ? myUpdateCount : myUpdateCount + 1; - final AbstractTreeUi ui = myTreeBuilder.getUi(); - final Collection yielding = ui.getYeildingPasses(); - for (Iterator iterator = yielding.iterator(); iterator.hasNext();) { - TreeUpdatePass eachYielding = iterator.next(); + if (!toAdd.isExpired()) { + final Collection yielding = ui.getYeildingPasses(); + for (Iterator iterator = yielding.iterator(); iterator.hasNext();) { + TreeUpdatePass eachYielding = iterator.next(); - final DefaultMutableTreeNode eachNode = eachYielding.getCurrentNode(); - if (eachNode != null) { - if (eachNode.isNodeAncestor(toAdd.getNode())) { - toAdd.expire(); - } else { - eachYielding.setUpdateStamp(newUpdateCount); + final DefaultMutableTreeNode eachNode = eachYielding.getCurrentNode(); + if (eachNode != null) { + if (eachNode.isNodeAncestor(toAdd.getNode())) { + toAdd.expire(); + } + else { + eachYielding.setUpdateStamp(newUpdateCount); + } } } } + if (toAdd.isExpired()) return; @@ -158,9 +172,10 @@ public class AbstractTreeUpdater implements Disposable, Activatable { try { performUpdate(); } - catch(ProcessCanceledException e) { + catch (ProcessCanceledException e) { throw e; - } catch(RuntimeException e) { + } + catch (RuntimeException e) { LOG.error(myTreeBuilder.getClass().getName(), e); } } @@ -172,21 +187,21 @@ public class AbstractTreeUpdater implements Disposable, Activatable { } /** - * @deprecated use addSubtreeToUpdate instead * @param node + * @deprecated use addSubtreeToUpdate instead */ protected void updateSubtree(DefaultMutableTreeNode node) { myTreeBuilder.updateSubtree(node); } public synchronized void performUpdate() { - if (myRunBeforeUpdate != null){ + if (myRunBeforeUpdate != null) { myRunBeforeUpdate.run(); myRunBeforeUpdate = null; } - while(!myNodeQueue.isEmpty()){ + while (!myNodeQueue.isEmpty()) { if (isInPostponeMode()) break; @@ -228,7 +243,8 @@ public class AbstractTreeUpdater implements Disposable, Activatable { final Application app = ApplicationManager.getApplication(); if (app != null) { app.invokeLater(runnable); - } else { + } + else { UIUtil.invokeAndWaitIfNeeded(runnable); } } @@ -243,16 +259,16 @@ public class AbstractTreeUpdater implements Disposable, Activatable { } DefaultMutableTreeNode node = myTreeBuilder.getNodeForElement(element); - if (node != null){ + if (node != null) { addSubtreeToUpdate(node); return true; } - else{ + else { return false; } } - public void cancelAllRequests(){ + public void cancelAllRequests() { myNodeQueue.clear(); myUpdateQueue.cancelAllUpdates(); } diff --git a/platform/util/src/com/intellij/openapi/util/MutualMap.java b/platform/util/src/com/intellij/openapi/util/MutualMap.java index 7dbd455abb7a..a7c0bed4542f 100644 --- a/platform/util/src/com/intellij/openapi/util/MutualMap.java +++ b/platform/util/src/com/intellij/openapi/util/MutualMap.java @@ -94,4 +94,9 @@ public class MutualMap { public int hashCode() { return 31 * myKey2Value.hashCode() + myValue2Key.hashCode(); } + + @Override + public String toString() { + return myKey2Value.toString(); + } }