e3dbdaf516d9984430d17ec454679c39da5e05a1
[idea/community.git] / platform / diff-impl / src / com / intellij / diff / chains / AsyncDiffRequestChain.java
1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.diff.chains;
3
4 import com.intellij.diff.chains.SimpleDiffRequestChain.DiffRequestProducerWrapper;
5 import com.intellij.diff.requests.ErrorDiffRequest;
6 import com.intellij.diff.requests.LoadingDiffRequest;
7 import com.intellij.openapi.Disposable;
8 import com.intellij.openapi.ListSelection;
9 import com.intellij.openapi.progress.ProgressIndicator;
10 import com.intellij.openapi.progress.util.BackgroundTaskUtil;
11 import com.intellij.openapi.util.UserDataHolderBase;
12 import com.intellij.util.EventDispatcher;
13 import com.intellij.util.concurrency.annotations.RequiresBackgroundThread;
14 import com.intellij.util.concurrency.annotations.RequiresEdt;
15 import org.jetbrains.annotations.NotNull;
16 import org.jetbrains.annotations.Nullable;
17
18 import java.util.EventListener;
19
20 public abstract class AsyncDiffRequestChain extends UserDataHolderBase implements DiffRequestSelectionChain {
21   private final EventDispatcher<Listener> myDispatcher = EventDispatcher.create(Listener.class);
22
23   private volatile ListSelection<? extends DiffRequestProducer> myRequests = null;
24
25   @Nullable private ProgressIndicator myIndicator;
26   private int myAssignments = 0;
27
28   public void addListener(@NotNull Listener listener, @NotNull Disposable disposable) {
29     myDispatcher.addListener(listener, disposable);
30   }
31
32   public void removeListener(@NotNull Listener listener) {
33     myDispatcher.removeListener(listener);
34   }
35
36   @Override
37   public @NotNull ListSelection<? extends DiffRequestProducer> getListSelection() {
38     ListSelection<? extends DiffRequestProducer> requests = myRequests;
39     if (requests == null) {
40       return ListSelection.createSingleton(new DiffRequestProducerWrapper(new LoadingDiffRequest()));
41     }
42     return requests;
43   }
44
45   @NotNull
46   @RequiresBackgroundThread
47   public ListSelection<? extends DiffRequestProducer> loadRequestsInBackground() {
48     try {
49       return loadRequestProducers();
50     }
51     catch (DiffRequestProducerException e) {
52       return ListSelection.createSingleton(new DiffRequestProducerWrapper(new ErrorDiffRequest(e)));
53     }
54   }
55
56   @RequiresEdt
57   public void onAssigned(boolean isAssigned) {
58     if (isAssigned) {
59       if (myAssignments == 0 && myIndicator == null) {
60         myIndicator = startLoading();
61       }
62       myAssignments++;
63     }
64     else {
65       myAssignments--;
66       if (myAssignments == 0 && myIndicator != null) {
67         myIndicator.cancel();
68         myIndicator = null;
69       }
70     }
71     assert myAssignments >= 0;
72   }
73
74   @Nullable
75   @RequiresEdt
76   private ProgressIndicator startLoading() {
77     if (myRequests != null) return null;
78
79     return BackgroundTaskUtil.executeAndTryWait(indicator -> {
80       ListSelection<? extends DiffRequestProducer> producers = loadRequestsInBackground();
81       return () -> {
82         indicator.checkCanceled();
83         applyLoadedChanges(producers);
84       };
85     }, null);
86   }
87
88   @RequiresEdt
89   private void applyLoadedChanges(@NotNull ListSelection<? extends DiffRequestProducer> producers) {
90     if (myRequests != null) return;
91
92     myRequests = producers;
93     myIndicator = null;
94
95     myDispatcher.getMulticaster().onRequestsLoaded();
96   }
97
98   @NotNull
99   @RequiresBackgroundThread
100   protected abstract ListSelection<? extends DiffRequestProducer> loadRequestProducers() throws DiffRequestProducerException;
101
102   public interface Listener extends EventListener {
103     @RequiresEdt
104     void onRequestsLoaded();
105   }
106 }