1 package be
.nikiroo
.utils
;
3 import java
.util
.ArrayList
;
4 import java
.util
.EventListener
;
5 import java
.util
.HashMap
;
8 import java
.util
.Map
.Entry
;
11 * Progress reporting system, possibly nested.
15 public class Progress
{
17 * This event listener is designed to report progress events from
22 public interface ProgressListener
extends EventListener
{
24 * A progression event.
27 * the {@link Progress} object that generated it, not
28 * necessarily the same as the one where the listener was
29 * attached (it could be a child {@link Progress} of this
32 * the first non-null name of the {@link Progress} step that
33 * generated this event
35 public void progress(Progress progress
, String name
);
38 private Progress parent
= null;
39 private Object lock
= new Object();
41 private Map
<Progress
, Double
> children
;
42 private List
<ProgressListener
> listeners
;
45 private double relativeLocalProgress
;
46 private double relativeProgress
; // children included
49 * Create a new default unnamed {@link Progress}, from 0 to 100.
56 * Create a new default {@link Progress}, from 0 to 100.
59 * the name of this {@link Progress} step
61 public Progress(String name
) {
66 * Create a new unnamed {@link Progress}, from min to max.
69 * the minimum progress value (and starting value) -- must be
72 * the maximum progress value
74 public Progress(int min
, int max
) {
79 * Create a new {@link Progress}, from min to max.
82 * the name of this {@link Progress} step
84 * the minimum progress value (and starting value) -- must be
87 * the maximum progress value
89 public Progress(String name
, int min
, int max
) {
91 this.children
= new HashMap
<Progress
, Double
>();
92 this.listeners
= new ArrayList
<Progress
.ProgressListener
>();
98 * The name of this {@link Progress} step.
102 public String
getName() {
107 * The name of this {@link Progress} step.
112 public void setName(String name
) {
118 * The minimum progress value.
122 public int getMin() {
127 * The minimum progress value.
133 * @throws RuntimeException
134 * if min < 0 or if min > max
136 public void setMin(int min
) {
138 throw new RuntimeException("negative values not supported");
141 synchronized (lock
) {
143 throw new RuntimeException(
144 "The minimum progress value must be <= the maximum progress value");
152 * The maximum progress value.
156 public int getMax() {
161 * The maximum progress value (must be >= the minimum progress value).
167 * @throws RuntimeException
170 public void setMax(int max
) {
171 synchronized (lock
) {
174 "The maximum progress value must be >= the minimum progress value");
182 * Set both the minimum and maximum progress values.
189 * @throws RuntimeException
190 * if min < 0 or if min > max
192 public void setMinMax(int min
, int max
) {
194 throw new RuntimeException("negative values not supported");
198 throw new RuntimeException(
199 "The minimum progress value must be <= the maximum progress value");
202 synchronized (lock
) {
209 * Get the total progress value (including the optional children
210 * {@link Progress}) on a {@link Progress#getMin()} to
211 * {@link Progress#getMax()} scale.
213 * @return the progress the value
215 public int getProgress() {
216 return (int) Math
.round(relativeProgress
* (max
- min
));
220 * Set the local progress value (not including the optional children
221 * {@link Progress}), on a {@link Progress#getMin()} to
222 * {@link Progress#getMax()} scale.
225 * the progress to set
227 public void setProgress(int progress
) {
228 synchronized (lock
) {
229 double childrenProgress
= relativeProgress
- relativeLocalProgress
;
231 relativeLocalProgress
= ((double) progress
) / (max
- min
);
233 setRelativeProgress(this, name
, relativeLocalProgress
239 * Get the total progress value (including the optional children
240 * {@link Progress}) on a 0.0 to 1.0 scale.
242 * @return the progress
244 public double getRelativeProgress() {
245 return relativeProgress
;
249 * Set the total progress value (including the optional children
250 * {@link Progress}), on a 0 to 1 scale.
253 * the {@link Progress} to report as the progression emitter
255 * the current name (if it is NULL, the first non-null name in
256 * the hierarchy will overwrite it) of the {@link Progress} who
257 * emitted this change
258 * @param relativeProgress
259 * the progress to set
261 private void setRelativeProgress(Progress pg
, String name
,
262 double relativeProgress
) {
263 synchronized (lock
) {
264 relativeProgress
= Math
.max(0, relativeProgress
);
265 relativeProgress
= Math
.min(1, relativeProgress
);
266 this.relativeProgress
= relativeProgress
;
273 * Get the total progress value (including the optional children
274 * {@link Progress}) on a 0 to 1 scale.
276 * @return the progress the value
278 private int getLocalProgress() {
279 return (int) Math
.round(relativeLocalProgress
* (max
- min
));
283 * Add some value to the current progression of this {@link Progress}.
288 public void add(int step
) {
289 synchronized (lock
) {
290 setProgress(getLocalProgress() + step
);
295 * Check if the action corresponding to this {@link Progress} is done (i.e.,
296 * if its progress value == its max value).
298 * @return TRUE if it is
300 public boolean isDone() {
301 return getProgress() == max
;
305 * Mark the {@link Progress} as done by setting its value to max.
308 synchronized (lock
) {
309 double childrenProgress
= relativeProgress
- relativeLocalProgress
;
310 relativeLocalProgress
= 1 - childrenProgress
;
311 setRelativeProgress(this, name
, 1d
);
316 * Return the list of direct children of this {@link Progress}.
318 * @return the children (Who will think of the children??)
320 public List
<Progress
> getChildren() {
321 synchronized (lock
) {
322 return new ArrayList
<Progress
>(children
.keySet());
327 * Notify the listeners that this {@link Progress} changed value.
330 * the emmiter, that is, the (sub-){link Progress} that just
331 * reported some change, not always the same as <tt>this</tt>
333 * the current name (if it is NULL, the first non-null name in
334 * the hierarchy will overwrite it) of the {@link Progress} who
335 * emitted this change
337 private void changed(Progress pg
, String name
) {
346 synchronized (lock
) {
347 for (ProgressListener l
: listeners
) {
348 l
.progress(pg
, name
);
354 * Add a {@link ProgressListener} that will trigger on progress changes.
356 * Note: the {@link Progress} that will be reported will be the active
357 * progress, not necessarily the same as the current one (it could be a
358 * child {@link Progress} of this {@link Progress}).
363 public void addProgressListener(ProgressListener l
) {
364 synchronized (lock
) {
365 this.listeners
.add(l
);
370 * Remove a {@link ProgressListener} that would trigger on progress changes.
375 * @return TRUE if it was found (and removed)
377 public boolean removeProgressListener(ProgressListener l
) {
378 synchronized (lock
) {
379 return this.listeners
.remove(l
);
384 * Add a child {@link Progress} of the given weight.
387 * the child {@link Progress} to add
389 * the weight (on a {@link Progress#getMin()} to
390 * {@link Progress#getMax()} scale) of this child
391 * {@link Progress} in relation to its parent
393 * @throws RuntimeException
394 * if weight exceed {@link Progress#getMax()} or if progress
395 * already has a parent
397 public void addProgress(Progress progress
, double weight
) {
398 if (weight
< min
|| weight
> max
) {
399 throw new RuntimeException(String
.format(
400 "Progress object %s cannot have a weight of %f, "
401 + "it is outside of its parent (%s) range (%d)",
402 progress
.name
, weight
, name
, max
));
405 if (progress
.parent
!= null) {
406 throw new RuntimeException(String
.format(
407 "Progress object %s cannot be added to %s, "
408 + "as it already has a parent (%s)", progress
.name
,
409 name
, progress
.parent
.name
));
412 ProgressListener progressListener
= new ProgressListener() {
414 public void progress(Progress pg
, String name
) {
415 synchronized (lock
) {
416 double total
= relativeLocalProgress
;
417 for (Entry
<Progress
, Double
> entry
: children
.entrySet()) {
418 total
+= (entry
.getValue() / (max
- min
))
419 * entry
.getKey().getRelativeProgress();
422 setRelativeProgress(pg
, name
, total
);
427 synchronized (lock
) {
428 progress
.parent
= this;
429 this.children
.put(progress
, weight
);
430 progress
.addProgressListener(progressListener
);