1 package com.acumenvelocity.ath.common;
2
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.FileOutputStream;
6 import java.io.IOException;
7 import java.io.UncheckedIOException;
8 import java.lang.reflect.Method;
9 import java.net.URI;
10 import java.net.URLEncoder;
11 import java.nio.file.Files;
12 import java.nio.file.Path;
13 import java.nio.file.Paths;
14 import java.nio.file.attribute.PosixFilePermissions;
15 import java.text.DateFormat;
16 import java.time.Instant;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.Date;
20 import java.util.List;
21 import java.util.Objects;
22 import java.util.Optional;
23 import java.util.UUID;
24 import java.util.zip.ZipEntry;
25 import java.util.zip.ZipInputStream;
26 import java.util.zip.ZipOutputStream;
27
28 import com.fasterxml.jackson.databind.JsonNode;
29
30 import net.sf.okapi.common.BOMAwareInputStream;
31 import net.sf.okapi.common.StreamUtil;
32 import net.sf.okapi.common.Util;
33
34
35
36
37
38
39
40
41
42 public class AthUtil {
43
44
45
46
47
48
49
50
51
52 public static <T> T fallback(T val, T defVal) {
53 return Optional.ofNullable(val).orElse(defVal);
54 }
55
56
57
58
59
60
61
62
63 public static String safeToStr(Object val, String defVal) {
64 return val == null ? defVal : val.toString();
65 }
66
67
68
69
70
71
72
73 public static String safeToStr(String val, String defVal) {
74 return Util.isEmpty(val) ? defVal : val;
75 }
76
77
78
79
80
81
82
83
84
85 public static Integer safeToInt(String val, int defVal) {
86 try {
87 return Integer.valueOf(val);
88
89 } catch (Exception e) {
90 return defVal;
91 }
92 }
93
94 public static UUID safeToUuid(String val, UUID defVal) {
95 try {
96 return UUID.fromString(val);
97
98 } catch (Exception e) {
99 return defVal;
100 }
101 }
102
103 public static List<UUID> safeToUuidList(Object val, List<UUID> defVal) {
104 if (val == null) {
105 return defVal != null ? defVal : Collections.emptyList();
106 }
107
108 List<UUID> result = new ArrayList<>();
109
110 if (val instanceof List) {
111
112 for (Object item : (List<?>) val) {
113 if (item != null) {
114 UUID uuid = safeToUuid(item.toString(), null);
115
116 if (uuid != null) {
117 result.add(uuid);
118 }
119 }
120 }
121 } else if (val instanceof String) {
122
123 String[] uuidStrings = val.toString().split(",");
124
125 for (String uuidStr : uuidStrings) {
126 uuidStr = uuidStr.trim();
127
128 if (!uuidStr.isEmpty()) {
129 UUID uuid = safeToUuid(uuidStr, null);
130
131 if (uuid != null) {
132 result.add(uuid);
133 }
134 }
135 }
136 }
137
138 return result.isEmpty() && defVal != null ? defVal : result;
139 }
140
141 public static <E extends Enum<E>> E safeToEnum(String val, Class<E> enumClass, E defVal) {
142 if (val == null) {
143 return defVal;
144 }
145
146 try {
147
148 return Enum.valueOf(enumClass, val);
149
150 } catch (IllegalArgumentException e) {
151
152 try {
153 Method fromValue = enumClass.getMethod("fromValue", String.class);
154 return enumClass.cast(fromValue.invoke(null, val));
155
156 } catch (Exception ex) {
157 return defVal;
158 }
159 }
160 }
161
162
163
164
165
166
167
168
169
170 public static <E> boolean safeAddToList(List<E> list, E elem) {
171 if (elem == null) {
172 return false;
173 }
174 return list.add(elem);
175 }
176
177
178
179
180
181
182
183
184
185 public static String lastChars(String st, int numChars) {
186 if (Util.isEmpty(st) || st.length() < numChars) {
187 return "";
188 }
189 return st.substring(st.length() - numChars);
190 }
191
192
193
194
195
196
197
198
199
200
201
202
203 public static <T> T clone(T obj, Class<T> cls) {
204 return JacksonUtil.fromJson(JacksonUtil.toJson(obj, false), cls);
205 }
206
207 public static <T> List<T> removeNulls(List<T> list) {
208 list.removeIf(Objects::isNull);
209 return list;
210 }
211
212 public static String fileAsString(final File file) throws IOException {
213 try (final BOMAwareInputStream bis = new BOMAwareInputStream(new FileInputStream(file),
214 "UTF-8")) {
215 return StreamUtil.streamAsString(bis, bis.detectEncoding());
216 }
217 }
218
219
220
221
222
223 public static <T> T safeFromJsonNode(JsonNode node, Class<T> clazz, T defVal) {
224 if (node == null) {
225 return defVal;
226 }
227 try {
228 return JacksonUtil.fromJsonNode(node, clazz);
229 } catch (Exception e) {
230 return defVal;
231 }
232 }
233
234
235
236
237
238 public static Date safeToDate(Object val, Date defVal) {
239 if (val == null) {
240 return defVal;
241 }
242 try {
243 return (Date) val;
244 } catch (ClassCastException e) {
245 return defVal;
246 }
247 }
248
249
250
251
252
253 public static Long safeToLong(String val, Long defVal) {
254 if (val == null) {
255 return defVal;
256 }
257 try {
258 return Long.parseLong(val);
259 } catch (NumberFormatException e) {
260 return defVal;
261 }
262 }
263
264
265
266
267
268 public static Float safeToFloat(Object val, Float defVal) {
269 if (val == null) {
270 return defVal;
271 }
272 try {
273 if (val instanceof Float) {
274 return (Float) val;
275 }
276 return Float.parseFloat(val.toString());
277 } catch (Exception e) {
278 return defVal;
279 }
280 }
281
282
283
284
285
286 public static Double safeToDouble(Object val, Double defVal) {
287 if (val == null) {
288 return defVal;
289 }
290
291 try {
292 if (val instanceof Double) {
293 return (Double) val;
294 }
295
296 return Double.parseDouble(val.toString());
297
298 } catch (Exception e) {
299 return defVal;
300 }
301 }
302
303
304
305
306
307 public static Date safeToDateFromIso(String isoDateStr, Date defVal) {
308 if (Util.isEmpty(isoDateStr)) {
309 return defVal;
310 }
311 try {
312 return Date.from(Instant.parse(isoDateStr));
313 } catch (Exception e) {
314 return defVal;
315 }
316 }
317
318 public static String dateToString(DateFormat dateFormat, Date date) {
319 return dateFormat.format(date);
320 }
321
322 public static File createTempFile() {
323 return createTempFile(Const.TEMP_PREFIX, null);
324 }
325
326 public static File createTempFile(String suffix) {
327 return createTempFile(Const.TEMP_PREFIX, suffix);
328 }
329
330 public static File createTempFile(String prefix, String suffix) {
331 File file = null;
332
333 try {
334 file = File.createTempFile(prefix, suffix);
335 file.deleteOnExit();
336
337
338 try {
339 Files.setPosixFilePermissions(file.toPath(),
340 PosixFilePermissions.fromString("rw-------"));
341
342 } catch (UnsupportedOperationException e) {
343
344 file.setReadable(true);
345 file.setWritable(true);
346 }
347
348 return file;
349
350 } catch (Exception e) {
351 Log.warn(AthUtil.class, "Cannot create a temp file: {}", e.getMessage());
352 return null;
353 }
354 }
355
356
357
358
359
360 public static URI toURI(String uriString) {
361 if (Util.isEmpty(uriString)) {
362 return null;
363 }
364
365 try {
366 String trimmed = uriString.trim();
367
368
369 try {
370 return URI.create(trimmed);
371
372 } catch (IllegalArgumentException e) {
373
374 }
375
376
377 int schemeEnd = trimmed.indexOf("://");
378
379 if (schemeEnd == -1) {
380
381 return URI.create(encodePath(trimmed));
382 }
383
384 String scheme = trimmed.substring(0, schemeEnd);
385 String rest = trimmed.substring(schemeEnd + 3);
386
387
388 int pathStart = rest.indexOf('/');
389
390 if (pathStart == -1) {
391
392 return URI.create(trimmed);
393 }
394
395 String authority = rest.substring(0, pathStart);
396 String pathAndQuery = rest.substring(pathStart + 1);
397
398
399 int queryStart = pathAndQuery.indexOf('?');
400 int fragmentStart = pathAndQuery.indexOf('#');
401
402 String path;
403 String suffix = "";
404
405 if (queryStart != -1) {
406 path = pathAndQuery.substring(0, queryStart);
407 suffix = pathAndQuery.substring(queryStart);
408
409 } else if (fragmentStart != -1) {
410 path = pathAndQuery.substring(0, fragmentStart);
411 suffix = pathAndQuery.substring(fragmentStart);
412
413 } else {
414 path = pathAndQuery;
415 }
416
417
418 String encodedPath = encodePath(path);
419
420 return URI.create(scheme + "://" + authority + "/" + encodedPath + suffix);
421
422 } catch (Exception e) {
423 return null;
424 }
425 }
426
427
428
429
430 private static String encodePath(String path) throws Exception {
431 if (path.isEmpty()) {
432 return path;
433 }
434
435 String[] segments = path.split("/", -1);
436 StringBuilder encoded = new StringBuilder();
437
438 for (int i = 0; i < segments.length; i++) {
439 if (i > 0)
440 encoded.append("/");
441
442 if (!segments[i].isEmpty()) {
443 encoded.append(URLEncoder.encode(segments[i], "UTF-8")
444 .replace("+", "%20"));
445 }
446 }
447
448 return encoded.toString();
449 }
450
451
452
453
454
455
456
457
458 public static String extractLastSection(String path) {
459 Objects.requireNonNull(path, "Path cannot be null");
460
461 int lastSlashIndex = path.lastIndexOf('/');
462
463 if (lastSlashIndex == -1) {
464 return "";
465 }
466
467 return path.substring(lastSlashIndex + 1);
468 }
469
470
471
472
473
474
475
476
477 public static String extractLastSectionOrDefault(String path, String defaultValue) {
478 if (path == null || path.isEmpty()) {
479 return defaultValue;
480 }
481
482 int lastSlashIndex = path.lastIndexOf('/');
483 if (lastSlashIndex == -1 || lastSlashIndex == path.length() - 1) {
484 return defaultValue;
485 }
486
487 return path.substring(lastSlashIndex + 1);
488 }
489
490 public static String nullIfEmpty(JsonNode node) {
491 if (node == null || node.isNull() || node.asText("").trim().isEmpty()) {
492 return null;
493 }
494 return node.asText();
495 }
496
497 public static Integer safeToInt(JsonNode node, Integer defaultValue) {
498 if (node == null || node.isNull() || !node.canConvertToInt()) {
499 return defaultValue;
500 }
501 return node.asInt();
502 }
503
504 public static UUID safeToUuid(JsonNode node, UUID defaultValue) {
505 if (node == null || node.isNull()) {
506 return defaultValue;
507 }
508 String text = node.asText(null);
509 return safeToUuid(text, defaultValue);
510 }
511
512 public static void deleteFile(File file) {
513 if (file != null && file.exists()) {
514 if (!file.delete()) {
515 file.deleteOnExit();
516 }
517 }
518 }
519
520 public static void deleteDirectory(File dir) {
521 File[] files = dir.listFiles();
522
523 if (files != null) {
524 for (File file : files) {
525 if (file.isDirectory()) {
526 deleteDirectory(file);
527
528 } else {
529 file.delete();
530 }
531 }
532 }
533
534 dir.delete();
535 }
536
537 public static void zip(String sourceDirPath, String zipFilePath) throws IOException {
538 Path source = Paths.get(sourceDirPath);
539
540 try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFilePath))) {
541 Files.walk(source).filter(path -> !Files.isDirectory(path)).forEach(path -> {
542 ZipEntry entry = new ZipEntry(source.relativize(path).toString().replace("\\", "/"));
543
544 try {
545 zos.putNextEntry(entry);
546 Files.copy(path, zos);
547 zos.closeEntry();
548
549 } catch (IOException e) {
550 throw new UncheckedIOException(e);
551 }
552 });
553 }
554 }
555
556 public static void unzip(String zipFilePath, String destDir) throws IOException {
557 File dir = new File(destDir);
558
559 if (!dir.exists()) {
560 dir.mkdirs();
561 }
562
563 try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath))) {
564 ZipEntry entry;
565
566 while ((entry = zis.getNextEntry()) != null) {
567 File file = new File(destDir, entry.getName());
568
569 if (entry.isDirectory()) {
570 file.mkdirs();
571
572 } else {
573 file.getParentFile().mkdirs();
574
575 try (FileOutputStream fos = new FileOutputStream(file)) {
576 byte[] buffer = new byte[4096];
577 int len;
578
579 while ((len = zis.read(buffer)) > 0) {
580 fos.write(buffer, 0, len);
581 }
582 }
583 }
584
585 zis.closeEntry();
586 }
587 }
588 }
589 }