1 package org
.jetbrains
.idea
.svn
;
3 import com
.intellij
.openapi
.diagnostic
.Logger
;
4 import com
.intellij
.openapi
.util
.Pair
;
5 import com
.intellij
.openapi
.util
.text
.StringUtil
;
6 import com
.intellij
.util
.Consumer
;
7 import com
.intellij
.util
.Function
;
8 import org
.jetbrains
.annotations
.NotNull
;
9 import org
.jetbrains
.annotations
.Nullable
;
11 import java
.util
.ArrayList
;
12 import java
.util
.HashMap
;
13 import java
.util
.List
;
17 * For exactly same refresh requests buffering:
19 * - refresh requests can be merged into one, but general principle is that each request should be reliably followed by refresh action
20 * - at the moment only one refresh action is being done
21 * - if request had been submitted while refresh action was in progress, new refresh action is initiated right after first refresh action finishes
24 public class RequestsMerger
{
25 private static final Logger LOG
= Logger
.getInstance("#org.jetbrains.idea.svn.RequestsMerger");
26 private static final int ourDelay
= 300;
28 private final MyWorker myWorker
;
30 private final Object myLock
= new Object();
32 private MyState myState
;
33 private final Consumer
<Runnable
> myAlarm
;
35 private final List
<Runnable
> myWaitingStartListeners
;
36 private final List
<Runnable
> myWaitingFinishListeners
;
38 public RequestsMerger(final Runnable runnable
, final Consumer
<Runnable
> alarm
) {
40 myWorker
= new MyWorker(runnable
);
42 myState
= MyState
.empty
;
44 myWaitingStartListeners
= new ArrayList
<Runnable
>();
45 myWaitingFinishListeners
= new ArrayList
<Runnable
>();
48 public void request() {
49 LOG
.debug("ext: request");
50 doAction(MyAction
.request
);
53 public void waitRefresh(final Runnable runnable
) {
54 LOG
.debug("ext: wait refresh");
55 synchronized (myLock
) {
56 myWaitingStartListeners
.add(runnable
);
61 public void ensureInitialization(final Runnable runnable
) {
62 LOG
.debug("ext: ensure init");
63 synchronized (myLock
) {
64 if (myWorker
.isInitialized()) {
68 myWaitingStartListeners
.add(runnable
);
73 private class MyWorker
implements Runnable
{
74 private boolean myInitialized
;
75 private final Runnable myRunnable
;
77 private MyWorker(Runnable runnable
) {
78 myRunnable
= runnable
;
82 LOG
.debug("worker: started refresh");
84 doAction(MyAction
.start
);
86 synchronized (myLock
) {
90 doAction(MyAction
.finish
);
94 public boolean isInitialized() {
99 private void doAction(final MyAction action
) {
100 LOG
.debug("doAction: START " + action
.name());
101 final MyExitAction
[] exitActions
;
102 List
<Runnable
> toBeCalled
= null;
103 synchronized (myLock
) {
104 final MyState oldState
= myState
;
105 myState
= myState
.transition(action
);
106 if (oldState
.equals(myState
)) return;
107 exitActions
= MyTransitionAction
.getExit(oldState
, myState
);
109 LOG
.debug("doAction: oldState: " + oldState
.name() + ", newState: " + myState
.name());
111 if (LOG
.isDebugEnabled() && (exitActions
!= null)) {
112 final String debugExitActions
= StringUtil
.join(exitActions
, new Function
<MyExitAction
, String
>() {
113 public String
fun(MyExitAction exitAction
) {
114 return exitAction
.name();
117 LOG
.debug("exit actions: " + debugExitActions
);
119 if (exitActions
!= null) {
120 for (MyExitAction exitAction
: exitActions
) {
121 if (MyExitAction
.markStart
.equals(exitAction
)) {
122 myWaitingFinishListeners
.addAll(myWaitingStartListeners
);
123 myWaitingStartListeners
.clear();
124 } else if (MyExitAction
.markEnd
.equals(exitAction
)) {
125 toBeCalled
= new ArrayList
<Runnable
>(myWaitingFinishListeners
);
126 myWaitingFinishListeners
.clear();
131 if (exitActions
!= null) {
132 for (MyExitAction exitAction
: exitActions
) {
133 if (MyExitAction
.submitRequestToExecutor
.equals(exitAction
)) {
134 myAlarm
.consume(myWorker
);
135 //myAlarm.addRequest(myWorker, ourDelay);
136 //ApplicationManager.getApplication().executeOnPooledThread(myWorker);
140 if (toBeCalled
!= null) {
141 for (Runnable runnable
: toBeCalled
) {
145 LOG
.debug("doAction: END " + action
.name());
148 private static enum MyState
{
151 public MyState
transition(MyAction action
) {
152 if (MyAction
.request
.equals(action
)) {
153 return MyState
.requestSubmitted
;
155 logWrongAction(this, action
);
160 public MyState
transition(MyAction action
) {
161 if (MyAction
.finish
.equals(action
)) {
162 return MyState
.empty
;
163 } else if (MyAction
.request
.equals(action
)) {
164 return MyState
.inProgressRequestSubmitted
;
166 logWrongAction(this, action
);
169 inProgressRequestSubmitted() {
171 public MyState
transition(MyAction action
) {
172 if (MyAction
.finish
.equals(action
)) {
173 return MyState
.requestSubmitted
;
175 if (MyAction
.start
.equals(action
)) {
176 logWrongAction(this, action
);
182 public MyState
transition(MyAction action
) {
183 if (MyAction
.start
.equals(action
)) {
184 return MyState
.inProgress
;
185 } else if (MyAction
.finish
.equals(action
)) {
186 // to be able to be started by another request
187 logWrongAction(this, action
);
188 return MyState
.empty
;
195 public abstract MyState
transition(final MyAction action
);
197 private static void logWrongAction(final MyState state
, final MyAction action
) {
198 LOG
.info("Wrong action: state=" + state
.name() + ", action=" + action
.name());
202 private static class MyTransitionAction
{
203 private final static Map
<Pair
<MyState
, MyState
>, MyExitAction
[]> myMap
= new HashMap
<Pair
<MyState
, MyState
>, MyExitAction
[]>();
206 add(MyState
.empty
, MyState
.requestSubmitted
, MyExitAction
.submitRequestToExecutor
);
207 add(MyState
.requestSubmitted
, MyState
.inProgress
, MyExitAction
.markStart
);
208 add(MyState
.inProgress
, MyState
.empty
, MyExitAction
.markEnd
);
209 add(MyState
.inProgressRequestSubmitted
, MyState
.requestSubmitted
, MyExitAction
.submitRequestToExecutor
, MyExitAction
.markEnd
);
211 //... and not real but to be safe:
212 add(MyState
.inProgressRequestSubmitted
, MyState
.empty
, MyExitAction
.markEnd
);
213 add(MyState
.inProgress
, MyState
.requestSubmitted
, MyExitAction
.markEnd
);
216 private static void add(final MyState from
, final MyState to
, final MyExitAction
... action
) {
217 myMap
.put(new Pair
<MyState
, MyState
>(from
, to
), action
);
221 public static MyExitAction
[] getExit(final MyState from
, final MyState to
) {
222 return myMap
.get(new Pair
<MyState
, MyState
>(from
, to
));
226 private static enum MyExitAction
{
228 submitRequestToExecutor
,
233 private static enum MyAction
{