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