Commit | Line | Data |
---|---|---|
3bfc1d20 NR |
1 | package be.nikiroo.utils.resources; |
2 | ||
3 | import java.util.ArrayList; | |
4 | import java.util.List; | |
5 | ||
6 | /** | |
7 | * Internal class used to convert data to/from {@link String}s in the context of | |
8 | * {@link Bundle}s. | |
9 | * | |
10 | * @author niki | |
11 | */ | |
12 | class BundleHelper { | |
13 | /** | |
14 | * Convert the given {@link String} into a {@link Boolean} if it represents | |
15 | * a {@link Boolean}, or NULL if it doesn't. | |
16 | * <p> | |
17 | * Note: null, "strange text", ""... will all be converted to NULL. | |
18 | * | |
19 | * @param str | |
20 | * the input {@link String} | |
d5026c09 NR |
21 | * @param item |
22 | * the item number to use for an array of values, or -1 for | |
23 | * non-arrays | |
3bfc1d20 NR |
24 | * |
25 | * @return the converted {@link Boolean} or NULL | |
26 | */ | |
d5026c09 NR |
27 | static public Boolean parseBoolean(String str, int item) { |
28 | str = getItem(str, item); | |
29 | if (str == null) { | |
30 | return null; | |
3bfc1d20 NR |
31 | } |
32 | ||
d5026c09 NR |
33 | if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("on") |
34 | || str.equalsIgnoreCase("yes")) | |
35 | return true; | |
36 | if (str.equalsIgnoreCase("false") || str.equalsIgnoreCase("off") | |
37 | || str.equalsIgnoreCase("no")) | |
38 | return false; | |
39 | ||
3bfc1d20 NR |
40 | return null; |
41 | } | |
42 | ||
43 | /** | |
44 | * Return a {@link String} representation of the given {@link Boolean}. | |
45 | * | |
46 | * @param value | |
47 | * the input value | |
48 | * | |
49 | * @return the raw {@link String} value that correspond to it | |
50 | */ | |
51 | static public String fromBoolean(boolean value) { | |
52 | return Boolean.toString(value); | |
53 | } | |
54 | ||
55 | /** | |
56 | * Convert the given {@link String} into a {@link Integer} if it represents | |
57 | * a {@link Integer}, or NULL if it doesn't. | |
58 | * <p> | |
59 | * Note: null, "strange text", ""... will all be converted to NULL. | |
60 | * | |
61 | * @param str | |
62 | * the input {@link String} | |
d5026c09 NR |
63 | * @param item |
64 | * the item number to use for an array of values, or -1 for | |
65 | * non-arrays | |
3bfc1d20 NR |
66 | * |
67 | * @return the converted {@link Integer} or NULL | |
68 | */ | |
d5026c09 NR |
69 | static public Integer parseInteger(String str, int item) { |
70 | str = getItem(str, item); | |
71 | if (str == null) { | |
72 | return null; | |
73 | } | |
74 | ||
3bfc1d20 NR |
75 | try { |
76 | return Integer.parseInt(str); | |
77 | } catch (Exception e) { | |
78 | } | |
79 | ||
80 | return null; | |
81 | } | |
82 | ||
83 | /** | |
84 | * Return a {@link String} representation of the given {@link Integer}. | |
85 | * | |
86 | * @param value | |
87 | * the input value | |
88 | * | |
89 | * @return the raw {@link String} value that correspond to it | |
90 | */ | |
91 | static public String fromInteger(int value) { | |
92 | return Integer.toString(value); | |
93 | } | |
94 | ||
3bfc1d20 NR |
95 | /** |
96 | * Convert the given {@link String} into a {@link Character} if it | |
97 | * represents a {@link Character}, or NULL if it doesn't. | |
98 | * <p> | |
99 | * Note: null, "strange text", ""... will all be converted to NULL | |
100 | * (remember: any {@link String} whose length is not 1 is <b>not</b> a | |
101 | * {@link Character}). | |
102 | * | |
103 | * @param str | |
104 | * the input {@link String} | |
d5026c09 NR |
105 | * @param item |
106 | * the item number to use for an array of values, or -1 for | |
107 | * non-arrays | |
3bfc1d20 NR |
108 | * |
109 | * @return the converted {@link Character} or NULL | |
110 | */ | |
d5026c09 NR |
111 | static public Character parseCharacter(String str, int item) { |
112 | str = getItem(str, item); | |
113 | if (str == null) { | |
114 | return null; | |
115 | } | |
116 | ||
3bfc1d20 NR |
117 | String s = str.trim(); |
118 | if (s.length() == 1) { | |
119 | return s.charAt(0); | |
120 | } | |
121 | ||
122 | return null; | |
123 | } | |
124 | ||
125 | /** | |
126 | * Return a {@link String} representation of the given {@link Boolean}. | |
127 | * | |
128 | * @param value | |
129 | * the input value | |
130 | * | |
131 | * @return the raw {@link String} value that correspond to it | |
132 | */ | |
133 | static public String fromCharacter(char value) { | |
134 | return Character.toString(value); | |
135 | } | |
136 | ||
137 | /** | |
138 | * Convert the given {@link String} into a colour (represented here as an | |
139 | * {@link Integer}) if it represents a colour, or NULL if it doesn't. | |
140 | * <p> | |
141 | * The returned colour value is an ARGB value. | |
142 | * | |
143 | * @param str | |
144 | * the input {@link String} | |
d5026c09 NR |
145 | * @param item |
146 | * the item number to use for an array of values, or -1 for | |
147 | * non-arrays | |
3bfc1d20 NR |
148 | * |
149 | * @return the converted colour as an {@link Integer} value or NULL | |
150 | */ | |
d5026c09 NR |
151 | static Integer parseColor(String str, int item) { |
152 | str = getItem(str, item); | |
153 | if (str == null) { | |
154 | return null; | |
155 | } | |
156 | ||
3bfc1d20 NR |
157 | Integer rep = null; |
158 | ||
159 | str = str.trim(); | |
160 | int r = 0, g = 0, b = 0, a = -1; | |
161 | if (str.startsWith("#") && (str.length() == 7 || str.length() == 9)) { | |
162 | try { | |
163 | r = Integer.parseInt(str.substring(1, 3), 16); | |
164 | g = Integer.parseInt(str.substring(3, 5), 16); | |
165 | b = Integer.parseInt(str.substring(5, 7), 16); | |
166 | if (str.length() == 9) { | |
167 | a = Integer.parseInt(str.substring(7, 9), 16); | |
168 | } else { | |
169 | a = 255; | |
170 | } | |
171 | ||
172 | } catch (NumberFormatException e) { | |
173 | // no changes | |
174 | } | |
175 | } | |
176 | ||
177 | // Try by name if still not found | |
178 | if (a == -1) { | |
179 | if ("black".equalsIgnoreCase(str)) { | |
180 | a = 255; | |
181 | r = 0; | |
182 | g = 0; | |
183 | b = 0; | |
184 | } else if ("white".equalsIgnoreCase(str)) { | |
185 | a = 255; | |
186 | r = 255; | |
187 | g = 255; | |
188 | b = 255; | |
189 | } else if ("red".equalsIgnoreCase(str)) { | |
190 | a = 255; | |
191 | r = 255; | |
192 | g = 0; | |
193 | b = 0; | |
194 | } else if ("green".equalsIgnoreCase(str)) { | |
195 | a = 255; | |
196 | r = 0; | |
197 | g = 255; | |
198 | b = 0; | |
199 | } else if ("blue".equalsIgnoreCase(str)) { | |
200 | a = 255; | |
201 | r = 0; | |
202 | g = 0; | |
203 | b = 255; | |
204 | } else if ("grey".equalsIgnoreCase(str) | |
205 | || "gray".equalsIgnoreCase(str)) { | |
206 | a = 255; | |
207 | r = 128; | |
208 | g = 128; | |
209 | b = 128; | |
210 | } else if ("cyan".equalsIgnoreCase(str)) { | |
211 | a = 255; | |
212 | r = 0; | |
213 | g = 255; | |
214 | b = 255; | |
215 | } else if ("magenta".equalsIgnoreCase(str)) { | |
216 | a = 255; | |
217 | r = 255; | |
218 | g = 0; | |
219 | b = 255; | |
220 | } else if ("yellow".equalsIgnoreCase(str)) { | |
221 | a = 255; | |
222 | r = 255; | |
223 | g = 255; | |
224 | b = 0; | |
225 | } | |
226 | } | |
227 | ||
228 | if (a != -1) { | |
229 | rep = ((a & 0xFF) << 24) // | |
230 | | ((r & 0xFF) << 16) // | |
231 | | ((g & 0xFF) << 8) // | |
232 | | ((b & 0xFF) << 0); | |
233 | } | |
234 | ||
235 | return rep; | |
236 | } | |
237 | ||
238 | /** | |
239 | * Return a {@link String} representation of the given colour. | |
240 | * <p> | |
241 | * The colour value is interpreted as an ARGB value. | |
242 | * | |
243 | * @param color | |
244 | * the ARGB colour value | |
245 | * @return the raw {@link String} value that correspond to it | |
246 | */ | |
9e834013 | 247 | static public String fromColor(int color) { |
3bfc1d20 NR |
248 | int a = (color >> 24) & 0xFF; |
249 | int r = (color >> 16) & 0xFF; | |
250 | int g = (color >> 8) & 0xFF; | |
251 | int b = (color >> 0) & 0xFF; | |
252 | ||
253 | String rs = Integer.toString(r, 16); | |
254 | String gs = Integer.toString(g, 16); | |
255 | String bs = Integer.toString(b, 16); | |
256 | String as = ""; | |
257 | if (a < 255) { | |
258 | as = Integer.toString(a, 16); | |
259 | } | |
260 | ||
261 | return "#" + rs + gs + bs + as; | |
262 | } | |
263 | ||
d5026c09 | 264 | /** |
856f5898 | 265 | * The size of this raw list (note than a NULL list is of size 0). |
d5026c09 NR |
266 | * |
267 | * @param raw | |
268 | * the raw list | |
269 | * | |
856f5898 NR |
270 | * @return its size if it is a list (NULL is an empty list), -1 if it is not |
271 | * a list | |
d5026c09 NR |
272 | */ |
273 | static public int getListSize(String raw) { | |
856f5898 NR |
274 | if (raw == null) { |
275 | return 0; | |
276 | } | |
277 | ||
d5026c09 NR |
278 | List<String> list = parseList(raw, -1); |
279 | if (list == null) { | |
280 | return -1; | |
281 | } | |
282 | ||
283 | return list.size(); | |
284 | } | |
285 | ||
3bfc1d20 NR |
286 | /** |
287 | * Return a {@link String} representation of the given list of values. | |
288 | * <p> | |
289 | * The list of values is comma-separated and each value is surrounded by | |
d5026c09 | 290 | * double-quotes; caret (^) and double-quotes (") are escaped by a caret. |
3bfc1d20 NR |
291 | * |
292 | * @param str | |
293 | * the input value | |
d5026c09 NR |
294 | * @param item |
295 | * the item number to use for an array of values, or -1 for | |
296 | * non-arrays | |
297 | * | |
3bfc1d20 NR |
298 | * @return the raw {@link String} value that correspond to it |
299 | */ | |
d5026c09 | 300 | static public List<String> parseList(String str, int item) { |
3bfc1d20 NR |
301 | if (str == null) { |
302 | return null; | |
303 | } | |
d5026c09 NR |
304 | |
305 | if (item >= 0) { | |
306 | str = getItem(str, item); | |
307 | } | |
308 | ||
3bfc1d20 NR |
309 | List<String> list = new ArrayList<String>(); |
310 | try { | |
311 | boolean inQuote = false; | |
312 | boolean prevIsBackSlash = false; | |
313 | StringBuilder builder = new StringBuilder(); | |
314 | for (int i = 0; i < str.length(); i++) { | |
315 | char car = str.charAt(i); | |
316 | ||
317 | if (prevIsBackSlash) { | |
b15a4985 | 318 | // We don't process it here |
3bfc1d20 NR |
319 | builder.append(car); |
320 | prevIsBackSlash = false; | |
321 | } else { | |
322 | switch (car) { | |
323 | case '"': | |
b15a4985 NR |
324 | // We don't process it here |
325 | builder.append(car); | |
326 | ||
3bfc1d20 | 327 | if (inQuote) { |
b15a4985 | 328 | list.add(unescape(builder.toString())); |
3bfc1d20 NR |
329 | builder.setLength(0); |
330 | } | |
331 | ||
332 | inQuote = !inQuote; | |
333 | break; | |
d5026c09 | 334 | case '^': |
b15a4985 NR |
335 | // We don't process it here |
336 | builder.append(car); | |
3bfc1d20 NR |
337 | prevIsBackSlash = true; |
338 | break; | |
339 | case ' ': | |
340 | case '\n': | |
341 | case '\r': | |
342 | if (inQuote) { | |
343 | builder.append(car); | |
344 | } | |
345 | break; | |
346 | ||
347 | case ',': | |
348 | if (!inQuote) { | |
349 | break; | |
350 | } | |
351 | // continue to default | |
352 | default: | |
353 | if (!inQuote) { | |
354 | // Bad format! | |
355 | return null; | |
356 | } | |
357 | ||
358 | builder.append(car); | |
359 | break; | |
360 | } | |
361 | } | |
362 | } | |
363 | ||
364 | if (inQuote || prevIsBackSlash) { | |
365 | // Bad format! | |
366 | return null; | |
367 | } | |
368 | ||
369 | } catch (Exception e) { | |
370 | return null; | |
371 | } | |
372 | ||
373 | return list; | |
374 | } | |
375 | ||
376 | /** | |
377 | * Return a {@link String} representation of the given list of values. | |
d5026c09 | 378 | * <p> |
856f5898 NR |
379 | * NULL will be assimilated to an empty {@link String} if later non-null |
380 | * values exist, or just ignored if not. | |
381 | * <p> | |
382 | * Example: | |
383 | * <ul> | |
384 | * <li><tt>1</tt>,<tt>NULL</tt>, <tt>3</tt> will become <tt>1</tt>, | |
385 | * <tt>""</tt>, <tt>3</tt></li> | |
386 | * <li><tt>1</tt>,<tt>NULL</tt>, <tt>NULL</tt> will become <tt>1</tt></li> | |
387 | * <li><tt>NULL</tt>, <tt>NULL</tt>, <tt>NULL</tt> will become an empty list | |
388 | * </li> | |
389 | * </ul> | |
3bfc1d20 NR |
390 | * |
391 | * @param list | |
392 | * the input value | |
393 | * | |
394 | * @return the raw {@link String} value that correspond to it | |
395 | */ | |
396 | static public String fromList(List<String> list) { | |
856f5898 NR |
397 | if (list == null) { |
398 | list = new ArrayList<String>(); | |
399 | } | |
400 | ||
401 | int last = list.size() - 1; | |
402 | for (int i = 0; i < list.size(); i++) { | |
403 | if (list.get(i) != null) { | |
404 | last = i; | |
405 | } | |
406 | } | |
407 | ||
3bfc1d20 | 408 | StringBuilder builder = new StringBuilder(); |
856f5898 NR |
409 | for (int i = 0; i <= last; i++) { |
410 | String item = list.get(i); | |
411 | if (item == null) { | |
412 | item = ""; | |
413 | } | |
414 | ||
3bfc1d20 NR |
415 | if (builder.length() > 0) { |
416 | builder.append(", "); | |
417 | } | |
856f5898 | 418 | builder.append(escape(item)); |
b15a4985 NR |
419 | } |
420 | ||
421 | return builder.toString(); | |
422 | } | |
423 | ||
856f5898 NR |
424 | /** |
425 | * Return a {@link String} representation of the given list of values. | |
426 | * <p> | |
427 | * NULL will be assimilated to an empty {@link String} if later non-null | |
428 | * values exist, or just ignored if not. | |
429 | * <p> | |
430 | * Example: | |
431 | * <ul> | |
432 | * <li><tt>1</tt>,<tt>NULL</tt>, <tt>3</tt> will become <tt>1</tt>, | |
433 | * <tt>""</tt>, <tt>3</tt></li> | |
434 | * <li><tt>1</tt>,<tt>NULL</tt>, <tt>NULL</tt> will become <tt>1</tt></li> | |
435 | * <li><tt>NULL</tt>, <tt>NULL</tt>, <tt>NULL</tt> will become an empty list | |
436 | * </li> | |
437 | * </ul> | |
438 | * | |
439 | * @param list | |
440 | * the input value | |
441 | * @param value | |
442 | * the value to insert | |
443 | * @param item | |
444 | * the position to insert it at | |
445 | * | |
446 | * @return the raw {@link String} value that correspond to it | |
447 | */ | |
448 | static public String fromList(List<String> list, String value, int item) { | |
449 | if (list == null) { | |
450 | list = new ArrayList<String>(); | |
451 | } | |
452 | ||
453 | while (item >= list.size()) { | |
454 | list.add(null); | |
455 | } | |
456 | list.set(item, value); | |
457 | ||
458 | return fromList(list); | |
459 | } | |
460 | ||
461 | /** | |
462 | * Return a {@link String} representation of the given list of values. | |
463 | * <p> | |
464 | * NULL will be assimilated to an empty {@link String} if later non-null | |
465 | * values exist, or just ignored if not. | |
466 | * <p> | |
467 | * Example: | |
468 | * <ul> | |
469 | * <li><tt>1</tt>,<tt>NULL</tt>, <tt>3</tt> will become <tt>1</tt>, | |
470 | * <tt>""</tt>, <tt>3</tt></li> | |
471 | * <li><tt>1</tt>,<tt>NULL</tt>, <tt>NULL</tt> will become <tt>1</tt></li> | |
472 | * <li><tt>NULL</tt>, <tt>NULL</tt>, <tt>NULL</tt> will become an empty list | |
473 | * </li> | |
474 | * </ul> | |
475 | * | |
476 | * @param list | |
477 | * the input value | |
478 | * @param value | |
479 | * the value to insert | |
480 | * @param item | |
481 | * the position to insert it at | |
482 | * | |
483 | * @return the raw {@link String} value that correspond to it | |
484 | */ | |
485 | static public String fromList(String list, String value, int item) { | |
486 | return fromList(parseList(list, -1), value, item); | |
487 | } | |
488 | ||
b15a4985 | 489 | /** |
d5026c09 | 490 | * Escape the given value for list formating (no carets, no NEWLINES...). |
b15a4985 NR |
491 | * <p> |
492 | * You can unescape it with {@link BundleHelper#unescape(String)} | |
493 | * | |
494 | * @param value | |
495 | * the value to escape | |
496 | * | |
497 | * @return an escaped value that can unquoted by the reverse operation | |
498 | * {@link BundleHelper#unescape(String)} | |
499 | */ | |
500 | static public String escape(String value) { | |
501 | return '"' + value// | |
d5026c09 NR |
502 | .replace("^", "^^") // |
503 | .replace("\"", "^\"") // | |
504 | .replace("\n", "^\n") // | |
505 | .replace("\r", "^\r") // | |
b15a4985 NR |
506 | + '"'; |
507 | } | |
508 | ||
509 | /** | |
d5026c09 NR |
510 | * Unescape the given value for list formating (change ^n into NEWLINE and |
511 | * so on). | |
b15a4985 NR |
512 | * <p> |
513 | * You can escape it with {@link BundleHelper#escape(String)} | |
514 | * | |
515 | * @param value | |
516 | * the value to escape | |
517 | * | |
518 | * @return an unescaped value that can reverted by the reverse operation | |
519 | * {@link BundleHelper#escape(String)}, or NULL if it was badly | |
520 | * formated | |
521 | */ | |
522 | static public String unescape(String value) { | |
523 | if (value.length() < 2 || !value.startsWith("\"") | |
524 | || !value.endsWith("\"")) { | |
525 | // Bad format | |
526 | return null; | |
527 | } | |
528 | ||
0e2553f9 | 529 | value = value.substring(1, value.length() - 1); |
b15a4985 NR |
530 | |
531 | boolean prevIsBackslash = false; | |
532 | StringBuilder builder = new StringBuilder(); | |
533 | for (char car : value.toCharArray()) { | |
534 | if (prevIsBackslash) { | |
535 | switch (car) { | |
536 | case 'n': | |
537 | case 'N': | |
538 | builder.append('\n'); | |
539 | break; | |
540 | case 'r': | |
541 | case 'R': | |
542 | builder.append('\r'); | |
543 | break; | |
d5026c09 | 544 | default: // includes ^ and " |
b15a4985 NR |
545 | builder.append(car); |
546 | break; | |
547 | } | |
d5026c09 | 548 | prevIsBackslash = false; |
b15a4985 | 549 | } else { |
d5026c09 | 550 | if (car == '^') { |
b15a4985 NR |
551 | prevIsBackslash = true; |
552 | } else { | |
553 | builder.append(car); | |
554 | } | |
555 | } | |
556 | } | |
557 | ||
558 | if (prevIsBackslash) { | |
559 | // Bad format | |
560 | return null; | |
3bfc1d20 NR |
561 | } |
562 | ||
563 | return builder.toString(); | |
564 | } | |
d5026c09 NR |
565 | |
566 | /** | |
567 | * Retrieve the specific item in the given value, assuming it is an array. | |
568 | * | |
569 | * @param value | |
570 | * the value to look into | |
571 | * @param item | |
572 | * the item number to get for an array of values, or -1 for | |
573 | * non-arrays (in that case, simply return the value as-is) | |
574 | * | |
575 | * @return the value as-is for non arrays, the item <tt>item</tt> if found, | |
576 | * NULL if not | |
577 | */ | |
578 | static private String getItem(String value, int item) { | |
579 | if (item >= 0) { | |
580 | value = null; | |
581 | List<String> values = parseList(value, -1); | |
582 | if (values != null && item < values.size()) { | |
583 | value = values.get(item); | |
584 | } | |
585 | } | |
586 | ||
587 | return value; | |
588 | } | |
3bfc1d20 | 589 | } |