blob: 1b7881452ae1391e045c0ce1a1707b4daeffa956 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska24c849c2014-10-27 09:53:05 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska24c849c2014-10-27 09:53:05 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska24c849c2014-10-27 09:53:05 -070015 */
tom5f38b3a2014-08-27 23:50:54 -070016package org.onlab.util;
17
Madan Jampani2bfa94c2015-04-11 05:03:49 -070018import static java.nio.file.Files.delete;
19import static java.nio.file.Files.walkFileTree;
20import static org.onlab.util.GroupedThreadFactory.groupedThreadFactory;
21import static org.slf4j.LoggerFactory.getLogger;
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080022
tom53efab52014-10-07 17:43:48 -070023import java.io.BufferedReader;
24import java.io.File;
Ray Milkey705d9bc2014-11-18 08:19:00 -080025import java.io.FileInputStream;
tom53efab52014-10-07 17:43:48 -070026import java.io.IOException;
Ray Milkey705d9bc2014-11-18 08:19:00 -080027import java.io.InputStreamReader;
Madan Jampani27b69c62015-05-15 15:49:02 -070028import java.nio.ByteBuffer;
Ray Milkey705d9bc2014-11-18 08:19:00 -080029import java.nio.charset.StandardCharsets;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080030import java.nio.file.FileVisitResult;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080031import java.nio.file.Files;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080032import java.nio.file.Path;
33import java.nio.file.Paths;
34import java.nio.file.SimpleFileVisitor;
Thomas Vachuska90b453f2015-01-30 18:57:14 -080035import java.nio.file.StandardCopyOption;
Thomas Vachuska02aeb032015-01-06 22:36:30 -080036import java.nio.file.attribute.BasicFileAttributes;
tom53efab52014-10-07 17:43:48 -070037import java.util.ArrayList;
Madan Jampani27b69c62015-05-15 15:49:02 -070038import java.util.Arrays;
Brian O'Connore2eac102015-02-12 18:30:22 -080039import java.util.Collection;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070040import java.util.Dictionary;
tom53efab52014-10-07 17:43:48 -070041import java.util.List;
Thomas Vachuskaadba1522015-06-04 15:08:30 -070042import java.util.Random;
Madan Jampani27b69c62015-05-15 15:49:02 -070043import java.util.concurrent.CompletableFuture;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070044import java.util.concurrent.ExecutionException;
45import java.util.concurrent.Future;
tom5f38b3a2014-08-27 23:50:54 -070046import java.util.concurrent.ThreadFactory;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070047import java.util.concurrent.TimeUnit;
48import java.util.concurrent.TimeoutException;
Madan Jampania29c6772015-08-17 13:17:07 -070049import java.util.function.Function;
50import java.util.function.Supplier;
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -070051import java.util.stream.Stream;
52import java.util.stream.StreamSupport;
tom5f38b3a2014-08-27 23:50:54 -070053
Madan Jampani2bfa94c2015-04-11 05:03:49 -070054import org.slf4j.Logger;
55
56import com.google.common.base.Strings;
57import com.google.common.primitives.UnsignedLongs;
58import com.google.common.util.concurrent.ThreadFactoryBuilder;
Ray Milkey705d9bc2014-11-18 08:19:00 -080059
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080060/**
61 * Miscellaneous utility methods.
62 */
tom5f38b3a2014-08-27 23:50:54 -070063public abstract class Tools {
64
65 private Tools() {
66 }
67
Thomas Vachuska02aeb032015-01-06 22:36:30 -080068 private static final Logger log = getLogger(Tools.class);
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080069
Thomas Vachuskaadba1522015-06-04 15:08:30 -070070 private static Random random = new Random();
71
tom5f38b3a2014-08-27 23:50:54 -070072 /**
73 * Returns a thread factory that produces threads named according to the
74 * supplied name pattern.
75 *
76 * @param pattern name pattern
77 * @return thread factory
78 */
79 public static ThreadFactory namedThreads(String pattern) {
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080080 return new ThreadFactoryBuilder()
81 .setNameFormat(pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080082 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
83 .build();
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080084 }
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080085
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080086 /**
87 * Returns a thread factory that produces threads named according to the
88 * supplied name pattern and from the specified thread-group. The thread
89 * group name is expected to be specified in slash-delimited format, e.g.
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080090 * {@code onos/intent}. The thread names will be produced by converting
91 * the thread group name into dash-delimited format and pre-pended to the
92 * specified pattern.
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080093 *
94 * @param groupName group name in slash-delimited format to indicate hierarchy
95 * @param pattern name pattern
96 * @return thread factory
97 */
98 public static ThreadFactory groupedThreads(String groupName, String pattern) {
99 return new ThreadFactoryBuilder()
100 .setThreadFactory(groupedThreadFactory(groupName))
Thomas Vachuskac13b90a2015-02-18 18:19:55 -0800101 .setNameFormat(groupName.replace(GroupedThreadFactory.DELIMITER, "-") + "-" + pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -0800102 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
103 .build();
tom5f38b3a2014-08-27 23:50:54 -0700104 }
105
tom782a7cf2014-09-11 23:58:38 -0700106 /**
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800107 * Returns a thread factory that produces threads with MIN_PRIORITY.
108 *
109 * @param factory backing ThreadFactory
110 * @return thread factory
111 */
112 public static ThreadFactory minPriority(ThreadFactory factory) {
113 return new ThreadFactoryBuilder()
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800114 .setThreadFactory(factory)
115 .setPriority(Thread.MIN_PRIORITY)
116 .build();
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800117 }
118
119 /**
Brian O'Connore2eac102015-02-12 18:30:22 -0800120 * Returns true if the collection is null or is empty.
121 *
122 * @param collection collection to test
123 * @return true if null or empty; false otherwise
124 */
125 public static boolean isNullOrEmpty(Collection collection) {
126 return collection == null || collection.isEmpty();
127 }
128
129 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700130 * Returns the specified item if that item is not null; otherwise throws
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700131 * not found exception.
132 *
133 * @param item item to check
134 * @param message not found message
135 * @param <T> item type
136 * @return item if not null
137 * @throws org.onlab.util.ItemNotFoundException if item is null
138 */
139 public static <T> T nullIsNotFound(T item, String message) {
140 if (item == null) {
141 throw new ItemNotFoundException(message);
142 }
143 return item;
144 }
145
146 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700147 * Returns the specified item if that item is not null; otherwise throws
148 * bad argument exception.
149 *
150 * @param item item to check
151 * @param message not found message
152 * @param <T> item type
153 * @return item if not null
154 * @throws IllegalArgumentException if item is null
155 */
156 public static <T> T nullIsIllegal(T item, String message) {
157 if (item == null) {
158 throw new IllegalArgumentException(message);
159 }
160 return item;
161 }
162
163 /**
tom782a7cf2014-09-11 23:58:38 -0700164 * Converts a string from hex to long.
165 *
166 * @param string hex number in string form; sans 0x
167 * @return long value
168 */
169 public static long fromHex(String string) {
170 return UnsignedLongs.parseUnsignedLong(string, 16);
171 }
172
173 /**
174 * Converts a long value to hex string; 16 wide and sans 0x.
175 *
176 * @param value long value
177 * @return hex string
178 */
179 public static String toHex(long value) {
180 return Strings.padStart(UnsignedLongs.toString(value, 16), 16, '0');
181 }
182
183 /**
184 * Converts a long value to hex string; 16 wide and sans 0x.
185 *
186 * @param value long value
187 * @param width string width; zero padded
188 * @return hex string
189 */
190 public static String toHex(long value, int width) {
191 return Strings.padStart(UnsignedLongs.toString(value, 16), width, '0');
192 }
tomf110fff2014-09-26 00:38:18 -0700193
194 /**
Madan Jampani9eb55d12015-08-14 07:47:56 -0700195 * Returns a copy of the input byte array.
196 *
197 * @param original input
198 * @return copy of original
199 */
200 public static byte[] copyOf(byte[] original) {
201 return Arrays.copyOf(original, original.length);
202 }
203
204 /**
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700205 * Get property as a string value.
206 *
207 * @param properties properties to be looked up
208 * @param propertyName the name of the property to look up
209 * @return value when the propertyName is defined or return null
210 */
211 public static String get(Dictionary<?, ?> properties, String propertyName) {
212 Object v = properties.get(propertyName);
213 String s = (v instanceof String) ? (String) v :
214 v != null ? v.toString() : null;
215 return Strings.isNullOrEmpty(s) ? null : s.trim();
216 }
217
218 /**
tomf110fff2014-09-26 00:38:18 -0700219 * Suspends the current thread for a specified number of millis.
220 *
221 * @param ms number of millis
222 */
223 public static void delay(int ms) {
224 try {
225 Thread.sleep(ms);
226 } catch (InterruptedException e) {
227 throw new RuntimeException("Interrupted", e);
228 }
229 }
230
tom53efab52014-10-07 17:43:48 -0700231 /**
Madan Jampania29c6772015-08-17 13:17:07 -0700232 * Returns a function that retries execution on failure.
233 * @param base base function
234 * @param exceptionClass type of exception for which to retry
235 * @param maxRetries max number of retries before giving up
236 * @param maxDelayBetweenRetries max delay between successive retries. The actual delay is randomly picked from
237 * the interval (0, maxDelayBetweenRetries]
238 * @return function
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700239 * @param <U> type of function input
240 * @param <V> type of function output
Madan Jampania29c6772015-08-17 13:17:07 -0700241 */
242 public static <U, V> Function<U, V> retryable(Function<U, V> base,
243 Class<? extends Throwable> exceptionClass,
244 int maxRetries,
245 int maxDelayBetweenRetries) {
246 return new RetryingFunction<>(base, exceptionClass, maxRetries, maxDelayBetweenRetries);
247 }
248
249 /**
250 * Returns a Supplier that retries execution on failure.
251 * @param base base supplier
252 * @param exceptionClass type of exception for which to retry
253 * @param maxRetries max number of retries before giving up
254 * @param maxDelayBetweenRetries max delay between successive retries. The actual delay is randomly picked from
255 * the interval (0, maxDelayBetweenRetries]
256 * @return supplier
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700257 * @param <V> type of supplied result
Madan Jampania29c6772015-08-17 13:17:07 -0700258 */
259 public static <V> Supplier<V> retryable(Supplier<V> base,
260 Class<? extends Throwable> exceptionClass,
261 int maxRetries,
262 int maxDelayBetweenRetries) {
263 return () -> new RetryingFunction<>(v -> base.get(),
264 exceptionClass,
265 maxRetries,
266 maxDelayBetweenRetries).apply(null);
267 }
268
269 /**
Thomas Vachuskaadba1522015-06-04 15:08:30 -0700270 * Suspends the current thread for a random number of millis between 0 and
271 * the indicated limit.
272 *
273 * @param ms max number of millis
274 */
275 public static void randomDelay(int ms) {
276 try {
277 Thread.sleep(random.nextInt(ms));
278 } catch (InterruptedException e) {
279 throw new RuntimeException("Interrupted", e);
280 }
281 }
282
283 /**
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700284 * Suspends the current thread for a specified number of millis and nanos.
285 *
286 * @param ms number of millis
287 * @param nanos number of nanos
288 */
289 public static void delay(int ms, int nanos) {
290 try {
291 Thread.sleep(ms, nanos);
292 } catch (InterruptedException e) {
293 throw new RuntimeException("Interrupted", e);
294 }
295 }
296
297 /**
tom53efab52014-10-07 17:43:48 -0700298 * Slurps the contents of a file into a list of strings, one per line.
299 *
300 * @param path file path
301 * @return file contents
HIGUCHI Yuta3b3bd1e2015-09-22 16:39:33 -0700302 * @deprecated in Emu release
tom53efab52014-10-07 17:43:48 -0700303 */
HIGUCHI Yuta3b3bd1e2015-09-22 16:39:33 -0700304 @Deprecated
tom53efab52014-10-07 17:43:48 -0700305 public static List<String> slurp(File path) {
HIGUCHI Yutacf4d6172015-09-22 16:39:33 -0700306 try (
Ray Milkey705d9bc2014-11-18 08:19:00 -0800307 BufferedReader br = new BufferedReader(
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800308 new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8));
HIGUCHI Yutacf4d6172015-09-22 16:39:33 -0700309 ) {
tom53efab52014-10-07 17:43:48 -0700310 List<String> lines = new ArrayList<>();
311 String line;
312 while ((line = br.readLine()) != null) {
313 lines.add(line);
314 }
315 return lines;
316
317 } catch (IOException e) {
318 return null;
319 }
320 }
321
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800322 /**
323 * Purges the specified directory path.&nbsp;Use with great caution since
324 * no attempt is made to check for symbolic links, which could result in
325 * deletion of unintended files.
326 *
327 * @param path directory to be removed
328 * @throws java.io.IOException if unable to remove contents
329 */
330 public static void removeDirectory(String path) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800331 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700332 File dir = new File(path);
333 if (dir.exists() && dir.isDirectory()) {
334 walkFileTree(Paths.get(path), visitor);
335 if (visitor.exception != null) {
336 throw visitor.exception;
337 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800338 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800339 }
340
341 /**
342 * Purges the specified directory path.&nbsp;Use with great caution since
343 * no attempt is made to check for symbolic links, which could result in
344 * deletion of unintended files.
345 *
346 * @param dir directory to be removed
347 * @throws java.io.IOException if unable to remove contents
348 */
349 public static void removeDirectory(File dir) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800350 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700351 if (dir.exists() && dir.isDirectory()) {
352 walkFileTree(Paths.get(dir.getAbsolutePath()), visitor);
353 if (visitor.exception != null) {
354 throw visitor.exception;
355 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800356 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800357 }
358
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800359 // Auxiliary path visitor for recursive directory structure removal.
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800360 private static class DirectoryDeleter extends SimpleFileVisitor<Path> {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800361
362 private IOException exception;
363
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800364 @Override
365 public FileVisitResult visitFile(Path file, BasicFileAttributes attributes)
366 throws IOException {
367 if (attributes.isRegularFile()) {
368 delete(file);
369 }
370 return FileVisitResult.CONTINUE;
371 }
372
373 @Override
374 public FileVisitResult postVisitDirectory(Path directory, IOException ioe)
375 throws IOException {
376 delete(directory);
377 return FileVisitResult.CONTINUE;
378 }
379
380 @Override
381 public FileVisitResult visitFileFailed(Path file, IOException ioe)
382 throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800383 this.exception = ioe;
384 return FileVisitResult.TERMINATE;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800385 }
386 }
387
Madan Jampani30a57f82015-03-02 12:19:41 -0800388 /**
389 * Returns a human friendly time ago string for a specified system time.
Thomas Vachuska480adad2015-03-06 10:27:09 -0800390 *
Madan Jampani30a57f82015-03-02 12:19:41 -0800391 * @param unixTime system time in millis
392 * @return human friendly time ago
393 */
394 public static String timeAgo(long unixTime) {
395 long deltaMillis = System.currentTimeMillis() - unixTime;
396 long secondsSince = (long) (deltaMillis / 1000.0);
397 long minsSince = (long) (deltaMillis / (1000.0 * 60));
398 long hoursSince = (long) (deltaMillis / (1000.0 * 60 * 60));
399 long daysSince = (long) (deltaMillis / (1000.0 * 60 * 60 * 24));
400 if (daysSince > 0) {
401 return String.format("%dd ago", daysSince);
402 } else if (hoursSince > 0) {
403 return String.format("%dh ago", hoursSince);
404 } else if (minsSince > 0) {
405 return String.format("%dm ago", minsSince);
406 } else if (secondsSince > 0) {
407 return String.format("%ds ago", secondsSince);
408 } else {
409 return "just now";
410 }
411 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800412
413 /**
414 * Copies the specified directory path.&nbsp;Use with great caution since
415 * no attempt is made to check for symbolic links, which could result in
416 * copy of unintended files.
417 *
418 * @param src directory to be copied
419 * @param dst destination directory to be removed
420 * @throws java.io.IOException if unable to remove contents
421 */
422 public static void copyDirectory(String src, String dst) throws IOException {
423 walkFileTree(Paths.get(src), new DirectoryCopier(src, dst));
424 }
425
426 /**
427 * Copies the specified directory path.&nbsp;Use with great caution since
428 * no attempt is made to check for symbolic links, which could result in
429 * copy of unintended files.
430 *
431 * @param src directory to be copied
432 * @param dst destination directory to be removed
433 * @throws java.io.IOException if unable to remove contents
434 */
435 public static void copyDirectory(File src, File dst) throws IOException {
436 walkFileTree(Paths.get(src.getAbsolutePath()),
437 new DirectoryCopier(src.getAbsolutePath(),
438 dst.getAbsolutePath()));
439 }
440
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700441 /**
442 * Returns the future value when complete or if future
443 * completes exceptionally returns the defaultValue.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700444 *
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700445 * @param future future
446 * @param defaultValue default value
447 * @param <T> future value type
448 * @return future value when complete or if future
449 * completes exceptionally returns the defaultValue.
450 */
451 public static <T> T futureGetOrElse(Future<T> future, T defaultValue) {
452 try {
453 return future.get();
454 } catch (InterruptedException e) {
455 Thread.currentThread().interrupt();
456 return defaultValue;
457 } catch (ExecutionException e) {
458 return defaultValue;
459 }
460 }
461
462 /**
463 * Returns the future value when complete or if future
464 * completes exceptionally returns the defaultValue.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700465 *
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700466 * @param future future
467 * @param timeout time to wait for successful completion
468 * @param timeUnit time unit
469 * @param defaultValue default value
470 * @param <T> future value type
471 * @return future value when complete or if future
472 * completes exceptionally returns the defaultValue.
473 */
474 public static <T> T futureGetOrElse(Future<T> future,
475 long timeout,
476 TimeUnit timeUnit,
477 T defaultValue) {
478 try {
479 return future.get(timeout, timeUnit);
480 } catch (InterruptedException e) {
481 Thread.currentThread().interrupt();
482 return defaultValue;
483 } catch (ExecutionException | TimeoutException e) {
484 return defaultValue;
485 }
486 }
487
Madan Jampani27b69c62015-05-15 15:49:02 -0700488 /**
489 * Returns a future that is completed exceptionally.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700490 *
Madan Jampani27b69c62015-05-15 15:49:02 -0700491 * @param t exception
492 * @param <T> future value type
493 * @return future
494 */
495 public static <T> CompletableFuture<T> exceptionalFuture(Throwable t) {
496 CompletableFuture<T> future = new CompletableFuture<>();
497 future.completeExceptionally(t);
498 return future;
499 }
500
501 /**
502 * Returns the contents of {@code ByteBuffer} as byte array.
503 * <p>
504 * WARNING: There is a performance cost due to array copy
505 * when using this method.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700506 *
Madan Jampani27b69c62015-05-15 15:49:02 -0700507 * @param buffer byte buffer
508 * @return byte array containing the byte buffer contents
509 */
510 public static byte[] byteBuffertoArray(ByteBuffer buffer) {
511 int length = buffer.remaining();
512 if (buffer.hasArray()) {
513 int offset = buffer.arrayOffset() + buffer.position();
514 return Arrays.copyOfRange(buffer.array(), offset, offset + length);
515 }
516 byte[] bytes = new byte[length];
517 buffer.duplicate().get(bytes);
518 return bytes;
519 }
520
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700521 /**
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700522 * Converts an iterable to a stream.
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700523 *
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700524 * @param it iterable to convert
525 * @param <T> type if item
526 * @return iterable as a stream
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700527 */
528 public static <T> Stream<T> stream(Iterable<T> it) {
529 return StreamSupport.stream(it.spliterator(), false);
530 }
531
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800532 // Auxiliary path visitor for recursive directory structure copying.
533 private static class DirectoryCopier extends SimpleFileVisitor<Path> {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800534 private Path src;
535 private Path dst;
536 private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING;
537
538 DirectoryCopier(String src, String dst) {
539 this.src = Paths.get(src);
540 this.dst = Paths.get(dst);
541 }
542
543 @Override
544 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
545 Path targetPath = dst.resolve(src.relativize(dir));
546 if (!Files.exists(targetPath)) {
547 Files.createDirectory(targetPath);
548 }
549 return FileVisitResult.CONTINUE;
550 }
551
552 @Override
553 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
554 Files.copy(file, dst.resolve(src.relativize(file)), copyOption);
555 return FileVisitResult.CONTINUE;
556 }
557 }
558
tom5f38b3a2014-08-27 23:50:54 -0700559}