Commit | Line | Data |
---|---|---|
ec1f3444 NR |
1 | package be.nikiroo.utils.resources; |
2 | ||
b3aad1f9 | 3 | import java.awt.Color; |
ec1f3444 NR |
4 | import java.io.BufferedWriter; |
5 | import java.io.File; | |
6 | import java.io.FileInputStream; | |
7 | import java.io.FileOutputStream; | |
8 | import java.io.IOException; | |
9 | import java.io.InputStreamReader; | |
10 | import java.io.OutputStreamWriter; | |
11 | import java.io.Reader; | |
ec1f3444 NR |
12 | import java.io.Writer; |
13 | import java.lang.reflect.Field; | |
14 | import java.util.ArrayList; | |
62c9ec78 | 15 | import java.util.HashMap; |
ec1f3444 NR |
16 | import java.util.List; |
17 | import java.util.Locale; | |
62c9ec78 | 18 | import java.util.Map; |
ec1f3444 NR |
19 | import java.util.MissingResourceException; |
20 | import java.util.PropertyResourceBundle; | |
21 | import java.util.ResourceBundle; | |
22 | ||
ec1f3444 | 23 | /** |
62c9ec78 | 24 | * This class encapsulate a {@link ResourceBundle} in UTF-8. It allows to |
ec1f3444 NR |
25 | * retrieve values associated to an enumeration, and allows some additional |
26 | * methods. | |
62c9ec78 NR |
27 | * <p> |
28 | * It also sports a writable change map, and you can save back the | |
29 | * {@link Bundle} to file with {@link Bundle#updateFile(String)}. | |
ec1f3444 | 30 | * |
ec1f3444 NR |
31 | * @param <E> |
32 | * the enum to use to get values out of this class | |
db31c358 NR |
33 | * |
34 | * @author niki | |
ec1f3444 | 35 | */ |
db31c358 | 36 | |
ec1f3444 | 37 | public class Bundle<E extends Enum<E>> { |
db31c358 | 38 | /** The type of E. */ |
ec1f3444 | 39 | protected Class<E> type; |
db31c358 NR |
40 | /** |
41 | * The {@link Enum} associated to this {@link Bundle} (all the keys used in | |
42 | * this {@link Bundle} will be of this type). | |
43 | */ | |
44 | protected Enum<?> keyType; | |
45 | ||
46 | private TransBundle<E> descriptionBundle; | |
47 | ||
48 | /** R/O map */ | |
49 | private Map<String, String> map; | |
50 | /** R/W map */ | |
51 | private Map<String, String> changeMap; | |
ec1f3444 NR |
52 | |
53 | /** | |
54 | * Create a new {@link Bundles} of the given name. | |
55 | * | |
56 | * @param type | |
57 | * a runtime instance of the class of E | |
ec1f3444 NR |
58 | * @param name |
59 | * the name of the {@link Bundles} | |
db31c358 NR |
60 | * @param descriptionBundle |
61 | * the description {@link TransBundle}, that is, a | |
62 | * {@link TransBundle} dedicated to the description of the values | |
63 | * of the given {@link Bundle} (can be NULL) | |
ec1f3444 | 64 | */ |
db31c358 NR |
65 | protected Bundle(Class<E> type, Enum<?> name, |
66 | TransBundle<E> descriptionBundle) { | |
ec1f3444 | 67 | this.type = type; |
db31c358 NR |
68 | this.keyType = name; |
69 | this.descriptionBundle = descriptionBundle; | |
70 | ||
487926f7 | 71 | this.map = new HashMap<String, String>(); |
62c9ec78 | 72 | this.changeMap = new HashMap<String, String>(); |
e9ca6bb8 | 73 | setBundle(name, Locale.getDefault(), false); |
ec1f3444 NR |
74 | } |
75 | ||
76 | /** | |
77 | * Return the value associated to the given id as a {@link String}. | |
78 | * | |
db31c358 | 79 | * @param id |
ec1f3444 NR |
80 | * the id of the value to get |
81 | * | |
82 | * @return the associated value, or NULL if not found (not present in the | |
83 | * resource file) | |
84 | */ | |
85 | public String getString(E id) { | |
487926f7 | 86 | return getString(id.name()); |
ec1f3444 NR |
87 | } |
88 | ||
62c9ec78 NR |
89 | /** |
90 | * Set the value associated to the given id as a {@link String}. | |
91 | * | |
db31c358 | 92 | * @param id |
62c9ec78 NR |
93 | * the id of the value to get |
94 | * @param value | |
95 | * the value | |
96 | * | |
97 | */ | |
98 | public void setString(E id, String value) { | |
487926f7 | 99 | setString(id.name(), value); |
62c9ec78 NR |
100 | } |
101 | ||
ec1f3444 NR |
102 | /** |
103 | * Return the value associated to the given id as a {@link String} suffixed | |
104 | * with the runtime value "_suffix" (that is, "_" and suffix). | |
487926f7 NR |
105 | * <p> |
106 | * Will only accept suffixes that form an existing id. | |
ec1f3444 | 107 | * |
db31c358 | 108 | * @param id |
ec1f3444 NR |
109 | * the id of the value to get |
110 | * @param suffix | |
111 | * the runtime suffix | |
112 | * | |
113 | * @return the associated value, or NULL if not found (not present in the | |
114 | * resource file) | |
115 | */ | |
116 | public String getStringX(E id, String suffix) { | |
117 | String key = id.name() | |
80383c14 | 118 | + (suffix == null ? "" : "_" + suffix.toUpperCase()); |
ec1f3444 | 119 | |
487926f7 NR |
120 | try { |
121 | id = Enum.valueOf(type, key); | |
122 | return getString(id); | |
123 | } catch (IllegalArgumentException e) { | |
124 | ||
ec1f3444 NR |
125 | } |
126 | ||
127 | return null; | |
128 | } | |
129 | ||
62c9ec78 NR |
130 | /** |
131 | * Set the value associated to the given id as a {@link String} suffixed | |
132 | * with the runtime value "_suffix" (that is, "_" and suffix). | |
487926f7 NR |
133 | * <p> |
134 | * Will only accept suffixes that form an existing id. | |
62c9ec78 | 135 | * |
db31c358 | 136 | * @param id |
62c9ec78 NR |
137 | * the id of the value to get |
138 | * @param suffix | |
139 | * the runtime suffix | |
140 | * @param value | |
141 | * the value | |
142 | */ | |
143 | public void setStringX(E id, String suffix, String value) { | |
144 | String key = id.name() | |
145 | + (suffix == null ? "" : "_" + suffix.toUpperCase()); | |
146 | ||
487926f7 NR |
147 | try { |
148 | id = Enum.valueOf(type, key); | |
149 | setString(id, value); | |
150 | } catch (IllegalArgumentException e) { | |
151 | ||
152 | } | |
62c9ec78 NR |
153 | } |
154 | ||
ec1f3444 NR |
155 | /** |
156 | * Return the value associated to the given id as a {@link Boolean}. | |
157 | * | |
db31c358 | 158 | * @param id |
ec1f3444 NR |
159 | * the id of the value to get |
160 | * | |
161 | * @return the associated value | |
162 | */ | |
163 | public Boolean getBoolean(E id) { | |
164 | String str = getString(id); | |
165 | if (str != null && str.length() > 0) { | |
166 | if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("on") | |
167 | || str.equalsIgnoreCase("yes")) | |
168 | return true; | |
169 | if (str.equalsIgnoreCase("false") || str.equalsIgnoreCase("off") | |
170 | || str.equalsIgnoreCase("no")) | |
171 | return false; | |
172 | ||
173 | } | |
174 | ||
175 | return null; | |
176 | } | |
177 | ||
178 | /** | |
62c9ec78 | 179 | * Return the value associated to the given id as a {@link Boolean}. |
ec1f3444 | 180 | * |
db31c358 | 181 | * @param id |
ec1f3444 NR |
182 | * the id of the value to get |
183 | * @param def | |
184 | * the default value when it is not present in the config file or | |
185 | * if it is not a boolean value | |
186 | * | |
187 | * @return the associated value | |
188 | */ | |
189 | public boolean getBoolean(E id, boolean def) { | |
190 | Boolean b = getBoolean(id); | |
191 | if (b != null) | |
192 | return b; | |
193 | ||
194 | return def; | |
195 | } | |
196 | ||
197 | /** | |
198 | * Return the value associated to the given id as an {@link Integer}. | |
199 | * | |
db31c358 | 200 | * @param id |
ec1f3444 NR |
201 | * the id of the value to get |
202 | * | |
203 | * @return the associated value | |
204 | */ | |
205 | public Integer getInteger(E id) { | |
206 | try { | |
207 | return Integer.parseInt(getString(id)); | |
208 | } catch (Exception e) { | |
209 | } | |
210 | ||
211 | return null; | |
212 | } | |
213 | ||
214 | /** | |
db31c358 | 215 | * Return the value associated to the given id as an int. |
ec1f3444 | 216 | * |
db31c358 | 217 | * @param id |
ec1f3444 NR |
218 | * the id of the value to get |
219 | * @param def | |
220 | * the default value when it is not present in the config file or | |
221 | * if it is not a int value | |
222 | * | |
223 | * @return the associated value | |
224 | */ | |
225 | public int getInteger(E id, int def) { | |
226 | Integer i = getInteger(id); | |
227 | if (i != null) | |
228 | return i; | |
229 | ||
230 | return def; | |
231 | } | |
232 | ||
233 | /** | |
234 | * Return the value associated to the given id as a {@link Character}. | |
235 | * | |
db31c358 | 236 | * @param id |
ec1f3444 NR |
237 | * the id of the value to get |
238 | * | |
239 | * @return the associated value | |
240 | */ | |
62c9ec78 | 241 | public Character getCharacter(E id) { |
ec1f3444 NR |
242 | String s = getString(id).trim(); |
243 | if (s.length() > 0) { | |
244 | return s.charAt(0); | |
245 | } | |
246 | ||
62c9ec78 NR |
247 | return null; |
248 | } | |
249 | ||
250 | /** | |
251 | * Return the value associated to the given id as a {@link Character}. | |
252 | * | |
db31c358 | 253 | * @param id |
62c9ec78 NR |
254 | * the id of the value to get |
255 | * @param def | |
256 | * the default value when it is not present in the config file or | |
257 | * if it is not a char value | |
258 | * | |
259 | * @return the associated value | |
260 | */ | |
261 | public char getCharacter(E id, char def) { | |
262 | String s = getString(id).trim(); | |
263 | if (s.length() > 0) { | |
264 | return s.charAt(0); | |
265 | } | |
266 | ||
267 | return def; | |
ec1f3444 NR |
268 | } |
269 | ||
b3aad1f9 NR |
270 | /** |
271 | * Return the value associated to the given id as a {@link Color}. | |
272 | * | |
db31c358 NR |
273 | * @param id |
274 | * the id of the value to get | |
b3aad1f9 NR |
275 | * |
276 | * @return the associated value | |
277 | */ | |
278 | public Color getColor(E id) { | |
279 | Color color = null; | |
280 | ||
281 | String bg = getString(id).trim(); | |
62c9ec78 | 282 | if (bg.startsWith("#") && (bg.length() == 7 || bg.length() == 9)) { |
b3aad1f9 | 283 | try { |
62c9ec78 NR |
284 | int r = Integer.parseInt(bg.substring(1, 3), 16); |
285 | int g = Integer.parseInt(bg.substring(3, 5), 16); | |
286 | int b = Integer.parseInt(bg.substring(5, 7), 16); | |
287 | int a = 255; | |
288 | if (bg.length() == 9) { | |
289 | a = Integer.parseInt(bg.substring(7, 9), 16); | |
290 | } | |
291 | color = new Color(r, g, b, a); | |
b3aad1f9 NR |
292 | } catch (NumberFormatException e) { |
293 | color = null; // no changes | |
294 | } | |
295 | } | |
296 | ||
db31c358 NR |
297 | // Try by name if still not found |
298 | if (color == null) { | |
299 | try { | |
300 | Field field = Color.class.getField(bg); | |
301 | color = (Color) field.get(null); | |
302 | } catch (Exception e) { | |
303 | } | |
304 | } | |
305 | // | |
306 | ||
b3aad1f9 NR |
307 | return color; |
308 | } | |
309 | ||
62c9ec78 NR |
310 | /** |
311 | * Set the value associated to the given id as a {@link Color}. | |
312 | * | |
db31c358 NR |
313 | * @param id |
314 | * the id of the value to set | |
315 | * @param color | |
316 | * the new color | |
62c9ec78 NR |
317 | */ |
318 | public void setColor(E id, Color color) { | |
db31c358 NR |
319 | // Check for named colours first |
320 | try { | |
321 | Field[] fields = Color.class.getFields(); | |
322 | for (Field field : fields) { | |
323 | if (field.equals(color)) { | |
324 | setString(id, field.getName()); | |
325 | return; | |
326 | } | |
327 | } | |
328 | } catch (Exception e) { | |
329 | } | |
330 | // | |
331 | ||
62c9ec78 NR |
332 | String r = Integer.toString(color.getRed(), 16); |
333 | String g = Integer.toString(color.getGreen(), 16); | |
334 | String b = Integer.toString(color.getBlue(), 16); | |
335 | String a = ""; | |
336 | if (color.getAlpha() < 255) { | |
337 | a = Integer.toString(color.getAlpha(), 16); | |
338 | } | |
339 | ||
340 | setString(id, "#" + r + g + b + a); | |
341 | } | |
342 | ||
487926f7 NR |
343 | /** |
344 | * Create/update the .properties file. | |
345 | * <p> | |
346 | * Will use the most likely candidate as base if the file does not already | |
347 | * exists and this resource is translatable (for instance, "en_US" will use | |
348 | * "en" as a base if the resource is a translation file). | |
349 | * <p> | |
350 | * Will update the files in {@link Bundles#getDirectory()}; it <b>MUST</b> | |
351 | * be set. | |
352 | * | |
353 | * @throws IOException | |
354 | * in case of IO errors | |
355 | */ | |
356 | public void updateFile() throws IOException { | |
357 | updateFile(Bundles.getDirectory()); | |
358 | } | |
359 | ||
ec1f3444 | 360 | /** |
80383c14 NR |
361 | * Create/update the .properties file. |
362 | * <p> | |
363 | * Will use the most likely candidate as base if the file does not already | |
364 | * exists and this resource is translatable (for instance, "en_US" will use | |
365 | * "en" as a base if the resource is a translation file). | |
ec1f3444 NR |
366 | * |
367 | * @param path | |
487926f7 NR |
368 | * the path where the .properties files are, <b>MUST NOT</b> be |
369 | * NULL | |
ec1f3444 NR |
370 | * |
371 | * @throws IOException | |
372 | * in case of IO errors | |
373 | */ | |
374 | public void updateFile(String path) throws IOException { | |
375 | File file = getUpdateFile(path); | |
376 | ||
377 | BufferedWriter writer = new BufferedWriter(new OutputStreamWriter( | |
378 | new FileOutputStream(file), "UTF-8")); | |
379 | ||
380 | writeHeader(writer); | |
381 | writer.write("\n"); | |
382 | writer.write("\n"); | |
383 | ||
384 | for (Field field : type.getDeclaredFields()) { | |
385 | Meta meta = field.getAnnotation(Meta.class); | |
386 | if (meta != null) { | |
cd0c27d2 | 387 | E id = Enum.valueOf(type, field.getName()); |
ec1f3444 NR |
388 | String info = getMetaInfo(meta); |
389 | ||
390 | if (info != null) { | |
391 | writer.write(info); | |
392 | writer.write("\n"); | |
393 | } | |
394 | ||
395 | writeValue(writer, id); | |
396 | } | |
397 | } | |
398 | ||
399 | writer.close(); | |
400 | } | |
401 | ||
db31c358 NR |
402 | /** |
403 | * The description {@link TransBundle}, that is, a {@link TransBundle} | |
404 | * dedicated to the description of the values of the given {@link Bundle} | |
405 | * (can be NULL). | |
406 | * | |
407 | * @return the description {@link TransBundle} | |
408 | */ | |
409 | public TransBundle<E> getDescriptionBundle() { | |
410 | return descriptionBundle; | |
411 | } | |
412 | ||
80383c14 NR |
413 | /** |
414 | * Reload the {@link Bundle} data files. | |
e9ca6bb8 NR |
415 | * |
416 | * @param resetToDefault | |
417 | * reset to the default configuration (do not look into the | |
418 | * possible user configuration files, only take the original | |
419 | * configuration) | |
80383c14 | 420 | */ |
e9ca6bb8 | 421 | public void reload(boolean resetToDefault) { |
db31c358 | 422 | setBundle(keyType, Locale.getDefault(), resetToDefault); |
80383c14 NR |
423 | } |
424 | ||
ec1f3444 NR |
425 | /** |
426 | * Check if the internal map contains the given key. | |
427 | * | |
428 | * @param key | |
429 | * the key to check for | |
430 | * | |
431 | * @return true if it does | |
432 | */ | |
433 | protected boolean containsKey(String key) { | |
487926f7 | 434 | return changeMap.containsKey(key) || map.containsKey(key); |
ec1f3444 NR |
435 | } |
436 | ||
437 | /** | |
2cce3dcb NR |
438 | * Get the value for the given key if it exists in the internal map, or NULL |
439 | * if not. | |
ec1f3444 NR |
440 | * |
441 | * @param key | |
442 | * the key to check for | |
443 | * | |
2cce3dcb | 444 | * @return the value, or NULL |
ec1f3444 NR |
445 | */ |
446 | protected String getString(String key) { | |
62c9ec78 NR |
447 | if (changeMap.containsKey(key)) { |
448 | return changeMap.get(key); | |
449 | } | |
450 | ||
487926f7 NR |
451 | if (map.containsKey(key)) { |
452 | return map.get(key); | |
ec1f3444 NR |
453 | } |
454 | ||
455 | return null; | |
456 | } | |
457 | ||
62c9ec78 NR |
458 | /** |
459 | * Set the value for this key, in the change map (it is kept in memory, not | |
460 | * yet on disk). | |
461 | * | |
462 | * @param key | |
463 | * the key | |
464 | * @param value | |
465 | * the associated value | |
466 | */ | |
467 | protected void setString(String key, String value) { | |
487926f7 | 468 | changeMap.put(key, value == null ? null : value.trim()); |
62c9ec78 NR |
469 | } |
470 | ||
ec1f3444 NR |
471 | /** |
472 | * Return formated, display-able information from the {@link Meta} field | |
473 | * given. Each line will always starts with a "#" character. | |
474 | * | |
475 | * @param meta | |
476 | * the {@link Meta} field | |
477 | * | |
478 | * @return the information to display or NULL if none | |
479 | */ | |
480 | protected String getMetaInfo(Meta meta) { | |
db31c358 NR |
481 | String desc = meta.description(); |
482 | boolean group = meta.group(); | |
483 | Meta.Format format = meta.format(); | |
484 | String[] list = meta.list(); | |
485 | boolean nullable = meta.nullable(); | |
ec1f3444 | 486 | String info = meta.info(); |
db31c358 | 487 | boolean array = meta.array(); |
ec1f3444 | 488 | |
db31c358 NR |
489 | // Default, empty values -> NULL |
490 | if (desc.length() + list.length + info.length() == 0 && !group | |
491 | && nullable && format == Meta.Format.STRING) { | |
ec1f3444 | 492 | return null; |
db31c358 | 493 | } |
ec1f3444 NR |
494 | |
495 | StringBuilder builder = new StringBuilder(); | |
db31c358 NR |
496 | builder.append("# ").append(desc); |
497 | if (desc.length() > 20) { | |
498 | builder.append("\n#"); | |
499 | } | |
ec1f3444 | 500 | |
db31c358 NR |
501 | if (group) { |
502 | builder.append("This item is used as a group, its content is not expected to be used."); | |
503 | } else { | |
504 | builder.append(" (FORMAT: ").append(format) | |
505 | .append(nullable ? "" : " (required)"); | |
506 | builder.append(") ").append(info); | |
507 | ||
508 | if (list.length > 0) { | |
509 | builder.append("\n# ALLOWED VALUES:"); | |
510 | for (String value : list) { | |
511 | builder.append(" \"").append(value).append("\""); | |
512 | } | |
ec1f3444 NR |
513 | } |
514 | ||
db31c358 NR |
515 | if (array) { |
516 | builder.append("\n# (This item accept a list of comma-separated values)"); | |
ec1f3444 NR |
517 | } |
518 | } | |
519 | ||
ec1f3444 NR |
520 | return builder.toString(); |
521 | } | |
522 | ||
523 | /** | |
524 | * The display name used in the <tt>.properties file</tt>. | |
525 | * | |
526 | * @return the name | |
527 | */ | |
528 | protected String getBundleDisplayName() { | |
db31c358 | 529 | return keyType.toString(); |
ec1f3444 NR |
530 | } |
531 | ||
532 | /** | |
533 | * Write the header found in the configuration <tt>.properties</tt> file of | |
534 | * this {@link Bundles}. | |
535 | * | |
536 | * @param writer | |
537 | * the {@link Writer} to write the header in | |
538 | * | |
539 | * @throws IOException | |
540 | * in case of IO error | |
541 | */ | |
542 | protected void writeHeader(Writer writer) throws IOException { | |
543 | writer.write("# " + getBundleDisplayName() + "\n"); | |
544 | writer.write("#\n"); | |
545 | } | |
546 | ||
547 | /** | |
548 | * Write the given id to the config file, i.e., "MY_ID = my_curent_value" | |
549 | * followed by a new line | |
550 | * | |
551 | * @param writer | |
552 | * the {@link Writer} to write into | |
553 | * @param id | |
554 | * the id to write | |
555 | * | |
556 | * @throws IOException | |
557 | * in case of IO error | |
558 | */ | |
559 | protected void writeValue(Writer writer, E id) throws IOException { | |
560 | writeValue(writer, id.name(), getString(id)); | |
561 | } | |
562 | ||
563 | /** | |
564 | * Write the given data to the config file, i.e., "MY_ID = my_curent_value" | |
565 | * followed by a new line | |
566 | * | |
567 | * @param writer | |
568 | * the {@link Writer} to write into | |
569 | * @param id | |
570 | * the id to write | |
571 | * @param value | |
572 | * the id's value | |
573 | * | |
574 | * @throws IOException | |
575 | * in case of IO error | |
576 | */ | |
577 | protected void writeValue(Writer writer, String id, String value) | |
578 | throws IOException { | |
579 | writer.write(id); | |
580 | writer.write(" = "); | |
581 | ||
582 | if (value == null) { | |
583 | value = ""; | |
584 | } | |
585 | ||
009196a4 | 586 | String[] lines = value.replaceAll("\t", "\\\\\\t").split("\n"); |
ec1f3444 NR |
587 | for (int i = 0; i < lines.length; i++) { |
588 | writer.write(lines[i]); | |
589 | if (i < lines.length - 1) { | |
590 | writer.write("\\n\\"); | |
591 | } | |
592 | writer.write("\n"); | |
593 | } | |
594 | } | |
595 | ||
596 | /** | |
597 | * Return the source file for this {@link Bundles} from the given path. | |
598 | * | |
599 | * @param path | |
600 | * the path where the .properties files are | |
601 | * | |
602 | * @return the source {@link File} | |
ec1f3444 NR |
603 | */ |
604 | protected File getUpdateFile(String path) { | |
db31c358 | 605 | return new File(path, keyType.name() + ".properties"); |
ec1f3444 NR |
606 | } |
607 | ||
608 | /** | |
62c9ec78 | 609 | * Change the currently used bundle, and reset all changes. |
ec1f3444 NR |
610 | * |
611 | * @param name | |
612 | * the name of the bundle to load | |
613 | * @param locale | |
614 | * the {@link Locale} to use | |
e9ca6bb8 NR |
615 | * @param resetToDefault |
616 | * reset to the default configuration (do not look into the | |
617 | * possible user configuration files, only take the original | |
618 | * configuration) | |
ec1f3444 | 619 | */ |
e9ca6bb8 | 620 | protected void setBundle(Enum<?> name, Locale locale, boolean resetToDefault) { |
62c9ec78 | 621 | changeMap.clear(); |
ec1f3444 NR |
622 | String dir = Bundles.getDirectory(); |
623 | ||
487926f7 | 624 | boolean found = false; |
e9ca6bb8 | 625 | if (!resetToDefault && dir != null) { |
ec1f3444 NR |
626 | try { |
627 | File file = getPropertyFile(dir, name.name(), locale); | |
628 | if (file != null) { | |
629 | Reader reader = new InputStreamReader(new FileInputStream( | |
630 | file), "UTF8"); | |
487926f7 NR |
631 | resetMap(new PropertyResourceBundle(reader)); |
632 | found = true; | |
ec1f3444 NR |
633 | } |
634 | } catch (IOException e) { | |
635 | e.printStackTrace(); | |
636 | } | |
637 | } | |
638 | ||
487926f7 NR |
639 | if (!found) { |
640 | String bname = type.getPackage().getName() + "." + name.name(); | |
e9ca6bb8 | 641 | try { |
487926f7 NR |
642 | resetMap(ResourceBundle |
643 | .getBundle(bname, locale, type.getClassLoader(), | |
644 | new FixedResourceBundleControl())); | |
e9ca6bb8 NR |
645 | } catch (Exception e) { |
646 | // We have no bundle for this Bundle | |
487926f7 NR |
647 | System.err.println("No bundle found for: " + bname); |
648 | resetMap(null); | |
649 | } | |
650 | } | |
651 | } | |
652 | ||
653 | /** | |
654 | * Reset the backing map to the content of the given bundle, or empty if | |
655 | * bundle is NULL. | |
656 | * | |
657 | * @param bundle | |
658 | * the bundle to copy | |
659 | */ | |
b771aed5 | 660 | protected void resetMap(ResourceBundle bundle) { |
487926f7 NR |
661 | this.map.clear(); |
662 | ||
663 | if (bundle != null) { | |
664 | for (E field : type.getEnumConstants()) { | |
665 | try { | |
666 | String value = bundle.getString(field.name()); | |
667 | this.map.put(field.name(), | |
668 | value == null ? null : value.trim()); | |
669 | } catch (MissingResourceException e) { | |
670 | } | |
e9ca6bb8 NR |
671 | } |
672 | } | |
673 | } | |
cd0c27d2 | 674 | |
e9ca6bb8 NR |
675 | /** |
676 | * Take a snapshot of the changes in memory in this {@link Bundle} made by | |
677 | * the "set" methods ( {@link Bundle#setString(Enum, String)}...) at the | |
678 | * current time. | |
679 | * | |
487926f7 | 680 | * @return a snapshot to use with {@link Bundle#restoreSnapshot(Object)} |
e9ca6bb8 | 681 | */ |
487926f7 | 682 | public Object takeSnapshot() { |
e9ca6bb8 NR |
683 | return new HashMap<String, String>(changeMap); |
684 | } | |
685 | ||
686 | /** | |
687 | * Restore a snapshot taken with {@link Bundle}, or reset the current | |
688 | * changes if the snapshot is NULL. | |
689 | * | |
690 | * @param snap | |
691 | * the snapshot or NULL | |
692 | */ | |
693 | @SuppressWarnings("unchecked") | |
487926f7 | 694 | public void restoreSnapshot(Object snap) { |
e9ca6bb8 NR |
695 | if (snap == null) { |
696 | changeMap.clear(); | |
697 | } else { | |
698 | if (snap instanceof Map) { | |
699 | changeMap = (Map<String, String>) snap; | |
700 | } else { | |
d827da2a | 701 | throw new RuntimeException( |
e9ca6bb8 NR |
702 | "Restoring changes in a Bundle must be done on a changes snapshot, " |
703 | + "or NULL to discard current changes"); | |
704 | } | |
ec1f3444 NR |
705 | } |
706 | } | |
707 | ||
708 | /** | |
709 | * Return the resource file that is closer to the {@link Locale}. | |
710 | * | |
711 | * @param dir | |
db31c358 | 712 | * the directory to look into |
ec1f3444 | 713 | * @param name |
db31c358 | 714 | * the file base name (without <tt>.properties</tt>) |
ec1f3444 NR |
715 | * @param locale |
716 | * the {@link Locale} | |
717 | * | |
718 | * @return the closest match or NULL if none | |
719 | */ | |
720 | private File getPropertyFile(String dir, String name, Locale locale) { | |
721 | List<String> locales = new ArrayList<String>(); | |
722 | if (locale != null) { | |
723 | String country = locale.getCountry() == null ? "" : locale | |
724 | .getCountry(); | |
725 | String language = locale.getLanguage() == null ? "" : locale | |
726 | .getLanguage(); | |
727 | if (!language.isEmpty() && !country.isEmpty()) { | |
728 | locales.add("_" + language + "-" + country); | |
729 | } | |
730 | if (!language.isEmpty()) { | |
731 | locales.add("_" + language); | |
732 | } | |
733 | } | |
734 | ||
735 | locales.add(""); | |
736 | ||
737 | File file = null; | |
738 | for (String loc : locales) { | |
739 | file = new File(dir, name + loc + ".properties"); | |
740 | if (file.exists()) { | |
741 | break; | |
ec1f3444 | 742 | } |
d827da2a | 743 | |
cd0c27d2 | 744 | file = null; |
ec1f3444 NR |
745 | } |
746 | ||
747 | return file; | |
748 | } | |
749 | } |