Merge commit '77d3a60869e7a780c6ae069e51530e1eacece5e2'
[fanfix.git] / src / be / nikiroo / utils / resources / MetaInfo.java
CommitLineData
9e834013
NR
1package be.nikiroo.utils.resources;
2
3import java.util.ArrayList;
0877d6f5 4import java.util.Iterator;
9e834013
NR
5import java.util.List;
6
7import 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 18public 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}
0f7de31e 144 * items. Also works for {@link Format#LOCALE}.
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
0f7de31e
NR
162 /**
163 * Return all the languages known by the program for this bundle.
164 * <p>
165 * This only works for {@link TransBundle}, and will return an empty list if
166 * this is not a {@link TransBundle}.
167 *
168 * @return the known language codes
169 */
170 public List<String> getKnownLanguages() {
171 if (bundle instanceof TransBundle) {
172 return ((TransBundle<E>) bundle).getKnownLanguages();
173 }
174
175 return new ArrayList<String>();
176 }
177
76b51de9
NR
178 /**
179 * This item is a comma-separated list of values instead of a single value.
180 * <p>
181 * The list items are separated by a comma, each surrounded by
182 * double-quotes, with backslashes and double-quotes escaped by a backslash.
183 * <p>
184 * Example: <tt>"un", "deux"</tt>
185 *
186 * @return TRUE if it is
187 */
0877d6f5
NR
188 public boolean isArray() {
189 return meta.array();
190 }
191
d5026c09 192 /**
74d2a495
NR
193 * A manual flag to specify if the data has been changed or not, which can
194 * be used by {@link MetaInfo#save(boolean)}.
d5026c09
NR
195 *
196 * @return TRUE if it is dirty (if it has changed)
197 */
198 public boolean isDirty() {
199 return dirty;
200 }
201
202 /**
74d2a495
NR
203 * A manual flag to specify that the data has been changed, which can be
204 * used by {@link MetaInfo#save(boolean)}.
d5026c09
NR
205 */
206 public void setDirty() {
207 this.dirty = true;
208 }
209
210 /**
211 * The number of items in this item if it {@link MetaInfo#isArray()}, or -1
212 * if not.
213 *
214 * @param useDefaultIfEmpty
215 * check the size of the default list instead if the list is
216 * empty
217 *
218 * @return -1 or the number of items
219 */
220 public int getListSize(boolean useDefaultIfEmpty) {
221 if (!isArray()) {
222 return -1;
223 }
224
225 return BundleHelper.getListSize(getString(-1, useDefaultIfEmpty));
226 }
227
76b51de9
NR
228 /**
229 * This item is only used as a group, not as an option.
230 * <p>
231 * For instance, you could have LANGUAGE_CODE as a group for which you won't
232 * use the value in the program, and LANGUAGE_CODE_FR, LANGUAGE_CODE_EN
233 * inside for which the value must be set.
234 *
235 * @return TRUE if it is a group
236 */
237 public boolean isGroup() {
238 return meta.group();
239 }
240
9e834013
NR
241 /**
242 * The value stored by this item, as a {@link String}.
243 *
d5026c09
NR
244 * @param item
245 * the item number to get for an array of values, or -1 to get
246 * the whole value (has no effect if {@link MetaInfo#isArray()}
247 * is FALSE)
d18e136e
NR
248 * @param useDefaultIfEmpty
249 * use the default value instead of NULL if the setting is not
250 * set
251 *
9e834013
NR
252 * @return the value
253 */
d5026c09
NR
254 public String getString(int item, boolean useDefaultIfEmpty) {
255 if (isArray() && item >= 0) {
256 List<String> values = BundleHelper.parseList(value, -1);
257 if (values != null && item < values.size()) {
258 return values.get(item);
259 }
260
261 if (useDefaultIfEmpty) {
262 return getDefaultString(item);
263 }
264
265 return null;
266 }
267
d18e136e 268 if (value == null && useDefaultIfEmpty) {
d5026c09 269 return getDefaultString(item);
d18e136e
NR
270 }
271
9e834013
NR
272 return value;
273 }
274
76b51de9
NR
275 /**
276 * The default value of this item, as a {@link String}.
277 *
d5026c09
NR
278 * @param item
279 * the item number to get for an array of values, or -1 to get
280 * the whole value (has no effect if {@link MetaInfo#isArray()}
281 * is FALSE)
282 *
76b51de9
NR
283 * @return the default value
284 */
d5026c09
NR
285 public String getDefaultString(int item) {
286 if (isArray() && item >= 0) {
287 List<String> values = BundleHelper.parseList(meta.def(), item);
288 if (values != null && item < values.size()) {
289 return values.get(item);
290 }
291
292 return null;
293 }
294
9e834013
NR
295 return meta.def();
296 }
297
76b51de9
NR
298 /**
299 * The value stored by this item, as a {@link Boolean}.
300 *
d5026c09
NR
301 * @param item
302 * the item number to get for an array of values, or -1 to get
303 * the whole value (has no effect if {@link MetaInfo#isArray()}
304 * is FALSE)
d18e136e
NR
305 * @param useDefaultIfEmpty
306 * use the default value instead of NULL if the setting is not
307 * set
308 *
76b51de9
NR
309 * @return the value
310 */
d5026c09
NR
311 public Boolean getBoolean(int item, boolean useDefaultIfEmpty) {
312 return BundleHelper
313 .parseBoolean(getString(item, useDefaultIfEmpty), -1);
9e834013
NR
314 }
315
76b51de9
NR
316 /**
317 * The default value of this item, as a {@link Boolean}.
318 *
d5026c09
NR
319 * @param item
320 * the item number to get for an array of values, or -1 to get
321 * the whole value (has no effect if {@link MetaInfo#isArray()}
322 * is FALSE)
323 *
76b51de9
NR
324 * @return the default value
325 */
d5026c09
NR
326 public Boolean getDefaultBoolean(int item) {
327 return BundleHelper.parseBoolean(getDefaultString(item), -1);
9e834013
NR
328 }
329
76b51de9
NR
330 /**
331 * The value stored by this item, as a {@link Character}.
332 *
d5026c09
NR
333 * @param item
334 * the item number to get for an array of values, or -1 to get
335 * the whole value (has no effect if {@link MetaInfo#isArray()}
336 * is FALSE)
d18e136e
NR
337 * @param useDefaultIfEmpty
338 * use the default value instead of NULL if the setting is not
339 * set
340 *
76b51de9
NR
341 * @return the value
342 */
d5026c09
NR
343 public Character getCharacter(int item, boolean useDefaultIfEmpty) {
344 return BundleHelper.parseCharacter(getString(item, useDefaultIfEmpty),
345 -1);
9e834013
NR
346 }
347
76b51de9
NR
348 /**
349 * The default value of this item, as a {@link Character}.
350 *
d5026c09
NR
351 * @param item
352 * the item number to get for an array of values, or -1 to get
353 * the whole value (has no effect if {@link MetaInfo#isArray()}
354 * is FALSE)
355 *
76b51de9
NR
356 * @return the default value
357 */
d5026c09
NR
358 public Character getDefaultCharacter(int item) {
359 return BundleHelper.parseCharacter(getDefaultString(item), -1);
9e834013
NR
360 }
361
76b51de9
NR
362 /**
363 * The value stored by this item, as an {@link Integer}.
364 *
d5026c09
NR
365 * @param item
366 * the item number to get for an array of values, or -1 to get
367 * the whole value (has no effect if {@link MetaInfo#isArray()}
368 * is FALSE)
d18e136e
NR
369 * @param useDefaultIfEmpty
370 * use the default value instead of NULL if the setting is not
371 * set
372 *
76b51de9
NR
373 * @return the value
374 */
d5026c09
NR
375 public Integer getInteger(int item, boolean useDefaultIfEmpty) {
376 return BundleHelper
377 .parseInteger(getString(item, useDefaultIfEmpty), -1);
9e834013
NR
378 }
379
76b51de9
NR
380 /**
381 * The default value of this item, as an {@link Integer}.
382 *
d5026c09
NR
383 * @param item
384 * the item number to get for an array of values, or -1 to get
385 * the whole value (has no effect if {@link MetaInfo#isArray()}
386 * is FALSE)
387 *
76b51de9
NR
388 * @return the default value
389 */
d5026c09
NR
390 public Integer getDefaultInteger(int item) {
391 return BundleHelper.parseInteger(getDefaultString(item), -1);
9e834013
NR
392 }
393
76b51de9
NR
394 /**
395 * The value stored by this item, as a colour (represented here as an
396 * {@link Integer}) if it represents a colour, or NULL if it doesn't.
397 * <p>
398 * The returned colour value is an ARGB value.
399 *
d5026c09
NR
400 * @param item
401 * the item number to get for an array of values, or -1 to get
402 * the whole value (has no effect if {@link MetaInfo#isArray()}
403 * is FALSE)
d18e136e
NR
404 * @param useDefaultIfEmpty
405 * use the default value instead of NULL if the setting is not
406 * set
407 *
76b51de9
NR
408 * @return the value
409 */
d5026c09
NR
410 public Integer getColor(int item, boolean useDefaultIfEmpty) {
411 return BundleHelper.parseColor(getString(item, useDefaultIfEmpty), -1);
9e834013
NR
412 }
413
76b51de9
NR
414 /**
415 * The default value stored by this item, as a colour (represented here as
416 * an {@link Integer}) if it represents a colour, or NULL if it doesn't.
417 * <p>
418 * The returned colour value is an ARGB value.
419 *
d5026c09
NR
420 * @param item
421 * the item number to get for an array of values, or -1 to get
422 * the whole value (has no effect if {@link MetaInfo#isArray()}
423 * is FALSE)
424 *
76b51de9
NR
425 * @return the value
426 */
d5026c09
NR
427 public Integer getDefaultColor(int item) {
428 return BundleHelper.parseColor(getDefaultString(item), -1);
9e834013
NR
429 }
430
76b51de9
NR
431 /**
432 * A {@link String} representation of the list of values.
433 * <p>
434 * The list of values is comma-separated and each value is surrounded by
435 * double-quotes; backslashes and double-quotes are escaped by a backslash.
436 *
d5026c09
NR
437 * @param item
438 * the item number to get for an array of values, or -1 to get
439 * the whole value (has no effect if {@link MetaInfo#isArray()}
440 * is FALSE)
d18e136e
NR
441 * @param useDefaultIfEmpty
442 * use the default value instead of NULL if the setting is not
443 * set
444 *
76b51de9
NR
445 * @return the value
446 */
d5026c09
NR
447 public List<String> getList(int item, boolean useDefaultIfEmpty) {
448 return BundleHelper.parseList(getString(item, useDefaultIfEmpty), -1);
9e834013
NR
449 }
450
76b51de9
NR
451 /**
452 * A {@link String} representation of the default list of values.
453 * <p>
454 * The list of values is comma-separated and each value is surrounded by
455 * double-quotes; backslashes and double-quotes are escaped by a backslash.
456 *
d5026c09
NR
457 * @param item
458 * the item number to get for an array of values, or -1 to get
459 * the whole value (has no effect if {@link MetaInfo#isArray()}
460 * is FALSE)
461 *
76b51de9
NR
462 * @return the value
463 */
d5026c09
NR
464 public List<String> getDefaultList(int item) {
465 return BundleHelper.parseList(getDefaultString(item), -1);
9e834013
NR
466 }
467
468 /**
469 * The value stored by this item, as a {@link String}.
470 *
471 * @param value
472 * the new value
d5026c09
NR
473 * @param item
474 * the item number to set for an array of values, or -1 to set
475 * the whole value (has no effect if {@link MetaInfo#isArray()}
476 * is FALSE)
9e834013 477 */
d5026c09
NR
478 public void setString(String value, int item) {
479 if (isArray() && item >= 0) {
856f5898 480 this.value = BundleHelper.fromList(this.value, value, item);
d5026c09
NR
481 } else {
482 this.value = value;
483 }
9e834013
NR
484 }
485
76b51de9
NR
486 /**
487 * The value stored by this item, as a {@link Boolean}.
488 *
489 * @param value
490 * the new value
d5026c09
NR
491 * @param item
492 * the item number to set for an array of values, or -1 to set
493 * the whole value (has no effect if {@link MetaInfo#isArray()}
494 * is FALSE)
76b51de9 495 */
d5026c09
NR
496 public void setBoolean(boolean value, int item) {
497 setString(BundleHelper.fromBoolean(value), item);
9e834013
NR
498 }
499
76b51de9
NR
500 /**
501 * The value stored by this item, as a {@link Character}.
502 *
503 * @param value
504 * the new value
d5026c09
NR
505 * @param item
506 * the item number to set for an array of values, or -1 to set
507 * the whole value (has no effect if {@link MetaInfo#isArray()}
508 * is FALSE)
76b51de9 509 */
d5026c09
NR
510 public void setCharacter(char value, int item) {
511 setString(BundleHelper.fromCharacter(value), item);
9e834013
NR
512 }
513
76b51de9
NR
514 /**
515 * The value stored by this item, as an {@link Integer}.
516 *
517 * @param value
518 * the new value
d5026c09
NR
519 * @param item
520 * the item number to set for an array of values, or -1 to set
521 * the whole value (has no effect if {@link MetaInfo#isArray()}
522 * is FALSE)
76b51de9 523 */
d5026c09
NR
524 public void setInteger(int value, int item) {
525 setString(BundleHelper.fromInteger(value), item);
9e834013
NR
526 }
527
76b51de9
NR
528 /**
529 * The value stored by this item, as a colour (represented here as an
530 * {@link Integer}) if it represents a colour, or NULL if it doesn't.
531 * <p>
74d2a495 532 * The colour value is an ARGB value.
76b51de9
NR
533 *
534 * @param value
535 * the value
d5026c09
NR
536 * @param item
537 * the item number to set for an array of values, or -1 to set
538 * the whole value (has no effect if {@link MetaInfo#isArray()}
539 * is FALSE)
76b51de9 540 */
d5026c09
NR
541 public void setColor(int value, int item) {
542 setString(BundleHelper.fromColor(value), item);
9e834013
NR
543 }
544
76b51de9
NR
545 /**
546 * A {@link String} representation of the default list of values.
547 * <p>
548 * The list of values is comma-separated and each value is surrounded by
549 * double-quotes; backslashes and double-quotes are escaped by a backslash.
550 *
551 * @param value
552 * the {@link String} representation
d5026c09
NR
553 * @param item
554 * the item number to set for an array of values, or -1 to set
555 * the whole value (has no effect if {@link MetaInfo#isArray()}
556 * is FALSE)
76b51de9 557 */
d5026c09
NR
558 public void setList(List<String> value, int item) {
559 setString(BundleHelper.fromList(value), item);
9e834013
NR
560 }
561
562 /**
76b51de9
NR
563 * Reload the value from the {@link Bundle}, so the last value that was
564 * saved will be used.
9e834013
NR
565 */
566 public void reload() {
fde375c1
NR
567 if (bundle.isSet(id, false)) {
568 value = bundle.getString(id);
569 } else {
570 value = null;
571 }
572
856f5898
NR
573 // Copy the list so we can create new listener in a listener
574 for (Runnable listener : new ArrayList<Runnable>(reloadedListeners)) {
9e834013
NR
575 try {
576 listener.run();
577 } catch (Exception e) {
9e834013
NR
578 e.printStackTrace();
579 }
580 }
581 }
582
76b51de9
NR
583 /**
584 * Add a listener that will be called <b>after</b> a reload operation.
585 * <p>
586 * You could use it to refresh the UI for instance.
587 *
588 * @param listener
589 * the listener
590 */
8517b60c
NR
591 public void addReloadedListener(Runnable listener) {
592 reloadedListeners.add(listener);
9e834013
NR
593 }
594
595 /**
596 * Save the current value to the {@link Bundle}.
d5026c09
NR
597 * <p>
598 * Note that listeners will be called <b>before</b> the dirty check and
599 * <b>before</b> saving the value.
600 *
601 * @param onlyIfDirty
602 * only save the data if the dirty flag is set (will reset the
603 * dirty flag)
9e834013 604 */
d5026c09 605 public void save(boolean onlyIfDirty) {
856f5898
NR
606 // Copy the list so we can create new listener in a listener
607 for (Runnable listener : new ArrayList<Runnable>(saveListeners)) {
8517b60c
NR
608 try {
609 listener.run();
610 } catch (Exception e) {
8517b60c
NR
611 e.printStackTrace();
612 }
613 }
d5026c09
NR
614
615 if (!onlyIfDirty || isDirty()) {
616 bundle.setString(id, value);
617 }
9e834013
NR
618 }
619
76b51de9
NR
620 /**
621 * Add a listener that will be called <b>before</b> a save operation.
622 * <p>
623 * You could use it to make some modification to the stored value before it
624 * is saved.
625 *
626 * @param listener
627 * the listener
628 */
8517b60c
NR
629 public void addSaveListener(Runnable listener) {
630 saveListeners.add(listener);
631 }
632
76b51de9
NR
633 /**
634 * The sub-items if any (if no sub-items, will return an empty list).
635 * <p>
636 * Sub-items are declared when a {@link Meta} has an ID that starts with the
637 * ID of a {@link Meta#group()} {@link MetaInfo}.
638 * <p>
639 * For instance:
640 * <ul>
641 * <li>{@link Meta} <tt>MY_PREFIX</tt> is a {@link Meta#group()}</li>
642 * <li>{@link Meta} <tt>MY_PREFIX_DESCRIPTION</tt> is another {@link Meta}</li>
643 * <li><tt>MY_PREFIX_DESCRIPTION</tt> will be a child of <tt>MY_PREFIX</tt></li>
644 * </ul>
645 *
646 * @return the sub-items if any
647 */
648 public List<MetaInfo<E>> getChildren() {
649 return children;
650 }
651
4840f1a4
NR
652 /**
653 * The number of sub-items, if any.
654 *
655 * @return the number or 0
656 */
657 public int size() {
658 return children.size();
659 }
660
0877d6f5
NR
661 @Override
662 public Iterator<MetaInfo<E>> iterator() {
663 return children.iterator();
664 }
665
9e834013
NR
666 /**
667 * Create a list of {@link MetaInfo}, one for each of the item in the given
668 * {@link Bundle}.
669 *
670 * @param <E>
671 * the type of {@link Bundle} to edit
672 * @param type
673 * a class instance of the item type to work on
674 * @param bundle
675 * the {@link Bundle} to sort through
676 *
677 * @return the list
678 */
679 static public <E extends Enum<E>> List<MetaInfo<E>> getItems(Class<E> type,
680 Bundle<E> bundle) {
681 List<MetaInfo<E>> list = new ArrayList<MetaInfo<E>>();
76b51de9 682 List<MetaInfo<E>> shadow = new ArrayList<MetaInfo<E>>();
9e834013 683 for (E id : type.getEnumConstants()) {
76b51de9
NR
684 MetaInfo<E> info = new MetaInfo<E>(type, bundle, id);
685 list.add(info);
686 shadow.add(info);
9e834013
NR
687 }
688
76b51de9
NR
689 for (int i = 0; i < list.size(); i++) {
690 MetaInfo<E> info = list.get(i);
8517b60c 691
76b51de9
NR
692 MetaInfo<E> parent = findParent(info, shadow);
693 if (parent != null) {
694 list.remove(i--);
695 parent.children.add(info);
d18e136e 696 info.name = idToName(info.id, parent.id);
8517b60c
NR
697 }
698 }
699
76b51de9 700 return list;
8517b60c
NR
701 }
702
76b51de9
NR
703 /**
704 * Find the longest parent of the given {@link MetaInfo}, which means:
705 * <ul>
706 * <li>the parent is a {@link Meta#group()}</li>
707 * <li>the parent Id is a substring of the Id of the given {@link MetaInfo}</li>
708 * <li>there is no other parent sharing a substring for this
709 * {@link MetaInfo} with a longer Id</li>
710 * </ul>
711 *
712 * @param <E>
713 * the kind of enum
714 * @param info
715 * the info to look for a parent for
716 * @param candidates
717 * the list of potential parents
718 *
719 * @return the longest parent or NULL if no parent is found
720 */
8517b60c 721 static private <E extends Enum<E>> MetaInfo<E> findParent(MetaInfo<E> info,
76b51de9
NR
722 List<MetaInfo<E>> candidates) {
723 String id = info.id.toString();
8517b60c
NR
724 MetaInfo<E> group = null;
725 for (MetaInfo<E> pcandidate : candidates) {
76b51de9
NR
726 if (pcandidate.isGroup()) {
727 String candidateId = pcandidate.id.toString();
728 if (!id.equals(candidateId) && id.startsWith(candidateId)) {
729 if (group == null
730 || group.id.toString().length() < candidateId
731 .length()) {
732 group = pcandidate;
733 }
8517b60c
NR
734 }
735 }
736 }
737
738 return group;
739 }
d18e136e
NR
740
741 static private <E extends Enum<E>> String idToName(E id, E prefix) {
742 String name = id.toString();
743 if (prefix != null && name.startsWith(prefix.toString())) {
744 name = name.substring(prefix.toString().length());
745 }
746
747 if (name.length() > 0) {
748 name = name.substring(0, 1).toUpperCase()
749 + name.substring(1).toLowerCase();
750 }
751
752 name = name.replace("_", " ");
753
754 return name.trim();
755 }
9e834013 756}