Background loading query structure fixed

This commit is contained in:
Kirill Kalishev
2009-10-01 15:07:42 +04:00
parent 4e92ee8929
commit ee8375bca1
4 changed files with 187 additions and 83 deletions
@@ -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;
}
}
@@ -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<Boolean, Object[]> unbuilt = processUnbuilt(node, descriptor, pass, wasExpanded, null);
if (!descriptorWasUpdated) {
update(descriptor, false);
descriptorWasUpdated = true;
}
Pair<Boolean, LoadedChildren> 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<Object, Integer> 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<TreeNode> nodesToInsert = collectNodesToInsert(descriptor, elementToIndexMap, node, expanded);
ArrayList<TreeNode> 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<Boolean, Object[]> processUnbuilt(final DefaultMutableTreeNode node,
private Pair<Boolean, LoadedChildren> 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<Boolean, Object[]>(true, null);
return new Pair<Boolean, LoadedChildren>(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<Boolean, Object[]>(processed, children);
return new Pair<Boolean, LoadedChildren>(processed, children);
}
private boolean removeIfLoading(TreeNode node) {
@@ -980,7 +987,7 @@ class AbstractTreeUi {
private void updateNodesToInsert(final ArrayList<TreeNode> 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<Object, Integer> loadElementsFromStructure(final NodeDescriptor descriptor, @Nullable Object[] preloadedChildren) {
private MutualMap<Object, Integer> loadElementsFromStructure(final NodeDescriptor descriptor, @Nullable LoadedChildren preloadedChildren) {
MutualMap<Object, Integer> elementToIndexMap = new MutualMap<Object, Integer>(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<TreeNode> collectNodesToInsert(final NodeDescriptor descriptor, final MutualMap<Object, Integer> elementToIndexMap, DefaultMutableTreeNode parent, boolean addLoadingNode) {
private ArrayList<TreeNode> collectNodesToInsert(final NodeDescriptor descriptor, final MutualMap<Object, Integer> elementToIndexMap, DefaultMutableTreeNode parent, boolean addLoadingNode, @NotNull LoadedChildren loadedChildren) {
ArrayList<TreeNode> nodesToInsert = new ArrayList<TreeNode>();
final Collection<Object> 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<Object> myElements = new HashSet<Object>();
@@ -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<Object[]> children = new Ref<Object[]>();
final Ref<LoadedChildren> children = new Ref<LoadedChildren>();
final Ref<Object> elementFromDescriptor = new Ref<Object>();
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<Boolean, Object[]> unbuilt = processUnbuilt(node, descriptor, pass, isExpanded(node, wasExpanded), children.get());
if (unbuilt.getFirst()) return;
Pair<Boolean, LoadedChildren> 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<Object, NodeDescriptor> myDescriptors = new HashMap<Object, NodeDescriptor>();
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;
}
}
}
@@ -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<TreeUpdatePass> 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<TreeUpdatePass> 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<TreeUpdatePass> yielding = ui.getYeildingPasses();
for (Iterator<TreeUpdatePass> iterator = yielding.iterator(); iterator.hasNext();) {
TreeUpdatePass eachYielding = iterator.next();
if (!toAdd.isExpired()) {
final Collection<TreeUpdatePass> yielding = ui.getYeildingPasses();
for (Iterator<TreeUpdatePass> 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();
}
@@ -94,4 +94,9 @@ public class MutualMap<Key, Value> {
public int hashCode() {
return 31 * myKey2Value.hashCode() + myValue2Key.hashCode();
}
@Override
public String toString() {
return myKey2Value.toString();
}
}