VCS: allow Git to report changed on server files comparing the whole tree. !! require...
[fedora-idea.git] / plugins / svn4idea / src / org / jetbrains / idea / svn / RequestsMerger.java
blob1f820bcb81bcda9df20b82ce69f4c677d2860211
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;
14 import java.util.Map;
16 /**
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) {
39 myAlarm = 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);
58 request();
61 public void ensureInitialization(final Runnable runnable) {
62 LOG.debug("ext: ensure init");
63 synchronized (myLock) {
64 if (myWorker.isInitialized()) {
65 runnable.run();
66 return;
68 myWaitingStartListeners.add(runnable);
70 request();
73 private class MyWorker implements Runnable {
74 private boolean myInitialized;
75 private final Runnable myRunnable;
77 private MyWorker(Runnable runnable) {
78 myRunnable = runnable;
81 public void run() {
82 LOG.debug("worker: started refresh");
83 try {
84 doAction(MyAction.start);
85 myRunnable.run();
86 synchronized (myLock) {
87 myInitialized = true;
89 } finally {
90 doAction(MyAction.finish);
94 public boolean isInitialized() {
95 return myInitialized;
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();
116 }, " ");
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) {
142 runnable.run();
145 LOG.debug("doAction: END " + action.name());
148 private static enum MyState {
149 empty() {
150 @NotNull
151 public MyState transition(MyAction action) {
152 if (MyAction.request.equals(action)) {
153 return MyState.requestSubmitted;
155 logWrongAction(this, action);
156 return this;
158 inProgress() {
159 @NotNull
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);
167 return this;
169 inProgressRequestSubmitted() {
170 @NotNull
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);
178 return this;
180 requestSubmitted() {
181 @NotNull
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;
190 return this;
193 // under lock
194 @NotNull
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[]>();
205 static {
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);
220 @Nullable
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 {
227 empty,
228 submitRequestToExecutor,
229 markStart,
230 markEnd
233 private static enum MyAction {
234 request,
235 start,
236 finish