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 NR |
264 | /** |
265 | * The size of this raw list. | |
266 | * | |
267 | * @param raw | |
268 | * the raw list | |
269 | * | |
270 | * @return its size if it is a list, -1 if not | |
271 | */ | |
272 | static public int getListSize(String raw) { | |
273 | List<String> list = parseList(raw, -1); | |
274 | if (list == null) { | |
275 | return -1; | |
276 | } | |
277 | ||
278 | return list.size(); | |
279 | } | |
280 | ||
3bfc1d20 NR |
281 | /** |
282 | * Return a {@link String} representation of the given list of values. | |
283 | * <p> | |
284 | * The list of values is comma-separated and each value is surrounded by | |
d5026c09 | 285 | * double-quotes; caret (^) and double-quotes (") are escaped by a caret. |
3bfc1d20 NR |
286 | * |
287 | * @param str | |
288 | * the input value | |
d5026c09 NR |
289 | * @param item |
290 | * the item number to use for an array of values, or -1 for | |
291 | * non-arrays | |
292 | * | |
3bfc1d20 NR |
293 | * @return the raw {@link String} value that correspond to it |
294 | */ | |
d5026c09 | 295 | static public List<String> parseList(String str, int item) { |
3bfc1d20 NR |
296 | if (str == null) { |
297 | return null; | |
298 | } | |
d5026c09 NR |
299 | |
300 | if (item >= 0) { | |
301 | str = getItem(str, item); | |
302 | } | |
303 | ||
3bfc1d20 NR |
304 | List<String> list = new ArrayList<String>(); |
305 | try { | |
306 | boolean inQuote = false; | |
307 | boolean prevIsBackSlash = false; | |
308 | StringBuilder builder = new StringBuilder(); | |
309 | for (int i = 0; i < str.length(); i++) { | |
310 | char car = str.charAt(i); | |
311 | ||
312 | if (prevIsBackSlash) { | |
b15a4985 | 313 | // We don't process it here |
3bfc1d20 NR |
314 | builder.append(car); |
315 | prevIsBackSlash = false; | |
316 | } else { | |
317 | switch (car) { | |
318 | case '"': | |
b15a4985 NR |
319 | // We don't process it here |
320 | builder.append(car); | |
321 | ||
3bfc1d20 | 322 | if (inQuote) { |
b15a4985 | 323 | list.add(unescape(builder.toString())); |
3bfc1d20 NR |
324 | builder.setLength(0); |
325 | } | |
326 | ||
327 | inQuote = !inQuote; | |
328 | break; | |
d5026c09 | 329 | case '^': |
b15a4985 NR |
330 | // We don't process it here |
331 | builder.append(car); | |
3bfc1d20 NR |
332 | prevIsBackSlash = true; |
333 | break; | |
334 | case ' ': | |
335 | case '\n': | |
336 | case '\r': | |
337 | if (inQuote) { | |
338 | builder.append(car); | |
339 | } | |
340 | break; | |
341 | ||
342 | case ',': | |
343 | if (!inQuote) { | |
344 | break; | |
345 | } | |
346 | // continue to default | |
347 | default: | |
348 | if (!inQuote) { | |
349 | // Bad format! | |
350 | return null; | |
351 | } | |
352 | ||
353 | builder.append(car); | |
354 | break; | |
355 | } | |
356 | } | |
357 | } | |
358 | ||
359 | if (inQuote || prevIsBackSlash) { | |
360 | // Bad format! | |
361 | return null; | |
362 | } | |
363 | ||
364 | } catch (Exception e) { | |
365 | return null; | |
366 | } | |
367 | ||
368 | return list; | |
369 | } | |
370 | ||
371 | /** | |
372 | * Return a {@link String} representation of the given list of values. | |
d5026c09 NR |
373 | * <p> |
374 | * NULL will be assimilated to an empty {@link String}. | |
3bfc1d20 NR |
375 | * |
376 | * @param list | |
377 | * the input value | |
378 | * | |
379 | * @return the raw {@link String} value that correspond to it | |
380 | */ | |
381 | static public String fromList(List<String> list) { | |
382 | StringBuilder builder = new StringBuilder(); | |
383 | for (String item : list) { | |
384 | if (builder.length() > 0) { | |
385 | builder.append(", "); | |
386 | } | |
d5026c09 | 387 | builder.append(escape(item == null ? "" : item)); |
b15a4985 NR |
388 | } |
389 | ||
390 | return builder.toString(); | |
391 | } | |
392 | ||
393 | /** | |
d5026c09 | 394 | * Escape the given value for list formating (no carets, no NEWLINES...). |
b15a4985 NR |
395 | * <p> |
396 | * You can unescape it with {@link BundleHelper#unescape(String)} | |
397 | * | |
398 | * @param value | |
399 | * the value to escape | |
400 | * | |
401 | * @return an escaped value that can unquoted by the reverse operation | |
402 | * {@link BundleHelper#unescape(String)} | |
403 | */ | |
404 | static public String escape(String value) { | |
405 | return '"' + value// | |
d5026c09 NR |
406 | .replace("^", "^^") // |
407 | .replace("\"", "^\"") // | |
408 | .replace("\n", "^\n") // | |
409 | .replace("\r", "^\r") // | |
b15a4985 NR |
410 | + '"'; |
411 | } | |
412 | ||
413 | /** | |
d5026c09 NR |
414 | * Unescape the given value for list formating (change ^n into NEWLINE and |
415 | * so on). | |
b15a4985 NR |
416 | * <p> |
417 | * You can escape it with {@link BundleHelper#escape(String)} | |
418 | * | |
419 | * @param value | |
420 | * the value to escape | |
421 | * | |
422 | * @return an unescaped value that can reverted by the reverse operation | |
423 | * {@link BundleHelper#escape(String)}, or NULL if it was badly | |
424 | * formated | |
425 | */ | |
426 | static public String unescape(String value) { | |
427 | if (value.length() < 2 || !value.startsWith("\"") | |
428 | || !value.endsWith("\"")) { | |
429 | // Bad format | |
430 | return null; | |
431 | } | |
432 | ||
0e2553f9 | 433 | value = value.substring(1, value.length() - 1); |
b15a4985 NR |
434 | |
435 | boolean prevIsBackslash = false; | |
436 | StringBuilder builder = new StringBuilder(); | |
437 | for (char car : value.toCharArray()) { | |
438 | if (prevIsBackslash) { | |
439 | switch (car) { | |
440 | case 'n': | |
441 | case 'N': | |
442 | builder.append('\n'); | |
443 | break; | |
444 | case 'r': | |
445 | case 'R': | |
446 | builder.append('\r'); | |
447 | break; | |
d5026c09 | 448 | default: // includes ^ and " |
b15a4985 NR |
449 | builder.append(car); |
450 | break; | |
451 | } | |
d5026c09 | 452 | prevIsBackslash = false; |
b15a4985 | 453 | } else { |
d5026c09 | 454 | if (car == '^') { |
b15a4985 NR |
455 | prevIsBackslash = true; |
456 | } else { | |
457 | builder.append(car); | |
458 | } | |
459 | } | |
460 | } | |
461 | ||
462 | if (prevIsBackslash) { | |
463 | // Bad format | |
464 | return null; | |
3bfc1d20 NR |
465 | } |
466 | ||
467 | return builder.toString(); | |
468 | } | |
d5026c09 NR |
469 | |
470 | /** | |
471 | * Retrieve the specific item in the given value, assuming it is an array. | |
472 | * | |
473 | * @param value | |
474 | * the value to look into | |
475 | * @param item | |
476 | * the item number to get for an array of values, or -1 for | |
477 | * non-arrays (in that case, simply return the value as-is) | |
478 | * | |
479 | * @return the value as-is for non arrays, the item <tt>item</tt> if found, | |
480 | * NULL if not | |
481 | */ | |
482 | static private String getItem(String value, int item) { | |
483 | if (item >= 0) { | |
484 | value = null; | |
485 | List<String> values = parseList(value, -1); | |
486 | if (values != null && item < values.size()) { | |
487 | value = values.get(item); | |
488 | } | |
489 | } | |
490 | ||
491 | return value; | |
492 | } | |
3bfc1d20 | 493 | } |