Version 1.3.5: better ProgressBar
[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
96 /**
97 * The minimum progress value.
98 *
99 * @return the min
100 */
101 public int getMin() {
102 return min;
103 }
104
105 /**
106 * The minimum progress value.
107 *
108 * @param min
109 * the min to set
110 */
111 public void setMin(int min) {
112 if (min < 0) {
113 throw new Error("negative values not supported");
114 }
115
116 if (min > max) {
117 throw new Error(
118 "The minimum progress value must be <= the maximum progress value");
119 }
120
121 this.min = min;
122 }
123
124 /**
125 * The maximum progress value.
126 *
127 * @return the max
128 */
129 public int getMax() {
130 return max;
131 }
132
133 /**
134 * The maximum progress value (must be >= the minimum progress value).
135 *
136 * @param max
137 * the max to set
138 */
139 public void setMax(int max) {
140 if (max < min) {
141 throw new Error(
142 "The maximum progress value must be >= the minimum progress value");
143 }
144
145 this.max = max;
146 }
147
148 /**
149 * Set both the minimum and maximum progress values.
150 *
151 * @param min
152 * the min
153 * @param max
154 * the max
155 */
156 public void setMinMax(int min, int max) {
157 if (min < 0) {
158 throw new Error("negative values not supported");
159 }
160
161 if (min > max) {
162 throw new Error(
163 "The minimum progress value must be <= the maximum progress value");
164 }
165
166 this.min = min;
167 this.max = max;
168 }
169
170 /**
171 * Get the total progress value (including the optional children
172 * {@link Progress}) on a {@link Progress#getMin()} to
173 * {@link Progress#getMax()} scale.
174 *
175 * @return the progress the value
176 */
177 public int getProgress() {
178 return progress;
179 }
180
181 /**
182 * Set the local progress value (not including the optional children
183 * {@link Progress}), on a {@link Progress#getMin()} to
184 * {@link Progress#getMax()} scale.
185 *
186 * @param progress
187 * the progress to set
188 */
189 public void setProgress(int progress) {
190 int diff = this.progress - this.localProgress;
191 this.localProgress = progress;
88b36f83 192 setTotalProgress(this, name, progress + diff);
86057589
NR
193 }
194
195 /**
196 * Check if the action corresponding to this {@link Progress} is done (i.e.,
197 * if its progress value is >= its max value).
198 *
199 * @return TRUE if it is
200 */
201 public boolean isDone() {
202 return progress >= max;
203 }
204
205 /**
206 * Get the total progress value (including the optional children
207 * {@link Progress}) on a 0.0 to 1.0 scale.
208 *
209 * @return the progress
210 */
211 public double getRelativeProgress() {
212 return (((double) progress) / (max - min));
213 }
214
88b36f83
NR
215 /**
216 * Return the list of direct children of this {@link Progress}.
217 *
218 * @return the children (who will think of them??)
219 */
220 public Set<Progress> getChildren() {
221 return children.keySet();
222 }
223
86057589
NR
224 /**
225 * Set the total progress value (including the optional children
226 * {@link Progress}), on a {@link Progress#getMin()} to
227 * {@link Progress#getMax()} scale.
228 *
88b36f83
NR
229 * @param pg
230 * the {@link Progress} to report as the progression emitter
86057589
NR
231 * @param name
232 * the current name (if it is NULL, the first non-null name in
88b36f83
NR
233 * the hierarchy will overwrite it) of the {@link Progress} who
234 * emitted this change
86057589
NR
235 * @param progress
236 * the progress to set
237 */
88b36f83 238 private void setTotalProgress(Progress pg, String name, int progress) {
86057589
NR
239 this.progress = progress;
240
241 for (ProgressListener l : listeners) {
88b36f83 242 l.progress(pg, name);
86057589
NR
243 }
244 }
245
246 /**
247 * Add a {@link ProgressListener} that will trigger on progress changes.
248 *
249 * @param l
250 * the listener
251 */
252 public void addProgressListener(ProgressListener l) {
253 this.listeners.add(l);
254 }
255
256 /**
257 * Add a child {@link Progress} of the given weight.
258 *
259 * @param progress
260 * the child {@link Progress} to add
261 * @param weight
262 * the weight (on a {@link Progress#getMin()} to
263 * {@link Progress#getMax()} scale) of this child
264 * {@link Progress} in relation to its parent
265 */
266 public void addProgress(Progress progress, double weight) {
267 if (weight < min || weight > max) {
268 throw new Error(
269 "A Progress object cannot have a weight outside its parent range");
270 }
271
272 // Note: this is quite inefficient, especially with many children
273 // TODO: improve it?
274 progress.addProgressListener(new ProgressListener() {
275 public void progress(Progress progress, String name) {
276 double total = ((double) localProgress) / (max - min);
277 for (Entry<Progress, Double> entry : children.entrySet()) {
278 total += (entry.getValue() / (max - min))
279 * entry.getKey().getRelativeProgress();
280 }
281
282 if (name == null) {
283 name = Progress.this.name;
284 }
285
88b36f83
NR
286 setTotalProgress(progress, name,
287 (int) Math.round(total * (max - min)));
86057589
NR
288 }
289 });
290
291 this.children.put(progress, weight);
292 }
293}