Make Bundle read/write (still no tests)
[nikiroo-utils.git] / src / be / nikiroo / utils / Progress.java
CommitLineData
b3aad1f9 1package be.nikiroo.utils;
86057589
NR
2
3import java.util.ArrayList;
4import java.util.EventListener;
5import java.util.HashMap;
6import java.util.List;
7import java.util.Map;
8import java.util.Map.Entry;
88b36f83 9import java.util.Set;
86057589
NR
10
11/**
12 * Progress reporting system, possibly nested.
13 *
14 * @author niki
15 */
16public class Progress {
17 public interface ProgressListener extends EventListener {
18 /**
19 * A progression event.
20 *
21 * @param progress
88b36f83 22 * the {@link Progress} object that generated it
86057589
NR
23 * @param name
24 * the first non-null name of the {@link Progress} step that
25 * generated this event
26 */
27 public void progress(Progress progress, String name);
28 }
29
30 private String name;
31 private Map<Progress, Double> children;
32 private List<ProgressListener> listeners;
33 private int min;
34 private int max;
35 private int localProgress;
36 private int progress; // children included
37
38 /**
39 * Create a new default unnamed {@link Progress}, from 0 to 100.
40 */
41 public Progress() {
42 this(null);
43 }
44
45 /**
46 * Create a new default {@link Progress}, from 0 to 100.
47 *
48 * @param name
49 * the name of this {@link Progress} step
50 */
51 public Progress(String name) {
52 this(name, 0, 100);
53 }
54
55 /**
56 * Create a new unnamed {@link Progress}, from min to max.
57 *
58 * @param min
59 * the minimum progress value (and starting value) -- must be
60 * non-negative
61 * @param max
62 * the maximum progress value
63 */
64 public Progress(int min, int max) {
65 this(null, min, max);
66 }
67
68 /**
69 * Create a new {@link Progress}, from min to max.
70 *
71 * @param name
72 * the name of this {@link Progress} step
73 * @param min
74 * the minimum progress value (and starting value) -- must be
75 * non-negative
76 * @param max
77 * the maximum progress value
78 */
79 public Progress(String name, int min, int max) {
80 this.name = name;
81 this.children = new HashMap<Progress, Double>();
82 this.listeners = new ArrayList<Progress.ProgressListener>();
83 setMinMax(min, max);
84 setProgress(min);
85 }
86
87 /**
88 * The name of this {@link Progress} step.
89 *
90 * @return the name
91 */
92 public String getName() {
93 return name;
94 }
95
2998b78a
NR
96 /**
97 * The name of this {@link Progress} step.
98 *
99 * @param name
100 * the new name
101 */
102 public void setName(String name) {
103 this.name = name;
104 // will fire an action event:
105 setProgress(this.localProgress);
106 }
107
86057589
NR
108 /**
109 * The minimum progress value.
110 *
111 * @return the min
112 */
113 public int getMin() {
114 return min;
115 }
116
117 /**
118 * The minimum progress value.
119 *
120 * @param min
121 * the min to set
122 */
123 public void setMin(int min) {
124 if (min < 0) {
125 throw new Error("negative values not supported");
126 }
127
128 if (min > max) {
129 throw new Error(
130 "The minimum progress value must be <= the maximum progress value");
131 }
132
133 this.min = min;
134 }
135
136 /**
137 * The maximum progress value.
138 *
139 * @return the max
140 */
141 public int getMax() {
142 return max;
143 }
144
145 /**
146 * The maximum progress value (must be >= the minimum progress value).
147 *
148 * @param max
149 * the max to set
150 */
151 public void setMax(int max) {
152 if (max < min) {
153 throw new Error(
154 "The maximum progress value must be >= the minimum progress value");
155 }
156
157 this.max = max;
158 }
159
160 /**
161 * Set both the minimum and maximum progress values.
162 *
163 * @param min
164 * the min
165 * @param max
166 * the max
167 */
168 public void setMinMax(int min, int max) {
169 if (min < 0) {
170 throw new Error("negative values not supported");
171 }
172
173 if (min > max) {
174 throw new Error(
175 "The minimum progress value must be <= the maximum progress value");
176 }
177
178 this.min = min;
179 this.max = max;
180 }
181
182 /**
183 * Get the total progress value (including the optional children
184 * {@link Progress}) on a {@link Progress#getMin()} to
185 * {@link Progress#getMax()} scale.
186 *
187 * @return the progress the value
188 */
189 public int getProgress() {
190 return progress;
191 }
192
193 /**
194 * Set the local progress value (not including the optional children
195 * {@link Progress}), on a {@link Progress#getMin()} to
196 * {@link Progress#getMax()} scale.
197 *
198 * @param progress
199 * the progress to set
200 */
201 public void setProgress(int progress) {
202 int diff = this.progress - this.localProgress;
203 this.localProgress = progress;
88b36f83 204 setTotalProgress(this, name, progress + diff);
86057589
NR
205 }
206
207 /**
208 * Check if the action corresponding to this {@link Progress} is done (i.e.,
209 * if its progress value is >= its max value).
210 *
211 * @return TRUE if it is
212 */
213 public boolean isDone() {
214 return progress >= max;
215 }
216
217 /**
218 * Get the total progress value (including the optional children
219 * {@link Progress}) on a 0.0 to 1.0 scale.
220 *
221 * @return the progress
222 */
223 public double getRelativeProgress() {
224 return (((double) progress) / (max - min));
225 }
226
88b36f83
NR
227 /**
228 * Return the list of direct children of this {@link Progress}.
229 *
230 * @return the children (who will think of them??)
231 */
232 public Set<Progress> getChildren() {
233 return children.keySet();
234 }
235
86057589
NR
236 /**
237 * Set the total progress value (including the optional children
238 * {@link Progress}), on a {@link Progress#getMin()} to
239 * {@link Progress#getMax()} scale.
240 *
88b36f83
NR
241 * @param pg
242 * the {@link Progress} to report as the progression emitter
86057589
NR
243 * @param name
244 * the current name (if it is NULL, the first non-null name in
88b36f83
NR
245 * the hierarchy will overwrite it) of the {@link Progress} who
246 * emitted this change
86057589
NR
247 * @param progress
248 * the progress to set
249 */
88b36f83 250 private void setTotalProgress(Progress pg, String name, int progress) {
86057589
NR
251 this.progress = progress;
252
253 for (ProgressListener l : listeners) {
88b36f83 254 l.progress(pg, name);
86057589
NR
255 }
256 }
257
258 /**
259 * Add a {@link ProgressListener} that will trigger on progress changes.
260 *
261 * @param l
262 * the listener
263 */
264 public void addProgressListener(ProgressListener l) {
265 this.listeners.add(l);
266 }
267
2998b78a
NR
268 /**
269 * Remove a {@link ProgressListener} that would trigger on progress changes.
270 *
271 * @param l
272 * the listener
273 *
274 * @return TRUE if it was found (and removed)
275 */
276 public boolean removeProgressListener(ProgressListener l) {
277 return this.listeners.remove(l);
278 }
279
86057589
NR
280 /**
281 * Add a child {@link Progress} of the given weight.
282 *
283 * @param progress
284 * the child {@link Progress} to add
285 * @param weight
286 * the weight (on a {@link Progress#getMin()} to
287 * {@link Progress#getMax()} scale) of this child
288 * {@link Progress} in relation to its parent
289 */
290 public void addProgress(Progress progress, double weight) {
291 if (weight < min || weight > max) {
292 throw new Error(
293 "A Progress object cannot have a weight outside its parent range");
294 }
295
296 // Note: this is quite inefficient, especially with many children
297 // TODO: improve it?
298 progress.addProgressListener(new ProgressListener() {
62c9ec78 299 public void progress(Progress pg, String name) {
86057589
NR
300 double total = ((double) localProgress) / (max - min);
301 for (Entry<Progress, Double> entry : children.entrySet()) {
302 total += (entry.getValue() / (max - min))
303 * entry.getKey().getRelativeProgress();
304 }
305
306 if (name == null) {
307 name = Progress.this.name;
308 }
309
62c9ec78 310 setTotalProgress(pg, name,
88b36f83 311 (int) Math.round(total * (max - min)));
86057589
NR
312 }
313 });
314
315 this.children.put(progress, weight);
316 }
317}