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
;
12 * Progress reporting system, possibly nested.
16 public class Progress
{
17 public interface ProgressListener
extends EventListener
{
19 * A progression event.
22 * the {@link Progress} object that generated it, not
23 * necessarily the same as the one where the listener was
24 * attached (it could be a child {@link Progress} of this
27 * the first non-null name of the {@link Progress} step that
28 * generated this event
30 public void progress(Progress progress
, String name
);
33 private Progress parent
= null;
34 private Object lock
= new Object();
36 private Map
<Progress
, Double
> children
;
37 private List
<ProgressListener
> listeners
;
40 private double relativeLocalProgress
;
41 private double relativeProgress
; // children included
44 * Create a new default unnamed {@link Progress}, from 0 to 100.
51 * Create a new default {@link Progress}, from 0 to 100.
54 * the name of this {@link Progress} step
56 public Progress(String name
) {
61 * Create a new unnamed {@link Progress}, from min to max.
64 * the minimum progress value (and starting value) -- must be
67 * the maximum progress value
69 public Progress(int min
, int max
) {
74 * Create a new {@link Progress}, from min to max.
77 * the name of this {@link Progress} step
79 * the minimum progress value (and starting value) -- must be
82 * the maximum progress value
84 public Progress(String name
, int min
, int max
) {
86 this.children
= new HashMap
<Progress
, Double
>();
87 this.listeners
= new ArrayList
<Progress
.ProgressListener
>();
93 * The name of this {@link Progress} step.
97 public String
getName() {
102 * The name of this {@link Progress} step.
107 public void setName(String name
) {
113 * The minimum progress value.
117 public int getMin() {
122 * The minimum progress value.
129 * if min < 0 or if min > max
131 public void setMin(int min
) {
133 throw new Error("negative values not supported");
136 synchronized (getLock()) {
139 "The minimum progress value must be <= the maximum progress value");
147 * The maximum progress value.
151 public int getMax() {
156 * The maximum progress value (must be >= the minimum progress value).
165 public void setMax(int max
) {
166 synchronized (getLock()) {
169 "The maximum progress value must be >= the minimum progress value");
177 * Set both the minimum and maximum progress values.
185 * if min < 0 or if min > max
187 public void setMinMax(int min
, int max
) {
189 throw new Error("negative values not supported");
194 "The minimum progress value must be <= the maximum progress value");
197 synchronized (getLock()) {
204 * Get the total progress value (including the optional children
205 * {@link Progress}) on a {@link Progress#getMin()} to
206 * {@link Progress#getMax()} scale.
208 * @return the progress the value
210 public int getProgress() {
211 return (int) Math
.round(relativeProgress
* (max
- min
));
215 * Set the local progress value (not including the optional children
216 * {@link Progress}), on a {@link Progress#getMin()} to
217 * {@link Progress#getMax()} scale.
220 * the progress to set
222 public void setProgress(int progress
) {
223 synchronized (getLock()) {
224 double childrenProgress
= relativeProgress
- relativeLocalProgress
;
226 relativeLocalProgress
= ((double) progress
) / (max
- min
);
228 setRelativeProgress(this, name
, relativeLocalProgress
234 * Get the total progress value (including the optional children
235 * {@link Progress}) on a 0.0 to 1.0 scale.
237 * @return the progress
239 public double getRelativeProgress() {
240 return relativeProgress
;
244 * Set the total progress value (including the optional children
245 * {@link Progress}), on a 0 to 1 scale.
248 * the {@link Progress} to report as the progression emitter
250 * the current name (if it is NULL, the first non-null name in
251 * the hierarchy will overwrite it) of the {@link Progress} who
252 * emitted this change
253 * @param relativeProgress
254 * the progress to set
256 private void setRelativeProgress(Progress pg
, String name
,
257 double relativeProgress
) {
258 synchronized (getLock()) {
259 relativeProgress
= Math
.max(0, relativeProgress
);
260 relativeProgress
= Math
.min(1, relativeProgress
);
261 this.relativeProgress
= relativeProgress
;
268 * Get the total progress value (including the optional children
269 * {@link Progress}) on a 0 to 1 scale.
271 * @return the progress the value
273 private int getLocalProgress() {
274 return (int) Math
.round(relativeLocalProgress
* (max
- min
));
278 * Add some value to the current progression of this {@link Progress}.
283 public void add(int step
) {
284 synchronized (getLock()) {
285 setProgress(getLocalProgress() + step
);
290 * Check if the action corresponding to this {@link Progress} is done (i.e.,
291 * if its progress value == its max value).
293 * @return TRUE if it is
295 public boolean isDone() {
296 return getProgress() == max
;
300 * Mark the {@link Progress} as done by setting its value to max.
303 synchronized (getLock()) {
304 double childrenProgress
= relativeProgress
- relativeLocalProgress
;
305 relativeLocalProgress
= 1 - childrenProgress
;
306 setRelativeProgress(this, name
, 1d
);
311 * Return the list of direct children of this {@link Progress}.
313 * @return the children (Who will think of the children??)
315 public Set
<Progress
> getChildren() {
316 return children
.keySet();
320 * Notify the listeners that this {@link Progress} changed value.
325 * the current name (if it is NULL, the first non-null name in
326 * the hierarchy will overwrite it) of the {@link Progress} who
327 * emitted this change
329 private void changed(Progress pg
, String name
) {
338 synchronized (getLock()) {
339 for (ProgressListener l
: listeners
) {
340 l
.progress(pg
, name
);
346 * Add a {@link ProgressListener} that will trigger on progress changes.
348 * Note: the {@link Progress} that will be reported will be the active
349 * progress, not necessarily the same as the current one (it could be a
350 * child {@link Progress} of this {@link Progress}).
355 public void addProgressListener(ProgressListener l
) {
356 this.listeners
.add(l
);
360 * Remove a {@link ProgressListener} that would trigger on progress changes.
365 * @return TRUE if it was found (and removed)
367 public boolean removeProgressListener(ProgressListener l
) {
368 return this.listeners
.remove(l
);
372 * Add a child {@link Progress} of the given weight.
375 * the child {@link Progress} to add
377 * the weight (on a {@link Progress#getMin()} to
378 * {@link Progress#getMax()} scale) of this child
379 * {@link Progress} in relation to its parent
382 * if weight exceed {@link Progress#getMax()} or if progress
383 * already has a parent
385 public void addProgress(Progress progress
, double weight
) {
386 if (weight
< min
|| weight
> max
) {
387 throw new Error(String
.format(
388 "Progress object %s cannot have a weight of %f, "
389 + "it is outside of its parent (%s) range (%f)",
390 progress
.name
, weight
, name
, max
));
393 if (progress
.parent
!= null) {
394 throw new Error(String
.format(
395 "Progress object %s cannot be added to %s, "
396 + "as it already has a parent (%s)", progress
.name
,
397 name
, progress
.parent
.name
));
400 progress
.addProgressListener(new ProgressListener() {
402 public void progress(Progress pg
, String name
) {
403 synchronized (getLock()) {
404 double total
= relativeLocalProgress
;
405 for (Entry
<Progress
, Double
> entry
: children
.entrySet()) {
406 total
+= (entry
.getValue() / (max
- min
))
407 * entry
.getKey().getRelativeProgress();
410 setRelativeProgress(pg
, name
, total
);
415 this.children
.put(progress
, weight
);
419 * The lock object to use (this one or the recursively-parent one).
421 * @return the lock object to use
423 private Object
getLock() {
424 synchronized (lock
) {
425 if (parent
!= null) {
426 return parent
.getLock();