IOUtils: new openResource with anchor class
[fanfix.git] / resources / BundleHelper.java
CommitLineData
3bfc1d20
NR
1package be.nikiroo.utils.resources;
2
3import java.util.ArrayList;
4import 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 */
12class 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}