mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
support process cancelling and command restarting
This commit is contained in:
@@ -17,6 +17,7 @@ package com.intellij.ui.tree;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.ui.LoadingNode;
|
||||
import com.intellij.util.concurrency.Command;
|
||||
@@ -41,7 +42,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
@@ -364,7 +364,7 @@ public final class AsyncTreeModel extends AbstractTreeModel implements Disposabl
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
private static abstract class ObsolescentCommand implements Obsolescent, Command<Node>, Function<Object, Node> {
|
||||
private abstract class ObsolescentCommand implements Obsolescent, Command<Reference<Node>> {
|
||||
final AsyncPromise<Node> promise = new AsyncPromise<>();
|
||||
final String name;
|
||||
final Object object;
|
||||
@@ -376,6 +376,10 @@ public final class AsyncTreeModel extends AbstractTreeModel implements Disposabl
|
||||
LOG.debug("create command: ", this);
|
||||
}
|
||||
|
||||
abstract Node getNode(Object object);
|
||||
|
||||
abstract void setNode(Node node);
|
||||
|
||||
boolean isPending() {
|
||||
return Promise.State.PENDING == promise.getState();
|
||||
}
|
||||
@@ -386,10 +390,28 @@ public final class AsyncTreeModel extends AbstractTreeModel implements Disposabl
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node get() {
|
||||
public Reference<Node> get() {
|
||||
started = true;
|
||||
LOG.debug("background command: ", this);
|
||||
return isObsolete() ? null : apply(object);
|
||||
return Reference.create(() -> getNode(object));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Reference<Node> reference) {
|
||||
if (isObsolete()) {
|
||||
LOG.debug("obsolete command: ", this);
|
||||
}
|
||||
else if (reference == null) {
|
||||
LOG.debug("failed command: ", this);
|
||||
}
|
||||
else if (reference.isValid()) {
|
||||
LOG.debug("foreground command: ", this);
|
||||
setNode(reference.get());
|
||||
}
|
||||
else {
|
||||
LOG.debug("restart command: ", this);
|
||||
processor.process(this); // restart command
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,20 +427,14 @@ public final class AsyncTreeModel extends AbstractTreeModel implements Disposabl
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node apply(Object object) {
|
||||
Node getNode(Object object) {
|
||||
if (object == null) object = model.getRoot();
|
||||
if (object == null || isObsolete()) return null;
|
||||
return new Node(object, model.isLeaf(object));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Node loaded) {
|
||||
if (isObsolete()) {
|
||||
LOG.debug("obsolete command: ", this);
|
||||
return;
|
||||
}
|
||||
LOG.debug("foreground command: ", this);
|
||||
|
||||
void setNode(Node loaded) {
|
||||
Node root = tree.root;
|
||||
if (root == null && loaded == null) {
|
||||
LOG.debug("no root");
|
||||
@@ -478,13 +494,11 @@ public final class AsyncTreeModel extends AbstractTreeModel implements Disposabl
|
||||
|
||||
@Override
|
||||
public boolean isObsolete() {
|
||||
if (this == node.queue.get()) return false;
|
||||
LOG.debug("obsolete command: ", this);
|
||||
return true;
|
||||
return this != node.queue.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node apply(Object object) {
|
||||
Node getNode(Object object) {
|
||||
Node loaded = new Node(object, model.isLeaf(object));
|
||||
if (loaded.leaf) return loaded;
|
||||
|
||||
@@ -492,7 +506,7 @@ public final class AsyncTreeModel extends AbstractTreeModel implements Disposabl
|
||||
//noinspection unchecked
|
||||
ChildrenProvider<Object> provider = (ChildrenProvider)model;
|
||||
List<Object> children = provider.getChildren(object);
|
||||
if (children == null) return null; // cancel this command
|
||||
if (children == null) throw new ProcessCanceledException(); // cancel this command
|
||||
loaded.children = load(children.size(), index -> children.get(index));
|
||||
}
|
||||
else {
|
||||
@@ -525,11 +539,7 @@ public final class AsyncTreeModel extends AbstractTreeModel implements Disposabl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Node loaded) {
|
||||
if (isObsolete()) {
|
||||
LOG.debug("obsolete command: ", this);
|
||||
return;
|
||||
}
|
||||
void setNode(Node loaded) {
|
||||
if (loaded == null || loaded.isLoadingRequired()) {
|
||||
LOG.debug("cancelled command: ", this);
|
||||
return;
|
||||
@@ -539,8 +549,6 @@ public final class AsyncTreeModel extends AbstractTreeModel implements Disposabl
|
||||
LOG.warn("ignore removed node: " + node.object);
|
||||
return;
|
||||
}
|
||||
LOG.debug("foreground command: ", this);
|
||||
|
||||
List<Node> oldChildren = node.getChildren();
|
||||
List<Node> newChildren = loaded.getChildren();
|
||||
if (oldChildren.isEmpty() && newChildren.isEmpty()) {
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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 com.intellij.ui.tree;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static java.awt.EventQueue.isDispatchThread;
|
||||
|
||||
/**
|
||||
* @author Sergey.Malenkov
|
||||
*/
|
||||
final class Reference<T> {
|
||||
private static final Logger LOG = Logger.getInstance(Reference.class);
|
||||
private volatile boolean valid;
|
||||
private volatile T value;
|
||||
|
||||
boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
void invalidate() {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
T set(T value) {
|
||||
T old = this.value;
|
||||
this.value = value;
|
||||
valid = true;
|
||||
return old;
|
||||
}
|
||||
|
||||
T get() {
|
||||
return value;
|
||||
}
|
||||
|
||||
static <T> Reference<T> create(@NotNull Supplier<T> supplier) {
|
||||
Reference<T> reference = new Reference<>();
|
||||
try {
|
||||
Runnable process = () -> reference.set(supplier.get());
|
||||
ProgressManager manager = getProgressManager();
|
||||
if (manager == null || isDispatchThread()) {
|
||||
process.run();
|
||||
}
|
||||
else {
|
||||
manager.runInReadActionWithWriteActionPriority(process, null);
|
||||
}
|
||||
}
|
||||
catch (ProcessCanceledException ignore) {
|
||||
}
|
||||
catch (Exception exception) {
|
||||
LOG.warn(exception);
|
||||
return null;
|
||||
}
|
||||
return reference;
|
||||
}
|
||||
|
||||
private static ProgressManager getProgressManager() {
|
||||
try {
|
||||
return ProgressManager.getInstance();
|
||||
}
|
||||
catch (NullPointerException exception) {
|
||||
LOG.debug("progress manager is not available");
|
||||
return null; // in tests without application
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.intellij.ui.tree;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.util.concurrency.Invoker;
|
||||
import com.intellij.util.concurrency.InvokerSupplier;
|
||||
@@ -417,6 +418,11 @@ public final class AsyncTreeModelTest {
|
||||
}
|
||||
|
||||
private void pause() {
|
||||
if (this instanceof InvokerSupplier && .9 < Math.random()) {
|
||||
// sometimes throw an exception to cancel current operation
|
||||
if (PRINT) System.out.println("interrupt access to model:" + toString());
|
||||
throw new ProcessCanceledException();
|
||||
}
|
||||
if (delay > 0) {
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
|
||||
Reference in New Issue
Block a user