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