/*
* Copyright 2003-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.ide.projectPane.logicalview.highlighting.visitor.updates;
import com.intellij.util.ui.Timer;
import jetbrains.mps.ide.ui.tree.MPSTreeNode;
import jetbrains.mps.project.Project;
import jetbrains.mps.util.Pair;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;
public final class TreeNodeUpdater {
private final Timer myTimer;
private final Project myProject;
private final Semaphore myGuard = new Semaphore(1);
private Queue<Pair<MPSTreeNode, NodeUpdate>> myUpdates = new ConcurrentLinkedQueue<Pair<MPSTreeNode, NodeUpdate>>();
public TreeNodeUpdater(Project mpsProject) {
myProject = mpsProject;
myTimer = new Timer("ProjectPane Tree Update Thread", 500) {
@Override
protected void onTimer() throws InterruptedException {
process();
}
};
myTimer.setTakeInitialDelay(true);
}
final void process() {
if (!myGuard.tryAcquire()) {
return;
}
try {
do {
int batchProcessMax = 20; // do not process more than X at once, not to block any write actions nor UI thread for too long
final ArrayList<Pair<MPSTreeNode, NodeUpdate>> updates = new ArrayList<Pair<MPSTreeNode, NodeUpdate>>(batchProcessMax);
Pair<MPSTreeNode, NodeUpdate> u;
while ((u = myUpdates.poll()) != null && batchProcessMax > 0) {
if (u.o1.getTree() == null) {
// no reason to update element which is not in the tree
continue;
}
updates.add(u);
batchProcessMax--;
}
if (updates.isEmpty()) {
break;
}
myProject.getModelAccess().runReadInEDT(new Runnable() {
@Override
public void run() {
final HashSet<MPSTreeNode> toRefresh = new HashSet<MPSTreeNode>();
for (Pair<MPSTreeNode, NodeUpdate> next : updates) {
MPSTreeNode node = next.o1;
if (node.getTree() == null) {
// once again, no reason to update element which is not in the tree
continue;
}
next.o2.update(node);
toRefresh.add(node);
}
for (MPSTreeNode node : toRefresh) {
node.updateNodePresentationInTree();
}
}
});
} while (!myUpdates.isEmpty());
myTimer.suspend();
} finally {
myGuard.release();
}
}
public void addUpdate(MPSTreeNode node, NodeUpdate r) {
if (!r.needed(node)) return;
myUpdates.add(new Pair<MPSTreeNode, NodeUpdate>(node, r));
myTimer.start(); // sic(!), resume() or restart() force timer into 'running' state, effectively skipping initial delay
}
}