blob: ffefbfd47cb18944f75f0eab4668ce8c584e67dc [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;
Ray Milkey36992c82015-11-17 13:31:15 -080043import java.util.Set;
Madan Jampani27b69c62015-05-15 15:49:02 -070044import java.util.concurrent.CompletableFuture;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070045import java.util.concurrent.ExecutionException;
46import java.util.concurrent.Future;
tom5f38b3a2014-08-27 23:50:54 -070047import java.util.concurrent.ThreadFactory;
Madan Jampani2bfa94c2015-04-11 05:03:49 -070048import java.util.concurrent.TimeUnit;
49import java.util.concurrent.TimeoutException;
Madan Jampania29c6772015-08-17 13:17:07 -070050import java.util.function.Function;
51import java.util.function.Supplier;
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -070052import java.util.stream.Stream;
53import java.util.stream.StreamSupport;
tom5f38b3a2014-08-27 23:50:54 -070054
Madan Jampani2bfa94c2015-04-11 05:03:49 -070055import org.slf4j.Logger;
56
57import com.google.common.base.Strings;
58import com.google.common.primitives.UnsignedLongs;
59import com.google.common.util.concurrent.ThreadFactoryBuilder;
Ray Milkey705d9bc2014-11-18 08:19:00 -080060
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080061/**
62 * Miscellaneous utility methods.
63 */
tom5f38b3a2014-08-27 23:50:54 -070064public abstract class Tools {
65
66 private Tools() {
67 }
68
Thomas Vachuska02aeb032015-01-06 22:36:30 -080069 private static final Logger log = getLogger(Tools.class);
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080070
Thomas Vachuskaadba1522015-06-04 15:08:30 -070071 private static Random random = new Random();
72
tom5f38b3a2014-08-27 23:50:54 -070073 /**
74 * Returns a thread factory that produces threads named according to the
75 * supplied name pattern.
76 *
77 * @param pattern name pattern
78 * @return thread factory
79 */
80 public static ThreadFactory namedThreads(String pattern) {
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080081 return new ThreadFactoryBuilder()
82 .setNameFormat(pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -080083 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
84 .build();
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080085 }
Yuta HIGUCHI683e9782014-11-25 17:26:36 -080086
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080087 /**
88 * Returns a thread factory that produces threads named according to the
89 * supplied name pattern and from the specified thread-group. The thread
90 * group name is expected to be specified in slash-delimited format, e.g.
Thomas Vachuskac13b90a2015-02-18 18:19:55 -080091 * {@code onos/intent}. The thread names will be produced by converting
92 * the thread group name into dash-delimited format and pre-pended to the
93 * specified pattern.
Thomas Vachuska9c17a6d2015-02-17 23:36:43 -080094 *
95 * @param groupName group name in slash-delimited format to indicate hierarchy
96 * @param pattern name pattern
97 * @return thread factory
98 */
99 public static ThreadFactory groupedThreads(String groupName, String pattern) {
100 return new ThreadFactoryBuilder()
101 .setThreadFactory(groupedThreadFactory(groupName))
Thomas Vachuskac13b90a2015-02-18 18:19:55 -0800102 .setNameFormat(groupName.replace(GroupedThreadFactory.DELIMITER, "-") + "-" + pattern)
Thomas Vachuska480adad2015-03-06 10:27:09 -0800103 .setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception on " + t.getName(), e))
104 .build();
tom5f38b3a2014-08-27 23:50:54 -0700105 }
106
tom782a7cf2014-09-11 23:58:38 -0700107 /**
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800108 * Returns a thread factory that produces threads with MIN_PRIORITY.
109 *
110 * @param factory backing ThreadFactory
111 * @return thread factory
112 */
113 public static ThreadFactory minPriority(ThreadFactory factory) {
114 return new ThreadFactoryBuilder()
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800115 .setThreadFactory(factory)
116 .setPriority(Thread.MIN_PRIORITY)
117 .build();
Yuta HIGUCHI06586272014-11-25 14:27:03 -0800118 }
119
120 /**
Brian O'Connore2eac102015-02-12 18:30:22 -0800121 * Returns true if the collection is null or is empty.
122 *
123 * @param collection collection to test
124 * @return true if null or empty; false otherwise
125 */
126 public static boolean isNullOrEmpty(Collection collection) {
127 return collection == null || collection.isEmpty();
128 }
129
130 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700131 * Returns the specified item if that item is not null; otherwise throws
Thomas Vachuskaca88bb72015-04-08 19:38:02 -0700132 * not found exception.
133 *
134 * @param item item to check
135 * @param message not found message
136 * @param <T> item type
137 * @return item if not null
138 * @throws org.onlab.util.ItemNotFoundException if item is null
139 */
140 public static <T> T nullIsNotFound(T item, String message) {
141 if (item == null) {
142 throw new ItemNotFoundException(message);
143 }
144 return item;
145 }
146
147 /**
Ray Milkey36992c82015-11-17 13:31:15 -0800148 * Returns the specified set if the set is not null and not empty;
149 * otherwise throws a not found exception.
150 *
151 * @param item set to check
152 * @param message not found message
153 * @param <T> Set item type
154 * @return item if not null and not empty
155 * @throws org.onlab.util.ItemNotFoundException if set is null or empty
156 */
157 public static <T> Set<T> emptyIsNotFound(Set<T> item, String message) {
158 if (item == null || item.isEmpty()) {
159 throw new ItemNotFoundException(message);
160 }
161 return item;
162 }
163
164 /**
Ray Milkeyd43fe452015-05-29 09:35:12 -0700165 * Returns the specified item if that item is not null; otherwise throws
166 * bad argument exception.
167 *
168 * @param item item to check
169 * @param message not found message
170 * @param <T> item type
171 * @return item if not null
172 * @throws IllegalArgumentException if item is null
173 */
174 public static <T> T nullIsIllegal(T item, String message) {
175 if (item == null) {
176 throw new IllegalArgumentException(message);
177 }
178 return item;
179 }
180
181 /**
tom782a7cf2014-09-11 23:58:38 -0700182 * Converts a string from hex to long.
183 *
184 * @param string hex number in string form; sans 0x
185 * @return long value
186 */
187 public static long fromHex(String string) {
188 return UnsignedLongs.parseUnsignedLong(string, 16);
189 }
190
191 /**
192 * Converts a long value to hex string; 16 wide and sans 0x.
193 *
194 * @param value long value
195 * @return hex string
196 */
197 public static String toHex(long value) {
198 return Strings.padStart(UnsignedLongs.toString(value, 16), 16, '0');
199 }
200
201 /**
202 * Converts a long value to hex string; 16 wide and sans 0x.
203 *
204 * @param value long value
205 * @param width string width; zero padded
206 * @return hex string
207 */
208 public static String toHex(long value, int width) {
209 return Strings.padStart(UnsignedLongs.toString(value, 16), width, '0');
210 }
tomf110fff2014-09-26 00:38:18 -0700211
212 /**
Madan Jampani9eb55d12015-08-14 07:47:56 -0700213 * Returns a copy of the input byte array.
214 *
215 * @param original input
216 * @return copy of original
217 */
218 public static byte[] copyOf(byte[] original) {
219 return Arrays.copyOf(original, original.length);
220 }
221
222 /**
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700223 * Get property as a string value.
224 *
225 * @param properties properties to be looked up
226 * @param propertyName the name of the property to look up
227 * @return value when the propertyName is defined or return null
228 */
229 public static String get(Dictionary<?, ?> properties, String propertyName) {
230 Object v = properties.get(propertyName);
231 String s = (v instanceof String) ? (String) v :
232 v != null ? v.toString() : null;
233 return Strings.isNullOrEmpty(s) ? null : s.trim();
234 }
235
236 /**
tomf110fff2014-09-26 00:38:18 -0700237 * Suspends the current thread for a specified number of millis.
238 *
239 * @param ms number of millis
240 */
241 public static void delay(int ms) {
242 try {
243 Thread.sleep(ms);
244 } catch (InterruptedException e) {
245 throw new RuntimeException("Interrupted", e);
246 }
247 }
248
tom53efab52014-10-07 17:43:48 -0700249 /**
Madan Jampania29c6772015-08-17 13:17:07 -0700250 * Returns a function that retries execution on failure.
251 * @param base base function
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 function
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700257 * @param <U> type of function input
258 * @param <V> type of function output
Madan Jampania29c6772015-08-17 13:17:07 -0700259 */
260 public static <U, V> Function<U, V> retryable(Function<U, V> base,
261 Class<? extends Throwable> exceptionClass,
262 int maxRetries,
263 int maxDelayBetweenRetries) {
264 return new RetryingFunction<>(base, exceptionClass, maxRetries, maxDelayBetweenRetries);
265 }
266
267 /**
268 * Returns a Supplier that retries execution on failure.
269 * @param base base supplier
270 * @param exceptionClass type of exception for which to retry
271 * @param maxRetries max number of retries before giving up
272 * @param maxDelayBetweenRetries max delay between successive retries. The actual delay is randomly picked from
273 * the interval (0, maxDelayBetweenRetries]
274 * @return supplier
Thomas Vachuska87ae1d92015-08-19 17:39:11 -0700275 * @param <V> type of supplied result
Madan Jampania29c6772015-08-17 13:17:07 -0700276 */
277 public static <V> Supplier<V> retryable(Supplier<V> base,
278 Class<? extends Throwable> exceptionClass,
279 int maxRetries,
280 int maxDelayBetweenRetries) {
281 return () -> new RetryingFunction<>(v -> base.get(),
282 exceptionClass,
283 maxRetries,
284 maxDelayBetweenRetries).apply(null);
285 }
286
287 /**
Thomas Vachuskaadba1522015-06-04 15:08:30 -0700288 * Suspends the current thread for a random number of millis between 0 and
289 * the indicated limit.
290 *
291 * @param ms max number of millis
292 */
293 public static void randomDelay(int ms) {
294 try {
295 Thread.sleep(random.nextInt(ms));
296 } catch (InterruptedException e) {
297 throw new RuntimeException("Interrupted", e);
298 }
299 }
300
301 /**
Thomas Vachuskac40d4632015-04-09 16:55:03 -0700302 * Suspends the current thread for a specified number of millis and nanos.
303 *
304 * @param ms number of millis
305 * @param nanos number of nanos
306 */
307 public static void delay(int ms, int nanos) {
308 try {
309 Thread.sleep(ms, nanos);
310 } catch (InterruptedException e) {
311 throw new RuntimeException("Interrupted", e);
312 }
313 }
314
315 /**
tom53efab52014-10-07 17:43:48 -0700316 * Slurps the contents of a file into a list of strings, one per line.
317 *
318 * @param path file path
319 * @return file contents
HIGUCHI Yuta3b3bd1e2015-09-22 16:39:33 -0700320 * @deprecated in Emu release
tom53efab52014-10-07 17:43:48 -0700321 */
HIGUCHI Yuta3b3bd1e2015-09-22 16:39:33 -0700322 @Deprecated
tom53efab52014-10-07 17:43:48 -0700323 public static List<String> slurp(File path) {
HIGUCHI Yutacf4d6172015-09-22 16:39:33 -0700324 try (
Ray Milkey705d9bc2014-11-18 08:19:00 -0800325 BufferedReader br = new BufferedReader(
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800326 new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8));
HIGUCHI Yutacf4d6172015-09-22 16:39:33 -0700327 ) {
tom53efab52014-10-07 17:43:48 -0700328 List<String> lines = new ArrayList<>();
329 String line;
330 while ((line = br.readLine()) != null) {
331 lines.add(line);
332 }
333 return lines;
334
335 } catch (IOException e) {
336 return null;
337 }
338 }
339
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800340 /**
341 * Purges the specified directory path.&nbsp;Use with great caution since
342 * no attempt is made to check for symbolic links, which could result in
343 * deletion of unintended files.
344 *
345 * @param path directory to be removed
346 * @throws java.io.IOException if unable to remove contents
347 */
348 public static void removeDirectory(String path) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800349 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700350 File dir = new File(path);
351 if (dir.exists() && dir.isDirectory()) {
352 walkFileTree(Paths.get(path), 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
359 /**
360 * Purges the specified directory path.&nbsp;Use with great caution since
361 * no attempt is made to check for symbolic links, which could result in
362 * deletion of unintended files.
363 *
364 * @param dir directory to be removed
365 * @throws java.io.IOException if unable to remove contents
366 */
367 public static void removeDirectory(File dir) throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800368 DirectoryDeleter visitor = new DirectoryDeleter();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700369 if (dir.exists() && dir.isDirectory()) {
370 walkFileTree(Paths.get(dir.getAbsolutePath()), visitor);
371 if (visitor.exception != null) {
372 throw visitor.exception;
373 }
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800374 }
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800375 }
376
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800377 // Auxiliary path visitor for recursive directory structure removal.
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800378 private static class DirectoryDeleter extends SimpleFileVisitor<Path> {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800379
380 private IOException exception;
381
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800382 @Override
383 public FileVisitResult visitFile(Path file, BasicFileAttributes attributes)
384 throws IOException {
385 if (attributes.isRegularFile()) {
386 delete(file);
387 }
388 return FileVisitResult.CONTINUE;
389 }
390
391 @Override
392 public FileVisitResult postVisitDirectory(Path directory, IOException ioe)
393 throws IOException {
394 delete(directory);
395 return FileVisitResult.CONTINUE;
396 }
397
398 @Override
399 public FileVisitResult visitFileFailed(Path file, IOException ioe)
400 throws IOException {
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800401 this.exception = ioe;
402 return FileVisitResult.TERMINATE;
Thomas Vachuska02aeb032015-01-06 22:36:30 -0800403 }
404 }
405
Madan Jampani30a57f82015-03-02 12:19:41 -0800406 /**
407 * Returns a human friendly time ago string for a specified system time.
Thomas Vachuska480adad2015-03-06 10:27:09 -0800408 *
Madan Jampani30a57f82015-03-02 12:19:41 -0800409 * @param unixTime system time in millis
410 * @return human friendly time ago
411 */
412 public static String timeAgo(long unixTime) {
413 long deltaMillis = System.currentTimeMillis() - unixTime;
414 long secondsSince = (long) (deltaMillis / 1000.0);
415 long minsSince = (long) (deltaMillis / (1000.0 * 60));
416 long hoursSince = (long) (deltaMillis / (1000.0 * 60 * 60));
417 long daysSince = (long) (deltaMillis / (1000.0 * 60 * 60 * 24));
418 if (daysSince > 0) {
419 return String.format("%dd ago", daysSince);
420 } else if (hoursSince > 0) {
421 return String.format("%dh ago", hoursSince);
422 } else if (minsSince > 0) {
423 return String.format("%dm ago", minsSince);
424 } else if (secondsSince > 0) {
425 return String.format("%ds ago", secondsSince);
426 } else {
427 return "just now";
428 }
429 }
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800430
431 /**
432 * Copies the specified directory path.&nbsp;Use with great caution since
433 * no attempt is made to check for symbolic links, which could result in
434 * copy of unintended files.
435 *
436 * @param src directory to be copied
437 * @param dst destination directory to be removed
438 * @throws java.io.IOException if unable to remove contents
439 */
440 public static void copyDirectory(String src, String dst) throws IOException {
441 walkFileTree(Paths.get(src), new DirectoryCopier(src, dst));
442 }
443
444 /**
445 * Copies the specified directory path.&nbsp;Use with great caution since
446 * no attempt is made to check for symbolic links, which could result in
447 * copy of unintended files.
448 *
449 * @param src directory to be copied
450 * @param dst destination directory to be removed
451 * @throws java.io.IOException if unable to remove contents
452 */
453 public static void copyDirectory(File src, File dst) throws IOException {
454 walkFileTree(Paths.get(src.getAbsolutePath()),
455 new DirectoryCopier(src.getAbsolutePath(),
456 dst.getAbsolutePath()));
457 }
458
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700459 /**
460 * Returns the future value when complete or if future
461 * completes exceptionally returns the defaultValue.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700462 *
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700463 * @param future future
464 * @param defaultValue default value
465 * @param <T> future value type
466 * @return future value when complete or if future
467 * completes exceptionally returns the defaultValue.
468 */
469 public static <T> T futureGetOrElse(Future<T> future, T defaultValue) {
470 try {
471 return future.get();
472 } catch (InterruptedException e) {
473 Thread.currentThread().interrupt();
474 return defaultValue;
475 } catch (ExecutionException e) {
476 return defaultValue;
477 }
478 }
479
480 /**
481 * Returns the future value when complete or if future
482 * completes exceptionally returns the defaultValue.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700483 *
Madan Jampani2bfa94c2015-04-11 05:03:49 -0700484 * @param future future
485 * @param timeout time to wait for successful completion
486 * @param timeUnit time unit
487 * @param defaultValue default value
488 * @param <T> future value type
489 * @return future value when complete or if future
490 * completes exceptionally returns the defaultValue.
491 */
492 public static <T> T futureGetOrElse(Future<T> future,
493 long timeout,
494 TimeUnit timeUnit,
495 T defaultValue) {
496 try {
497 return future.get(timeout, timeUnit);
498 } catch (InterruptedException e) {
499 Thread.currentThread().interrupt();
500 return defaultValue;
501 } catch (ExecutionException | TimeoutException e) {
502 return defaultValue;
503 }
504 }
505
Madan Jampani27b69c62015-05-15 15:49:02 -0700506 /**
507 * Returns a future that is completed exceptionally.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700508 *
Madan Jampani27b69c62015-05-15 15:49:02 -0700509 * @param t exception
510 * @param <T> future value type
511 * @return future
512 */
513 public static <T> CompletableFuture<T> exceptionalFuture(Throwable t) {
514 CompletableFuture<T> future = new CompletableFuture<>();
515 future.completeExceptionally(t);
516 return future;
517 }
518
519 /**
520 * Returns the contents of {@code ByteBuffer} as byte array.
521 * <p>
522 * WARNING: There is a performance cost due to array copy
523 * when using this method.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700524 *
Madan Jampani27b69c62015-05-15 15:49:02 -0700525 * @param buffer byte buffer
526 * @return byte array containing the byte buffer contents
527 */
528 public static byte[] byteBuffertoArray(ByteBuffer buffer) {
529 int length = buffer.remaining();
530 if (buffer.hasArray()) {
531 int offset = buffer.arrayOffset() + buffer.position();
532 return Arrays.copyOfRange(buffer.array(), offset, offset + length);
533 }
534 byte[] bytes = new byte[length];
535 buffer.duplicate().get(bytes);
536 return bytes;
537 }
538
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700539 /**
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700540 * Converts an iterable to a stream.
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700541 *
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700542 * @param it iterable to convert
543 * @param <T> type if item
544 * @return iterable as a stream
HIGUCHI Yutabfc8b7a2015-07-01 23:47:43 -0700545 */
546 public static <T> Stream<T> stream(Iterable<T> it) {
547 return StreamSupport.stream(it.spliterator(), false);
548 }
549
Thomas Vachuska62ad95f2015-02-18 12:11:36 -0800550 // Auxiliary path visitor for recursive directory structure copying.
551 private static class DirectoryCopier extends SimpleFileVisitor<Path> {
Thomas Vachuska90b453f2015-01-30 18:57:14 -0800552 private Path src;
553 private Path dst;
554 private StandardCopyOption copyOption = StandardCopyOption.REPLACE_EXISTING;
555
556 DirectoryCopier(String src, String dst) {
557 this.src = Paths.get(src);
558 this.dst = Paths.get(dst);
559 }
560
561 @Override
562 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
563 Path targetPath = dst.resolve(src.relativize(dir));
564 if (!Files.exists(targetPath)) {
565 Files.createDirectory(targetPath);
566 }
567 return FileVisitResult.CONTINUE;
568 }
569
570 @Override
571 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
572 Files.copy(file, dst.resolve(src.relativize(file)), copyOption);
573 return FileVisitResult.CONTINUE;
574 }
575 }
576
tom5f38b3a2014-08-27 23:50:54 -0700577}