001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.gui.progress; 003 004 import java.util.Arrays; 005 import java.util.Iterator; 006 import java.util.LinkedList; 007 import java.util.Queue; 008 009 public abstract class AbstractProgressMonitor implements ProgressMonitor { 010 011 private static class Request { 012 AbstractProgressMonitor originator; 013 int childTicks; 014 double currentValue; 015 016 String title; 017 String customText; 018 String extraText; 019 Boolean intermediate; 020 021 boolean finishRequested; 022 } 023 024 private final CancelHandler cancelHandler; 025 026 protected enum State {INIT, IN_TASK, IN_SUBTASK, FINISHED} 027 028 protected State state = State.INIT; 029 030 int ticksCount; 031 int ticks; 032 private int childTicks; 033 034 private String taskTitle; 035 private String customText; 036 private String extraText; 037 private String shownTitle; 038 private String shownCustomText; 039 private boolean intermediateTask; 040 041 private Queue<Request> requests = new LinkedList<Request>(); 042 private AbstractProgressMonitor currentChild; 043 private Request requestedState = new Request(); 044 045 protected abstract void doBeginTask(); 046 protected abstract void doFinishTask(); 047 protected abstract void doSetIntermediate(boolean value); 048 protected abstract void doSetTitle(String title); 049 protected abstract void doSetCustomText(String title); 050 051 protected AbstractProgressMonitor(CancelHandler cancelHandler) { 052 this.cancelHandler = cancelHandler; 053 } 054 055 protected void checkState(State... expectedStates) { 056 for (State s:expectedStates) { 057 if (s == state) 058 return; 059 } 060 throw new ProgressException("Expected states are %s but current state is %s", Arrays.asList(expectedStates).toString(), state); 061 } 062 063 /*======= 064 * Tasks 065 =======*/ 066 067 public void beginTask(String title) { 068 beginTask(title, DEFAULT_TICKS); 069 } 070 071 public synchronized void beginTask(String title, int ticks) { 072 this.taskTitle = title; 073 checkState(State.INIT); 074 state = State.IN_TASK; 075 doBeginTask(); 076 setTicksCount(ticks); 077 resetState(); 078 } 079 080 public synchronized void finishTask() { 081 if (state != State.FINISHED) { 082 083 if (state == State.IN_SUBTASK) { 084 requestedState.finishRequested = true; 085 } else { 086 checkState(State.IN_TASK); 087 state = State.FINISHED; 088 doFinishTask(); 089 } 090 } 091 } 092 093 public synchronized void invalidate() { 094 if (state == State.INIT) { 095 state = State.FINISHED; 096 doFinishTask(); 097 } 098 } 099 100 public synchronized void subTask(final String title) { 101 if (state == State.IN_SUBTASK) { 102 if (title != null) { 103 requestedState.title = title; 104 } 105 requestedState.intermediate = false; 106 } else { 107 checkState(State.IN_TASK); 108 if (title != null) { 109 this.taskTitle = title; 110 resetState(); 111 } 112 this.intermediateTask = false; 113 doSetIntermediate(false); 114 } 115 } 116 117 public synchronized void indeterminateSubTask(String title) { 118 if (state == State.IN_SUBTASK) { 119 if (title != null) { 120 requestedState.title = title; 121 } 122 requestedState.intermediate = true; 123 } else { 124 checkState(State.IN_TASK); 125 if (title != null) { 126 this.taskTitle = title; 127 resetState(); 128 } 129 this.intermediateTask = true; 130 doSetIntermediate(true); 131 } 132 } 133 134 public synchronized void setCustomText(String text) { 135 if (state == State.IN_SUBTASK) { 136 requestedState.customText = text; 137 } else { 138 this.customText = text; 139 resetState(); 140 } 141 } 142 143 public synchronized void setExtraText(String text) { 144 if (state == State.IN_SUBTASK) { 145 requestedState.extraText = text; 146 } else { 147 this.extraText = text; 148 resetState(); 149 } 150 } 151 152 /** 153 * Default implementation is empty. Override in subclasses to display the log messages. 154 */ 155 public void appendLogMessage(String message) { 156 // do nothing 157 } 158 159 private void resetState() { 160 String newTitle; 161 if (extraText != null) { 162 newTitle = taskTitle + " " + extraText; 163 } else { 164 newTitle = taskTitle; 165 } 166 167 if (newTitle == null?shownTitle != null:!newTitle.equals(shownTitle)) { 168 shownTitle = newTitle; 169 doSetTitle(shownTitle); 170 } 171 172 if (customText == null?shownCustomText != null:!customText.equals(shownCustomText)) { 173 shownCustomText = customText; 174 doSetCustomText(shownCustomText); 175 } 176 doSetIntermediate(intermediateTask); 177 } 178 179 public void cancel() { 180 cancelHandler.cancel(); 181 } 182 183 public boolean isCanceled() { 184 return cancelHandler.isCanceled(); 185 } 186 187 public void addCancelListener(CancelListener listener) { 188 cancelHandler.addCancelListener(listener); 189 } 190 191 public void removeCancelListener(CancelListener listener) { 192 cancelHandler.removeCancelListener(listener); 193 } 194 195 /*================= 196 * Ticks handling 197 ==================*/ 198 199 abstract void updateProgress(double value); 200 201 public synchronized void setTicks(int ticks) { 202 if (ticks >= ticksCount) { 203 ticks = ticksCount - 1; 204 } 205 this.ticks = ticks; 206 internalUpdateProgress(0); 207 } 208 209 public synchronized void setTicksCount(int ticks) { 210 this.ticksCount = ticks; 211 internalUpdateProgress(0); 212 } 213 214 public void worked(int ticks) { 215 if (ticks == ALL_TICKS) { 216 setTicks(this.ticksCount - 1); 217 } else { 218 setTicks(this.ticks + ticks); 219 } 220 } 221 222 private void internalUpdateProgress(double childProgress) { 223 if (childProgress > 1) { 224 childProgress = 1; 225 } 226 checkState(State.IN_TASK, State.IN_SUBTASK); 227 updateProgress(ticksCount == 0?0:(ticks + childProgress * childTicks) / ticksCount); 228 } 229 230 public synchronized int getTicks() { 231 return ticks; 232 } 233 234 public synchronized int getTicksCount() { 235 return ticksCount; 236 } 237 238 /*========== 239 * Subtasks 240 ==========*/ 241 242 public synchronized ProgressMonitor createSubTaskMonitor(int ticks, boolean internal) { 243 if (ticks == ALL_TICKS) { 244 ticks = ticksCount - this.ticks; 245 } 246 247 if (state == State.IN_SUBTASK) { 248 Request request = new Request(); 249 request.originator = new ChildProgress(this, cancelHandler, internal); 250 request.childTicks = ticks; 251 requests.add(request); 252 return request.originator; 253 } else { 254 checkState(State.IN_TASK); 255 state = State.IN_SUBTASK; 256 this.childTicks = ticks; 257 currentChild = new ChildProgress(this, cancelHandler, internal); 258 return currentChild; 259 } 260 } 261 262 private void applyChildRequest(Request request) { 263 if (request.customText != null) { 264 doSetCustomText(request.customText); 265 } 266 267 if (request.title != null) { 268 doSetTitle(request.title); 269 } 270 271 if (request.intermediate != null) { 272 doSetIntermediate(request.intermediate); 273 } 274 275 internalUpdateProgress(request.currentValue); 276 } 277 278 private void applyThisRequest(Request request) { 279 if (request.finishRequested) { 280 finishTask(); 281 } else { 282 if (request.customText != null) { 283 this.customText = request.customText; 284 } 285 286 if (request.title != null) { 287 this.taskTitle = request.title; 288 } 289 290 if (request.intermediate != null) { 291 this.intermediateTask = request.intermediate; 292 } 293 294 if (request.extraText != null) { 295 this.extraText = request.extraText; 296 } 297 298 resetState(); 299 } 300 } 301 302 protected synchronized void childFinished(AbstractProgressMonitor child) { 303 checkState(State.IN_SUBTASK); 304 if (currentChild == child) { 305 setTicks(ticks + childTicks); 306 if (requests.isEmpty()) { 307 state = State.IN_TASK; 308 applyThisRequest(requestedState); 309 requestedState = new Request(); 310 } else { 311 Request newChild = requests.poll(); 312 currentChild = newChild.originator; 313 childTicks = newChild.childTicks; 314 applyChildRequest(newChild); 315 } 316 } else { 317 Iterator<Request> it = requests.iterator(); 318 while (it.hasNext()) { 319 if (it.next().originator == child) { 320 it.remove(); 321 return; 322 } 323 } 324 throw new ProgressException("Subtask %s not found", child); 325 } 326 } 327 328 private Request getRequest(AbstractProgressMonitor child) { 329 for (Request request:requests) { 330 if (request.originator == child) 331 return request; 332 } 333 throw new ProgressException("Subtask %s not found", child); 334 } 335 336 protected synchronized void childSetProgress(AbstractProgressMonitor child, double value) { 337 checkState(State.IN_SUBTASK); 338 if (currentChild == child) { 339 internalUpdateProgress(value); 340 } else { 341 getRequest(child).currentValue = value; 342 } 343 } 344 345 protected synchronized void childSetTitle(AbstractProgressMonitor child, String title) { 346 checkState(State.IN_SUBTASK); 347 if (currentChild == child) { 348 doSetTitle(title); 349 } else { 350 getRequest(child).title = title; 351 } 352 } 353 354 protected synchronized void childSetCustomText(AbstractProgressMonitor child, String customText) { 355 checkState(State.IN_SUBTASK); 356 if (currentChild == child) { 357 doSetCustomText(customText); 358 } else { 359 getRequest(child).customText = customText; 360 } 361 } 362 363 protected synchronized void childSetIntermediate(AbstractProgressMonitor child, boolean value) { 364 checkState(State.IN_SUBTASK); 365 if (currentChild == child) { 366 doSetIntermediate(value); 367 } else { 368 getRequest(child).intermediate = value; 369 } 370 } 371 }