Commit | Line | Data |
---|---|---|
9e834013 NR |
1 | package be.nikiroo.utils.resources; |
2 | ||
3 | import java.util.ArrayList; | |
0877d6f5 | 4 | import java.util.Iterator; |
9e834013 NR |
5 | import java.util.List; |
6 | ||
7 | import be.nikiroo.utils.resources.Meta.Format; | |
8 | ||
9 | /** | |
10 | * A graphical item that reflect a configuration option from the given | |
11 | * {@link Bundle}. | |
12 | * | |
13 | * @author niki | |
14 | * | |
15 | * @param <E> | |
16 | * the type of {@link Bundle} to edit | |
17 | */ | |
0877d6f5 | 18 | public class MetaInfo<E extends Enum<E>> implements Iterable<MetaInfo<E>> { |
9e834013 NR |
19 | private final Bundle<E> bundle; |
20 | private final E id; | |
21 | ||
22 | private Meta meta; | |
0877d6f5 | 23 | private List<MetaInfo<E>> children = new ArrayList<MetaInfo<E>>(); |
9e834013 NR |
24 | |
25 | private String value; | |
8517b60c NR |
26 | private List<Runnable> reloadedListeners = new ArrayList<Runnable>(); |
27 | private List<Runnable> saveListeners = new ArrayList<Runnable>(); | |
9e834013 NR |
28 | |
29 | private String name; | |
30 | private String description; | |
31 | ||
d5026c09 NR |
32 | private boolean dirty; |
33 | ||
76b51de9 NR |
34 | /** |
35 | * Create a new {@link MetaInfo} from a value (without children). | |
36 | * <p> | |
37 | * For instance, you can call | |
38 | * <tt>new MetaInfo(Config.class, configBundle, Config.MY_VALUE)</tt>. | |
39 | * | |
40 | * @param type | |
41 | * the type of enum the value is | |
42 | * @param bundle | |
43 | * the bundle this value belongs to | |
44 | * @param id | |
45 | * the value itself | |
46 | */ | |
9e834013 NR |
47 | public MetaInfo(Class<E> type, Bundle<E> bundle, E id) { |
48 | this.bundle = bundle; | |
49 | this.id = id; | |
50 | ||
51 | try { | |
52 | this.meta = type.getDeclaredField(id.name()).getAnnotation( | |
53 | Meta.class); | |
54 | } catch (NoSuchFieldException e) { | |
55 | } catch (SecurityException e) { | |
56 | } | |
57 | ||
58 | // We consider that if a description bundle is used, everything is in it | |
59 | ||
60 | String description = null; | |
61 | if (bundle.getDescriptionBundle() != null) { | |
62 | description = bundle.getDescriptionBundle().getString(id); | |
63 | if (description != null && description.trim().isEmpty()) { | |
64 | description = null; | |
65 | } | |
66 | } | |
9e834013 NR |
67 | if (description == null) { |
68 | description = meta.description(); | |
0877d6f5 NR |
69 | if (description == null) { |
70 | description = ""; | |
71 | } | |
d18e136e NR |
72 | } |
73 | ||
74 | String name = idToName(id, null); | |
75 | ||
76 | // Special rules for groups: | |
77 | if (meta.group()) { | |
78 | String groupName = description.split("\n")[0]; | |
79 | description = description.substring(groupName.length()).trim(); | |
80 | if (!groupName.isEmpty()) { | |
81 | name = groupName; | |
9e834013 NR |
82 | } |
83 | } | |
84 | ||
d18e136e NR |
85 | if (meta.def() != null && !meta.def().isEmpty()) { |
86 | if (!description.isEmpty()) { | |
87 | description += "\n\n"; | |
88 | } | |
89 | description += "(Default value: " + meta.def() + ")"; | |
9e834013 NR |
90 | } |
91 | ||
92 | this.name = name; | |
93 | this.description = description; | |
94 | ||
95 | reload(); | |
96 | } | |
97 | ||
98 | /** | |
d18e136e NR |
99 | * For normal items, this is the name of this item, deduced from its ID (or |
100 | * in other words, it is the ID but presented in a displayable form). | |
101 | * <p> | |
102 | * For group items, this is the first line of the description if it is not | |
103 | * empty (else, it is the ID in the same way as normal items). | |
9e834013 | 104 | * <p> |
d18e136e | 105 | * Never NULL. |
9e834013 | 106 | * |
d18e136e NR |
107 | * |
108 | * @return the name, never NULL | |
9e834013 NR |
109 | */ |
110 | public String getName() { | |
111 | return name; | |
112 | } | |
113 | ||
114 | /** | |
d18e136e NR |
115 | * A description for this item: what it is or does, how to explain that item |
116 | * to the user including what can be used here (i.e., %s = file name, %d = | |
117 | * file size...). | |
118 | * <p> | |
119 | * For group, the first line ('\\n'-separated) will be used as a title while | |
120 | * the rest will be the description. | |
121 | * <p> | |
122 | * If a default value is known, it will be specified here, too. | |
123 | * <p> | |
124 | * Never NULL. | |
9e834013 | 125 | * |
d18e136e | 126 | * @return the description, not NULL |
9e834013 NR |
127 | */ |
128 | public String getDescription() { | |
129 | return description; | |
130 | } | |
131 | ||
76b51de9 NR |
132 | /** |
133 | * The format this item is supposed to follow | |
134 | * | |
135 | * @return the format | |
136 | */ | |
9e834013 NR |
137 | public Format getFormat() { |
138 | return meta.format(); | |
139 | } | |
140 | ||
76b51de9 NR |
141 | /** |
142 | * The allowed list of values that a {@link Format#FIXED_LIST} item is | |
143 | * allowed to be, or a list of suggestions for {@link Format#COMBO_LIST} | |
144 | * items. | |
d18e136e NR |
145 | * <p> |
146 | * Will always allow an empty string in addition to the rest. | |
76b51de9 NR |
147 | * |
148 | * @return the list of values | |
149 | */ | |
0877d6f5 | 150 | public String[] getAllowedValues() { |
d18e136e NR |
151 | String[] list = meta.list(); |
152 | ||
153 | String[] withEmpty = new String[list.length + 1]; | |
154 | withEmpty[0] = ""; | |
155 | for (int i = 0; i < list.length; i++) { | |
156 | withEmpty[i + 1] = list[i]; | |
157 | } | |
158 | ||
159 | return withEmpty; | |
0877d6f5 NR |
160 | } |
161 | ||
76b51de9 NR |
162 | /** |
163 | * This item is a comma-separated list of values instead of a single value. | |
164 | * <p> | |
165 | * The list items are separated by a comma, each surrounded by | |
166 | * double-quotes, with backslashes and double-quotes escaped by a backslash. | |
167 | * <p> | |
168 | * Example: <tt>"un", "deux"</tt> | |
169 | * | |
170 | * @return TRUE if it is | |
171 | */ | |
0877d6f5 NR |
172 | public boolean isArray() { |
173 | return meta.array(); | |
174 | } | |
175 | ||
d5026c09 | 176 | /** |
74d2a495 NR |
177 | * A manual flag to specify if the data has been changed or not, which can |
178 | * be used by {@link MetaInfo#save(boolean)}. | |
d5026c09 NR |
179 | * |
180 | * @return TRUE if it is dirty (if it has changed) | |
181 | */ | |
182 | public boolean isDirty() { | |
183 | return dirty; | |
184 | } | |
185 | ||
186 | /** | |
74d2a495 NR |
187 | * A manual flag to specify that the data has been changed, which can be |
188 | * used by {@link MetaInfo#save(boolean)}. | |
d5026c09 NR |
189 | */ |
190 | public void setDirty() { | |
191 | this.dirty = true; | |
192 | } | |
193 | ||
194 | /** | |
195 | * The number of items in this item if it {@link MetaInfo#isArray()}, or -1 | |
196 | * if not. | |
197 | * | |
198 | * @param useDefaultIfEmpty | |
199 | * check the size of the default list instead if the list is | |
200 | * empty | |
201 | * | |
202 | * @return -1 or the number of items | |
203 | */ | |
204 | public int getListSize(boolean useDefaultIfEmpty) { | |
205 | if (!isArray()) { | |
206 | return -1; | |
207 | } | |
208 | ||
209 | return BundleHelper.getListSize(getString(-1, useDefaultIfEmpty)); | |
210 | } | |
211 | ||
76b51de9 NR |
212 | /** |
213 | * This item is only used as a group, not as an option. | |
214 | * <p> | |
215 | * For instance, you could have LANGUAGE_CODE as a group for which you won't | |
216 | * use the value in the program, and LANGUAGE_CODE_FR, LANGUAGE_CODE_EN | |
217 | * inside for which the value must be set. | |
218 | * | |
219 | * @return TRUE if it is a group | |
220 | */ | |
221 | public boolean isGroup() { | |
222 | return meta.group(); | |
223 | } | |
224 | ||
9e834013 NR |
225 | /** |
226 | * The value stored by this item, as a {@link String}. | |
227 | * | |
d5026c09 NR |
228 | * @param item |
229 | * the item number to get for an array of values, or -1 to get | |
230 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
231 | * is FALSE) | |
d18e136e NR |
232 | * @param useDefaultIfEmpty |
233 | * use the default value instead of NULL if the setting is not | |
234 | * set | |
235 | * | |
9e834013 NR |
236 | * @return the value |
237 | */ | |
d5026c09 NR |
238 | public String getString(int item, boolean useDefaultIfEmpty) { |
239 | if (isArray() && item >= 0) { | |
240 | List<String> values = BundleHelper.parseList(value, -1); | |
241 | if (values != null && item < values.size()) { | |
242 | return values.get(item); | |
243 | } | |
244 | ||
245 | if (useDefaultIfEmpty) { | |
246 | return getDefaultString(item); | |
247 | } | |
248 | ||
249 | return null; | |
250 | } | |
251 | ||
d18e136e | 252 | if (value == null && useDefaultIfEmpty) { |
d5026c09 | 253 | return getDefaultString(item); |
d18e136e NR |
254 | } |
255 | ||
9e834013 NR |
256 | return value; |
257 | } | |
258 | ||
76b51de9 NR |
259 | /** |
260 | * The default value of this item, as a {@link String}. | |
261 | * | |
d5026c09 NR |
262 | * @param item |
263 | * the item number to get for an array of values, or -1 to get | |
264 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
265 | * is FALSE) | |
266 | * | |
76b51de9 NR |
267 | * @return the default value |
268 | */ | |
d5026c09 NR |
269 | public String getDefaultString(int item) { |
270 | if (isArray() && item >= 0) { | |
271 | List<String> values = BundleHelper.parseList(meta.def(), item); | |
272 | if (values != null && item < values.size()) { | |
273 | return values.get(item); | |
274 | } | |
275 | ||
276 | return null; | |
277 | } | |
278 | ||
9e834013 NR |
279 | return meta.def(); |
280 | } | |
281 | ||
76b51de9 NR |
282 | /** |
283 | * The value stored by this item, as a {@link Boolean}. | |
284 | * | |
d5026c09 NR |
285 | * @param item |
286 | * the item number to get for an array of values, or -1 to get | |
287 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
288 | * is FALSE) | |
d18e136e NR |
289 | * @param useDefaultIfEmpty |
290 | * use the default value instead of NULL if the setting is not | |
291 | * set | |
292 | * | |
76b51de9 NR |
293 | * @return the value |
294 | */ | |
d5026c09 NR |
295 | public Boolean getBoolean(int item, boolean useDefaultIfEmpty) { |
296 | return BundleHelper | |
297 | .parseBoolean(getString(item, useDefaultIfEmpty), -1); | |
9e834013 NR |
298 | } |
299 | ||
76b51de9 NR |
300 | /** |
301 | * The default value of this item, as a {@link Boolean}. | |
302 | * | |
d5026c09 NR |
303 | * @param item |
304 | * the item number to get for an array of values, or -1 to get | |
305 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
306 | * is FALSE) | |
307 | * | |
76b51de9 NR |
308 | * @return the default value |
309 | */ | |
d5026c09 NR |
310 | public Boolean getDefaultBoolean(int item) { |
311 | return BundleHelper.parseBoolean(getDefaultString(item), -1); | |
9e834013 NR |
312 | } |
313 | ||
76b51de9 NR |
314 | /** |
315 | * The value stored by this item, as a {@link Character}. | |
316 | * | |
d5026c09 NR |
317 | * @param item |
318 | * the item number to get for an array of values, or -1 to get | |
319 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
320 | * is FALSE) | |
d18e136e NR |
321 | * @param useDefaultIfEmpty |
322 | * use the default value instead of NULL if the setting is not | |
323 | * set | |
324 | * | |
76b51de9 NR |
325 | * @return the value |
326 | */ | |
d5026c09 NR |
327 | public Character getCharacter(int item, boolean useDefaultIfEmpty) { |
328 | return BundleHelper.parseCharacter(getString(item, useDefaultIfEmpty), | |
329 | -1); | |
9e834013 NR |
330 | } |
331 | ||
76b51de9 NR |
332 | /** |
333 | * The default value of this item, as a {@link Character}. | |
334 | * | |
d5026c09 NR |
335 | * @param item |
336 | * the item number to get for an array of values, or -1 to get | |
337 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
338 | * is FALSE) | |
339 | * | |
76b51de9 NR |
340 | * @return the default value |
341 | */ | |
d5026c09 NR |
342 | public Character getDefaultCharacter(int item) { |
343 | return BundleHelper.parseCharacter(getDefaultString(item), -1); | |
9e834013 NR |
344 | } |
345 | ||
76b51de9 NR |
346 | /** |
347 | * The value stored by this item, as an {@link Integer}. | |
348 | * | |
d5026c09 NR |
349 | * @param item |
350 | * the item number to get for an array of values, or -1 to get | |
351 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
352 | * is FALSE) | |
d18e136e NR |
353 | * @param useDefaultIfEmpty |
354 | * use the default value instead of NULL if the setting is not | |
355 | * set | |
356 | * | |
76b51de9 NR |
357 | * @return the value |
358 | */ | |
d5026c09 NR |
359 | public Integer getInteger(int item, boolean useDefaultIfEmpty) { |
360 | return BundleHelper | |
361 | .parseInteger(getString(item, useDefaultIfEmpty), -1); | |
9e834013 NR |
362 | } |
363 | ||
76b51de9 NR |
364 | /** |
365 | * The default value of this item, as an {@link Integer}. | |
366 | * | |
d5026c09 NR |
367 | * @param item |
368 | * the item number to get for an array of values, or -1 to get | |
369 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
370 | * is FALSE) | |
371 | * | |
76b51de9 NR |
372 | * @return the default value |
373 | */ | |
d5026c09 NR |
374 | public Integer getDefaultInteger(int item) { |
375 | return BundleHelper.parseInteger(getDefaultString(item), -1); | |
9e834013 NR |
376 | } |
377 | ||
76b51de9 NR |
378 | /** |
379 | * The value stored by this item, as a colour (represented here as an | |
380 | * {@link Integer}) if it represents a colour, or NULL if it doesn't. | |
381 | * <p> | |
382 | * The returned colour value is an ARGB value. | |
383 | * | |
d5026c09 NR |
384 | * @param item |
385 | * the item number to get for an array of values, or -1 to get | |
386 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
387 | * is FALSE) | |
d18e136e NR |
388 | * @param useDefaultIfEmpty |
389 | * use the default value instead of NULL if the setting is not | |
390 | * set | |
391 | * | |
76b51de9 NR |
392 | * @return the value |
393 | */ | |
d5026c09 NR |
394 | public Integer getColor(int item, boolean useDefaultIfEmpty) { |
395 | return BundleHelper.parseColor(getString(item, useDefaultIfEmpty), -1); | |
9e834013 NR |
396 | } |
397 | ||
76b51de9 NR |
398 | /** |
399 | * The default value stored by this item, as a colour (represented here as | |
400 | * an {@link Integer}) if it represents a colour, or NULL if it doesn't. | |
401 | * <p> | |
402 | * The returned colour value is an ARGB value. | |
403 | * | |
d5026c09 NR |
404 | * @param item |
405 | * the item number to get for an array of values, or -1 to get | |
406 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
407 | * is FALSE) | |
408 | * | |
76b51de9 NR |
409 | * @return the value |
410 | */ | |
d5026c09 NR |
411 | public Integer getDefaultColor(int item) { |
412 | return BundleHelper.parseColor(getDefaultString(item), -1); | |
9e834013 NR |
413 | } |
414 | ||
76b51de9 NR |
415 | /** |
416 | * A {@link String} representation of the list of values. | |
417 | * <p> | |
418 | * The list of values is comma-separated and each value is surrounded by | |
419 | * double-quotes; backslashes and double-quotes are escaped by a backslash. | |
420 | * | |
d5026c09 NR |
421 | * @param item |
422 | * the item number to get for an array of values, or -1 to get | |
423 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
424 | * is FALSE) | |
d18e136e NR |
425 | * @param useDefaultIfEmpty |
426 | * use the default value instead of NULL if the setting is not | |
427 | * set | |
428 | * | |
76b51de9 NR |
429 | * @return the value |
430 | */ | |
d5026c09 NR |
431 | public List<String> getList(int item, boolean useDefaultIfEmpty) { |
432 | return BundleHelper.parseList(getString(item, useDefaultIfEmpty), -1); | |
9e834013 NR |
433 | } |
434 | ||
76b51de9 NR |
435 | /** |
436 | * A {@link String} representation of the default list of values. | |
437 | * <p> | |
438 | * The list of values is comma-separated and each value is surrounded by | |
439 | * double-quotes; backslashes and double-quotes are escaped by a backslash. | |
440 | * | |
d5026c09 NR |
441 | * @param item |
442 | * the item number to get for an array of values, or -1 to get | |
443 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
444 | * is FALSE) | |
445 | * | |
76b51de9 NR |
446 | * @return the value |
447 | */ | |
d5026c09 NR |
448 | public List<String> getDefaultList(int item) { |
449 | return BundleHelper.parseList(getDefaultString(item), -1); | |
9e834013 NR |
450 | } |
451 | ||
452 | /** | |
453 | * The value stored by this item, as a {@link String}. | |
454 | * | |
455 | * @param value | |
456 | * the new value | |
d5026c09 NR |
457 | * @param item |
458 | * the item number to set for an array of values, or -1 to set | |
459 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
460 | * is FALSE) | |
9e834013 | 461 | */ |
d5026c09 NR |
462 | public void setString(String value, int item) { |
463 | if (isArray() && item >= 0) { | |
464 | List<String> values = BundleHelper.parseList(this.value, -1); | |
465 | for (int i = values.size(); i <= item; i++) { | |
466 | values.add(null); | |
467 | } | |
468 | values.set(item, value); | |
469 | this.value = BundleHelper.fromList(values); | |
470 | } else { | |
471 | this.value = value; | |
472 | } | |
9e834013 NR |
473 | } |
474 | ||
76b51de9 NR |
475 | /** |
476 | * The value stored by this item, as a {@link Boolean}. | |
477 | * | |
478 | * @param value | |
479 | * the new value | |
d5026c09 NR |
480 | * @param item |
481 | * the item number to set for an array of values, or -1 to set | |
482 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
483 | * is FALSE) | |
76b51de9 | 484 | */ |
d5026c09 NR |
485 | public void setBoolean(boolean value, int item) { |
486 | setString(BundleHelper.fromBoolean(value), item); | |
9e834013 NR |
487 | } |
488 | ||
76b51de9 NR |
489 | /** |
490 | * The value stored by this item, as a {@link Character}. | |
491 | * | |
492 | * @param value | |
493 | * the new value | |
d5026c09 NR |
494 | * @param item |
495 | * the item number to set for an array of values, or -1 to set | |
496 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
497 | * is FALSE) | |
76b51de9 | 498 | */ |
d5026c09 NR |
499 | public void setCharacter(char value, int item) { |
500 | setString(BundleHelper.fromCharacter(value), item); | |
9e834013 NR |
501 | } |
502 | ||
76b51de9 NR |
503 | /** |
504 | * The value stored by this item, as an {@link Integer}. | |
505 | * | |
506 | * @param value | |
507 | * the new value | |
d5026c09 NR |
508 | * @param item |
509 | * the item number to set for an array of values, or -1 to set | |
510 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
511 | * is FALSE) | |
76b51de9 | 512 | */ |
d5026c09 NR |
513 | public void setInteger(int value, int item) { |
514 | setString(BundleHelper.fromInteger(value), item); | |
9e834013 NR |
515 | } |
516 | ||
76b51de9 NR |
517 | /** |
518 | * The value stored by this item, as a colour (represented here as an | |
519 | * {@link Integer}) if it represents a colour, or NULL if it doesn't. | |
520 | * <p> | |
74d2a495 | 521 | * The colour value is an ARGB value. |
76b51de9 NR |
522 | * |
523 | * @param value | |
524 | * the value | |
d5026c09 NR |
525 | * @param item |
526 | * the item number to set for an array of values, or -1 to set | |
527 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
528 | * is FALSE) | |
76b51de9 | 529 | */ |
d5026c09 NR |
530 | public void setColor(int value, int item) { |
531 | setString(BundleHelper.fromColor(value), item); | |
9e834013 NR |
532 | } |
533 | ||
76b51de9 NR |
534 | /** |
535 | * A {@link String} representation of the default list of values. | |
536 | * <p> | |
537 | * The list of values is comma-separated and each value is surrounded by | |
538 | * double-quotes; backslashes and double-quotes are escaped by a backslash. | |
539 | * | |
540 | * @param value | |
541 | * the {@link String} representation | |
d5026c09 NR |
542 | * @param item |
543 | * the item number to set for an array of values, or -1 to set | |
544 | * the whole value (has no effect if {@link MetaInfo#isArray()} | |
545 | * is FALSE) | |
76b51de9 | 546 | */ |
d5026c09 NR |
547 | public void setList(List<String> value, int item) { |
548 | setString(BundleHelper.fromList(value), item); | |
9e834013 NR |
549 | } |
550 | ||
551 | /** | |
76b51de9 NR |
552 | * Reload the value from the {@link Bundle}, so the last value that was |
553 | * saved will be used. | |
9e834013 NR |
554 | */ |
555 | public void reload() { | |
fde375c1 NR |
556 | if (bundle.isSet(id, false)) { |
557 | value = bundle.getString(id); | |
558 | } else { | |
559 | value = null; | |
560 | } | |
561 | ||
8517b60c | 562 | for (Runnable listener : reloadedListeners) { |
9e834013 NR |
563 | try { |
564 | listener.run(); | |
565 | } catch (Exception e) { | |
9e834013 NR |
566 | e.printStackTrace(); |
567 | } | |
568 | } | |
569 | } | |
570 | ||
76b51de9 NR |
571 | /** |
572 | * Add a listener that will be called <b>after</b> a reload operation. | |
573 | * <p> | |
574 | * You could use it to refresh the UI for instance. | |
575 | * | |
576 | * @param listener | |
577 | * the listener | |
578 | */ | |
8517b60c NR |
579 | public void addReloadedListener(Runnable listener) { |
580 | reloadedListeners.add(listener); | |
9e834013 NR |
581 | } |
582 | ||
583 | /** | |
584 | * Save the current value to the {@link Bundle}. | |
d5026c09 NR |
585 | * <p> |
586 | * Note that listeners will be called <b>before</b> the dirty check and | |
587 | * <b>before</b> saving the value. | |
588 | * | |
589 | * @param onlyIfDirty | |
590 | * only save the data if the dirty flag is set (will reset the | |
591 | * dirty flag) | |
9e834013 | 592 | */ |
d5026c09 | 593 | public void save(boolean onlyIfDirty) { |
8517b60c NR |
594 | for (Runnable listener : saveListeners) { |
595 | try { | |
596 | listener.run(); | |
597 | } catch (Exception e) { | |
8517b60c NR |
598 | e.printStackTrace(); |
599 | } | |
600 | } | |
d5026c09 NR |
601 | |
602 | if (!onlyIfDirty || isDirty()) { | |
603 | bundle.setString(id, value); | |
604 | } | |
9e834013 NR |
605 | } |
606 | ||
76b51de9 NR |
607 | /** |
608 | * Add a listener that will be called <b>before</b> a save operation. | |
609 | * <p> | |
610 | * You could use it to make some modification to the stored value before it | |
611 | * is saved. | |
612 | * | |
613 | * @param listener | |
614 | * the listener | |
615 | */ | |
8517b60c NR |
616 | public void addSaveListener(Runnable listener) { |
617 | saveListeners.add(listener); | |
618 | } | |
619 | ||
76b51de9 NR |
620 | /** |
621 | * The sub-items if any (if no sub-items, will return an empty list). | |
622 | * <p> | |
623 | * Sub-items are declared when a {@link Meta} has an ID that starts with the | |
624 | * ID of a {@link Meta#group()} {@link MetaInfo}. | |
625 | * <p> | |
626 | * For instance: | |
627 | * <ul> | |
628 | * <li>{@link Meta} <tt>MY_PREFIX</tt> is a {@link Meta#group()}</li> | |
629 | * <li>{@link Meta} <tt>MY_PREFIX_DESCRIPTION</tt> is another {@link Meta}</li> | |
630 | * <li><tt>MY_PREFIX_DESCRIPTION</tt> will be a child of <tt>MY_PREFIX</tt></li> | |
631 | * </ul> | |
632 | * | |
633 | * @return the sub-items if any | |
634 | */ | |
635 | public List<MetaInfo<E>> getChildren() { | |
636 | return children; | |
637 | } | |
638 | ||
0877d6f5 NR |
639 | @Override |
640 | public Iterator<MetaInfo<E>> iterator() { | |
641 | return children.iterator(); | |
642 | } | |
643 | ||
9e834013 NR |
644 | /** |
645 | * Create a list of {@link MetaInfo}, one for each of the item in the given | |
646 | * {@link Bundle}. | |
647 | * | |
648 | * @param <E> | |
649 | * the type of {@link Bundle} to edit | |
650 | * @param type | |
651 | * a class instance of the item type to work on | |
652 | * @param bundle | |
653 | * the {@link Bundle} to sort through | |
654 | * | |
655 | * @return the list | |
656 | */ | |
657 | static public <E extends Enum<E>> List<MetaInfo<E>> getItems(Class<E> type, | |
658 | Bundle<E> bundle) { | |
659 | List<MetaInfo<E>> list = new ArrayList<MetaInfo<E>>(); | |
76b51de9 | 660 | List<MetaInfo<E>> shadow = new ArrayList<MetaInfo<E>>(); |
9e834013 | 661 | for (E id : type.getEnumConstants()) { |
76b51de9 NR |
662 | MetaInfo<E> info = new MetaInfo<E>(type, bundle, id); |
663 | list.add(info); | |
664 | shadow.add(info); | |
9e834013 NR |
665 | } |
666 | ||
76b51de9 NR |
667 | for (int i = 0; i < list.size(); i++) { |
668 | MetaInfo<E> info = list.get(i); | |
8517b60c | 669 | |
76b51de9 NR |
670 | MetaInfo<E> parent = findParent(info, shadow); |
671 | if (parent != null) { | |
672 | list.remove(i--); | |
673 | parent.children.add(info); | |
d18e136e | 674 | info.name = idToName(info.id, parent.id); |
8517b60c NR |
675 | } |
676 | } | |
677 | ||
76b51de9 | 678 | return list; |
8517b60c NR |
679 | } |
680 | ||
76b51de9 NR |
681 | /** |
682 | * Find the longest parent of the given {@link MetaInfo}, which means: | |
683 | * <ul> | |
684 | * <li>the parent is a {@link Meta#group()}</li> | |
685 | * <li>the parent Id is a substring of the Id of the given {@link MetaInfo}</li> | |
686 | * <li>there is no other parent sharing a substring for this | |
687 | * {@link MetaInfo} with a longer Id</li> | |
688 | * </ul> | |
689 | * | |
690 | * @param <E> | |
691 | * the kind of enum | |
692 | * @param info | |
693 | * the info to look for a parent for | |
694 | * @param candidates | |
695 | * the list of potential parents | |
696 | * | |
697 | * @return the longest parent or NULL if no parent is found | |
698 | */ | |
8517b60c | 699 | static private <E extends Enum<E>> MetaInfo<E> findParent(MetaInfo<E> info, |
76b51de9 NR |
700 | List<MetaInfo<E>> candidates) { |
701 | String id = info.id.toString(); | |
8517b60c NR |
702 | MetaInfo<E> group = null; |
703 | for (MetaInfo<E> pcandidate : candidates) { | |
76b51de9 NR |
704 | if (pcandidate.isGroup()) { |
705 | String candidateId = pcandidate.id.toString(); | |
706 | if (!id.equals(candidateId) && id.startsWith(candidateId)) { | |
707 | if (group == null | |
708 | || group.id.toString().length() < candidateId | |
709 | .length()) { | |
710 | group = pcandidate; | |
711 | } | |
8517b60c NR |
712 | } |
713 | } | |
714 | } | |
715 | ||
716 | return group; | |
717 | } | |
d18e136e NR |
718 | |
719 | static private <E extends Enum<E>> String idToName(E id, E prefix) { | |
720 | String name = id.toString(); | |
721 | if (prefix != null && name.startsWith(prefix.toString())) { | |
722 | name = name.substring(prefix.toString().length()); | |
723 | } | |
724 | ||
725 | if (name.length() > 0) { | |
726 | name = name.substring(0, 1).toUpperCase() | |
727 | + name.substring(1).toLowerCase(); | |
728 | } | |
729 | ||
730 | name = name.replace("_", " "); | |
731 | ||
732 | return name.trim(); | |
733 | } | |
9e834013 | 734 | } |