source: josm/trunk/src/org/openstreetmap/josm/tools/Utils.java@ 6421

Last change on this file since 6421 was 6421, checked in by simon04, 10 years ago

see #9341 - provide utility methods to open URL + decompress stream if needed

  • Property svn:eol-style set to native
File size: 28.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.Color;
8import java.awt.Toolkit;
9import java.awt.datatransfer.Clipboard;
10import java.awt.datatransfer.ClipboardOwner;
11import java.awt.datatransfer.DataFlavor;
12import java.awt.datatransfer.StringSelection;
13import java.awt.datatransfer.Transferable;
14import java.awt.datatransfer.UnsupportedFlavorException;
15import java.io.BufferedInputStream;
16import java.io.BufferedReader;
17import java.io.Closeable;
18import java.io.File;
19import java.io.FileInputStream;
20import java.io.FileOutputStream;
21import java.io.IOException;
22import java.io.InputStream;
23import java.io.InputStreamReader;
24import java.io.OutputStream;
25import java.io.UnsupportedEncodingException;
26import java.net.HttpURLConnection;
27import java.net.URL;
28import java.net.URLConnection;
29import java.nio.channels.FileChannel;
30import java.security.MessageDigest;
31import java.security.NoSuchAlgorithmException;
32import java.text.MessageFormat;
33import java.util.AbstractCollection;
34import java.util.AbstractList;
35import java.util.ArrayList;
36import java.util.Arrays;
37import java.util.Collection;
38import java.util.Iterator;
39import java.util.List;
40import java.util.zip.GZIPInputStream;
41import java.util.zip.ZipFile;
42
43import org.apache.tools.bzip2.CBZip2InputStream;
44import org.openstreetmap.josm.Main;
45import org.openstreetmap.josm.data.Version;
46import org.openstreetmap.josm.io.FileImporter;
47
48/**
49 * Basic utils, that can be useful in different parts of the program.
50 */
51public final class Utils {
52
53 private Utils() {
54 // Hide default constructor for utils classes
55 }
56
57 public static <T> boolean exists(Iterable<? extends T> collection, Predicate<? super T> predicate) {
58 for (T item : collection) {
59 if (predicate.evaluate(item))
60 return true;
61 }
62 return false;
63 }
64
65 public static <T> boolean exists(Iterable<T> collection, Class<? extends T> klass) {
66 for (Object item : collection) {
67 if (klass.isInstance(item))
68 return true;
69 }
70 return false;
71 }
72
73 public static <T> T find(Iterable<? extends T> collection, Predicate<? super T> predicate) {
74 for (T item : collection) {
75 if (predicate.evaluate(item))
76 return item;
77 }
78 return null;
79 }
80
81 @SuppressWarnings("unchecked")
82 public static <T> T find(Iterable<? super T> collection, Class<? extends T> klass) {
83 for (Object item : collection) {
84 if (klass.isInstance(item))
85 return (T) item;
86 }
87 return null;
88 }
89
90 public static <T> Collection<T> filter(Collection<? extends T> collection, Predicate<? super T> predicate) {
91 return new FilteredCollection<T>(collection, predicate);
92 }
93
94 public static <T> T firstNonNull(T... items) {
95 for (T i : items) {
96 if (i != null) {
97 return i;
98 }
99 }
100 return null;
101 }
102
103 /**
104 * Filter a collection by (sub)class.
105 * This is an efficient read-only implementation.
106 */
107 public static <S, T extends S> SubclassFilteredCollection<S, T> filteredCollection(Collection<S> collection, final Class<T> klass) {
108 return new SubclassFilteredCollection<S, T>(collection, new Predicate<S>() {
109 @Override
110 public boolean evaluate(S o) {
111 return klass.isInstance(o);
112 }
113 });
114 }
115
116 public static <T> int indexOf(Iterable<? extends T> collection, Predicate<? super T> predicate) {
117 int i = 0;
118 for (T item : collection) {
119 if (predicate.evaluate(item))
120 return i;
121 i++;
122 }
123 return -1;
124 }
125
126 /**
127 * Get minimum of 3 values
128 */
129 public static int min(int a, int b, int c) {
130 if (b < c) {
131 if (a < b)
132 return a;
133 return b;
134 } else {
135 if (a < c)
136 return a;
137 return c;
138 }
139 }
140
141 public static int max(int a, int b, int c, int d) {
142 return Math.max(Math.max(a, b), Math.max(c, d));
143 }
144
145 /**
146 * for convenience: test whether 2 objects are either both null or a.equals(b)
147 */
148 public static <T> boolean equal(T a, T b) {
149 if (a == b)
150 return true;
151 return (a != null && a.equals(b));
152 }
153
154 public static void ensure(boolean condition, String message, Object...data) {
155 if (!condition)
156 throw new AssertionError(
157 MessageFormat.format(message,data)
158 );
159 }
160
161 /**
162 * return the modulus in the range [0, n)
163 */
164 public static int mod(int a, int n) {
165 if (n <= 0)
166 throw new IllegalArgumentException();
167 int res = a % n;
168 if (res < 0) {
169 res += n;
170 }
171 return res;
172 }
173
174 /**
175 * Joins a list of strings (or objects that can be converted to string via
176 * Object.toString()) into a single string with fields separated by sep.
177 * @param sep the separator
178 * @param values collection of objects, null is converted to the
179 * empty string
180 * @return null if values is null. The joined string otherwise.
181 */
182 public static String join(String sep, Collection<?> values) {
183 if (sep == null)
184 throw new IllegalArgumentException();
185 if (values == null)
186 return null;
187 if (values.isEmpty())
188 return "";
189 StringBuilder s = null;
190 for (Object a : values) {
191 if (a == null) {
192 a = "";
193 }
194 if (s != null) {
195 s.append(sep).append(a.toString());
196 } else {
197 s = new StringBuilder(a.toString());
198 }
199 }
200 return s.toString();
201 }
202
203 public static String joinAsHtmlUnorderedList(Collection<?> values) {
204 StringBuilder sb = new StringBuilder(1024);
205 sb.append("<ul>");
206 for (Object i : values) {
207 sb.append("<li>").append(i).append("</li>");
208 }
209 sb.append("</ul>");
210 return sb.toString();
211 }
212
213 /**
214 * convert Color to String
215 * (Color.toString() omits alpha value)
216 */
217 public static String toString(Color c) {
218 if (c == null)
219 return "null";
220 if (c.getAlpha() == 255)
221 return String.format("#%06x", c.getRGB() & 0x00ffffff);
222 else
223 return String.format("#%06x(alpha=%d)", c.getRGB() & 0x00ffffff, c.getAlpha());
224 }
225
226 /**
227 * convert float range 0 <= x <= 1 to integer range 0..255
228 * when dealing with colors and color alpha value
229 * @return null if val is null, the corresponding int if val is in the
230 * range 0...1. If val is outside that range, return 255
231 */
232 public static Integer color_float2int(Float val) {
233 if (val == null)
234 return null;
235 if (val < 0 || val > 1)
236 return 255;
237 return (int) (255f * val + 0.5f);
238 }
239
240 /**
241 * convert back
242 */
243 public static Float color_int2float(Integer val) {
244 if (val == null)
245 return null;
246 if (val < 0 || val > 255)
247 return 1f;
248 return ((float) val) / 255f;
249 }
250
251 public static Color complement(Color clr) {
252 return new Color(255 - clr.getRed(), 255 - clr.getGreen(), 255 - clr.getBlue(), clr.getAlpha());
253 }
254
255 /**
256 * Copies the given array. Unlike {@link Arrays#copyOf}, this method is null-safe.
257 * @param array The array to copy
258 * @return A copy of the original array, or {@code null} if {@code array} is null
259 * @since 6221
260 */
261 public static <T> T[] copyArray(T[] array) {
262 if (array != null) {
263 return Arrays.copyOf(array, array.length);
264 }
265 return null;
266 }
267
268 /**
269 * Copies the given array. Unlike {@link Arrays#copyOf}, this method is null-safe.
270 * @param array The array to copy
271 * @return A copy of the original array, or {@code null} if {@code array} is null
272 * @since 6222
273 */
274 public static char[] copyArray(char[] array) {
275 if (array != null) {
276 return Arrays.copyOf(array, array.length);
277 }
278 return null;
279 }
280
281 /**
282 * Simple file copy function that will overwrite the target file.<br/>
283 * Taken from <a href="http://www.rgagnon.com/javadetails/java-0064.html">this article</a> (CC-NC-BY-SA)
284 * @param in The source file
285 * @param out The destination file
286 * @throws IOException If any I/O error occurs
287 */
288 public static void copyFile(File in, File out) throws IOException {
289 // TODO: remove this function when we move to Java 7 (use Files.copy instead)
290 FileInputStream inStream = null;
291 FileOutputStream outStream = null;
292 try {
293 inStream = new FileInputStream(in);
294 outStream = new FileOutputStream(out);
295 FileChannel inChannel = inStream.getChannel();
296 inChannel.transferTo(0, inChannel.size(), outStream.getChannel());
297 }
298 catch (IOException e) {
299 throw e;
300 }
301 finally {
302 close(outStream);
303 close(inStream);
304 }
305 }
306
307 public static int copyStream(InputStream source, OutputStream destination) throws IOException {
308 int count = 0;
309 byte[] b = new byte[512];
310 int read;
311 while ((read = source.read(b)) != -1) {
312 count += read;
313 destination.write(b, 0, read);
314 }
315 return count;
316 }
317
318 public static boolean deleteDirectory(File path) {
319 if( path.exists() ) {
320 File[] files = path.listFiles();
321 for (File file : files) {
322 if (file.isDirectory()) {
323 deleteDirectory(file);
324 } else {
325 file.delete();
326 }
327 }
328 }
329 return( path.delete() );
330 }
331
332 /**
333 * <p>Utility method for closing a {@link Closeable} object.</p>
334 *
335 * @param c the closeable object. May be null.
336 */
337 public static void close(Closeable c) {
338 if (c == null) return;
339 try {
340 c.close();
341 } catch (IOException e) {
342 Main.warn(e);
343 }
344 }
345
346 /**
347 * <p>Utility method for closing a {@link ZipFile}.</p>
348 *
349 * @param zip the zip file. May be null.
350 */
351 public static void close(ZipFile zip) {
352 if (zip == null) return;
353 try {
354 zip.close();
355 } catch (IOException e) {
356 Main.warn(e);
357 }
358 }
359
360 private final static double EPSILON = 1e-11;
361
362 /**
363 * Determines if the two given double values are equal (their delta being smaller than a fixed epsilon)
364 * @param a The first double value to compare
365 * @param b The second double value to compare
366 * @return {@code true} if {@code abs(a - b) <= 1e-11}, {@code false} otherwise
367 */
368 public static boolean equalsEpsilon(double a, double b) {
369 return Math.abs(a - b) <= EPSILON;
370 }
371
372 /**
373 * Copies the string {@code s} to system clipboard.
374 * @param s string to be copied to clipboard.
375 * @return true if succeeded, false otherwise.
376 */
377 public static boolean copyToClipboard(String s) {
378 try {
379 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(s), new ClipboardOwner() {
380
381 @Override
382 public void lostOwnership(Clipboard clpbrd, Transferable t) {
383 }
384 });
385 return true;
386 } catch (IllegalStateException ex) {
387 ex.printStackTrace();
388 return false;
389 }
390 }
391
392 /**
393 * Extracts clipboard content as string.
394 * @return string clipboard contents if available, {@code null} otherwise.
395 */
396 public static String getClipboardContent() {
397 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
398 Transferable t = null;
399 for (int tries = 0; t == null && tries < 10; tries++) {
400 try {
401 t = clipboard.getContents(null);
402 } catch (IllegalStateException e) {
403 // Clipboard currently unavailable. On some platforms, the system clipboard is unavailable while it is accessed by another application.
404 try {
405 Thread.sleep(1);
406 } catch (InterruptedException ex) {
407 Main.warn("InterruptedException in "+Utils.class.getSimpleName()+" while getting clipboard content");
408 }
409 }
410 }
411 try {
412 if (t != null && t.isDataFlavorSupported(DataFlavor.stringFlavor)) {
413 String text = (String) t.getTransferData(DataFlavor.stringFlavor);
414 return text;
415 }
416 } catch (UnsupportedFlavorException ex) {
417 ex.printStackTrace();
418 return null;
419 } catch (IOException ex) {
420 ex.printStackTrace();
421 return null;
422 }
423 return null;
424 }
425
426 /**
427 * Calculate MD5 hash of a string and output in hexadecimal format.
428 * @param data arbitrary String
429 * @return MD5 hash of data, string of length 32 with characters in range [0-9a-f]
430 */
431 public static String md5Hex(String data) {
432 byte[] byteData = null;
433 try {
434 byteData = data.getBytes("UTF-8");
435 } catch (UnsupportedEncodingException e) {
436 throw new RuntimeException();
437 }
438 MessageDigest md = null;
439 try {
440 md = MessageDigest.getInstance("MD5");
441 } catch (NoSuchAlgorithmException e) {
442 throw new RuntimeException();
443 }
444 byte[] byteDigest = md.digest(byteData);
445 return toHexString(byteDigest);
446 }
447
448 private static final char[] HEX_ARRAY = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
449
450 /**
451 * Converts a byte array to a string of hexadecimal characters.
452 * Preserves leading zeros, so the size of the output string is always twice
453 * the number of input bytes.
454 * @param bytes the byte array
455 * @return hexadecimal representation
456 */
457 public static String toHexString(byte[] bytes) {
458
459 if (bytes == null) {
460 return "";
461 }
462
463 final int len = bytes.length;
464 if (len == 0) {
465 return "";
466 }
467
468 char[] hexChars = new char[len * 2];
469 for (int i = 0, j = 0; i < len; i++) {
470 final int v = bytes[i];
471 hexChars[j++] = HEX_ARRAY[(v & 0xf0) >> 4];
472 hexChars[j++] = HEX_ARRAY[v & 0xf];
473 }
474 return new String(hexChars);
475 }
476
477 /**
478 * Topological sort.
479 *
480 * @param dependencies contains mappings (key -> value). In the final list of sorted objects, the key will come
481 * after the value. (In other words, the key depends on the value(s).)
482 * There must not be cyclic dependencies.
483 * @return the list of sorted objects
484 */
485 public static <T> List<T> topologicalSort(final MultiMap<T,T> dependencies) {
486 MultiMap<T,T> deps = new MultiMap<T,T>();
487 for (T key : dependencies.keySet()) {
488 deps.putVoid(key);
489 for (T val : dependencies.get(key)) {
490 deps.putVoid(val);
491 deps.put(key, val);
492 }
493 }
494
495 int size = deps.size();
496 List<T> sorted = new ArrayList<T>();
497 for (int i=0; i<size; ++i) {
498 T parentless = null;
499 for (T key : deps.keySet()) {
500 if (deps.get(key).isEmpty()) {
501 parentless = key;
502 break;
503 }
504 }
505 if (parentless == null) throw new RuntimeException();
506 sorted.add(parentless);
507 deps.remove(parentless);
508 for (T key : deps.keySet()) {
509 deps.remove(key, parentless);
510 }
511 }
512 if (sorted.size() != size) throw new RuntimeException();
513 return sorted;
514 }
515
516 /**
517 * Represents a function that can be applied to objects of {@code A} and
518 * returns objects of {@code B}.
519 * @param <A> class of input objects
520 * @param <B> class of transformed objects
521 */
522 public static interface Function<A, B> {
523
524 /**
525 * Applies the function on {@code x}.
526 * @param x an object of
527 * @return the transformed object
528 */
529 B apply(A x);
530 }
531
532 /**
533 * Transforms the collection {@code c} into an unmodifiable collection and
534 * applies the {@link Function} {@code f} on each element upon access.
535 * @param <A> class of input collection
536 * @param <B> class of transformed collection
537 * @param c a collection
538 * @param f a function that transforms objects of {@code A} to objects of {@code B}
539 * @return the transformed unmodifiable collection
540 */
541 public static <A, B> Collection<B> transform(final Collection<? extends A> c, final Function<A, B> f) {
542 return new AbstractCollection<B>() {
543
544 @Override
545 public int size() {
546 return c.size();
547 }
548
549 @Override
550 public Iterator<B> iterator() {
551 return new Iterator<B>() {
552
553 private Iterator<? extends A> it = c.iterator();
554
555 @Override
556 public boolean hasNext() {
557 return it.hasNext();
558 }
559
560 @Override
561 public B next() {
562 return f.apply(it.next());
563 }
564
565 @Override
566 public void remove() {
567 throw new UnsupportedOperationException();
568 }
569 };
570 }
571 };
572 }
573
574 /**
575 * Transforms the list {@code l} into an unmodifiable list and
576 * applies the {@link Function} {@code f} on each element upon access.
577 * @param <A> class of input collection
578 * @param <B> class of transformed collection
579 * @param l a collection
580 * @param f a function that transforms objects of {@code A} to objects of {@code B}
581 * @return the transformed unmodifiable list
582 */
583 public static <A, B> List<B> transform(final List<? extends A> l, final Function<A, B> f) {
584 return new AbstractList<B>() {
585
586
587 @Override
588 public int size() {
589 return l.size();
590 }
591
592 @Override
593 public B get(int index) {
594 return f.apply(l.get(index));
595 }
596
597
598 };
599 }
600
601 /**
602 * Convert Hex String to Color.
603 * @param s Must be of the form "#34a300" or "#3f2", otherwise throws Exception.
604 * Upper/lower case does not matter.
605 * @return The corresponding color.
606 */
607 static public Color hexToColor(String s) {
608 String clr = s.substring(1);
609 if (clr.length() == 3) {
610 clr = new String(new char[] {
611 clr.charAt(0), clr.charAt(0), clr.charAt(1), clr.charAt(1), clr.charAt(2), clr.charAt(2)
612 });
613 }
614 if (clr.length() != 6)
615 throw new IllegalArgumentException();
616 return new Color(Integer.parseInt(clr, 16));
617 }
618
619 /**
620 * Opens a HTTP connection to the given URL and sets the User-Agent property to JOSM's one.
621 * @param httpURL The HTTP url to open (must use http:// or https://)
622 * @return An open HTTP connection to the given URL
623 * @throws IOException if an I/O exception occurs.
624 * @since 5587
625 */
626 public static HttpURLConnection openHttpConnection(URL httpURL) throws IOException {
627 if (httpURL == null || !httpURL.getProtocol().matches("https?")) {
628 throw new IllegalArgumentException("Invalid HTTP url");
629 }
630 HttpURLConnection connection = (HttpURLConnection) httpURL.openConnection();
631 connection.setRequestProperty("User-Agent", Version.getInstance().getFullAgentString());
632 connection.setUseCaches(false);
633 return connection;
634 }
635
636 /**
637 * Opens a connection to the given URL and sets the User-Agent property to JOSM's one.
638 * @param url The url to open
639 * @return An stream for the given URL
640 * @throws IOException if an I/O exception occurs.
641 * @since 5867
642 */
643 public static InputStream openURL(URL url) throws IOException {
644 return openURLAndDecompress(url, false);
645 }
646
647 /**
648 * Opens a connection to the given URL, sets the User-Agent property to JOSM's one, and decompresses stream if necessary.
649 * @param url The url to open
650 * @param decompress whether to wrap steam in a {@link GZIPInputStream} or {@link CBZip2InputStream}
651 * if the {@code Content-Type} header is set accordingly.
652 * @return An stream for the given URL
653 * @throws IOException if an I/O exception occurs.
654 * @since 6421
655 */
656 public static InputStream openURLAndDecompress(final URL url, final boolean decompress) throws IOException {
657 final URLConnection connection = setupURLConnection(url.openConnection());
658 if (decompress && "application/x-gzip".equals(connection.getHeaderField("Content-Type"))) {
659 return new GZIPInputStream(connection.getInputStream());
660 } else if (decompress && "application/x-bzip2".equals(connection.getHeaderField("Content-Type"))) {
661 return FileImporter.getBZip2InputStream(new BufferedInputStream(connection.getInputStream()));
662 } else {
663 return connection.getInputStream();
664 }
665 }
666
667 /***
668 * Setups the given URL connection to match JOSM needs by setting its User-Agent and timeout properties.
669 * @param connection The connection to setup
670 * @return {@code connection}, with updated properties
671 * @since 5887
672 */
673 public static URLConnection setupURLConnection(URLConnection connection) {
674 if (connection != null) {
675 connection.setRequestProperty("User-Agent", Version.getInstance().getFullAgentString());
676 connection.setConnectTimeout(Main.pref.getInteger("socket.timeout.connect",15)*1000);
677 connection.setReadTimeout(Main.pref.getInteger("socket.timeout.read",30)*1000);
678 }
679 return connection;
680 }
681
682 /**
683 * Opens a connection to the given URL and sets the User-Agent property to JOSM's one.
684 * @param url The url to open
685 * @return An buffered stream reader for the given URL (using UTF-8)
686 * @throws IOException if an I/O exception occurs.
687 * @since 5868
688 */
689 public static BufferedReader openURLReader(URL url) throws IOException {
690 return openURLReaderAndDecompress(url, false);
691 }
692
693 /**
694 * Opens a connection to the given URL and sets the User-Agent property to JOSM's one.
695 * @param url The url to open
696 * @param decompress whether to wrap steam in a {@link GZIPInputStream} or {@link CBZip2InputStream}
697 * if the {@code Content-Type} header is set accordingly.
698 * @return An buffered stream reader for the given URL (using UTF-8)
699 * @throws IOException if an I/O exception occurs.
700 * @since 6421
701 */
702 public static BufferedReader openURLReaderAndDecompress(final URL url, final boolean decompress) throws IOException {
703 return new BufferedReader(new InputStreamReader(openURLAndDecompress(url, decompress), "utf-8"));
704 }
705
706 /**
707 * Opens a HTTP connection to the given URL, sets the User-Agent property to JOSM's one and optionnaly disables Keep-Alive.
708 * @param httpURL The HTTP url to open (must use http:// or https://)
709 * @param keepAlive whether not to set header {@code Connection=close}
710 * @return An open HTTP connection to the given URL
711 * @throws IOException if an I/O exception occurs.
712 * @since 5587
713 */
714 public static HttpURLConnection openHttpConnection(URL httpURL, boolean keepAlive) throws IOException {
715 HttpURLConnection connection = openHttpConnection(httpURL);
716 if (!keepAlive) {
717 connection.setRequestProperty("Connection", "close");
718 }
719 return connection;
720 }
721
722 /**
723 * An alternative to {@link String#trim()} to effectively remove all leading and trailing white characters, including Unicode ones.
724 * @see <a href="http://closingbraces.net/2008/11/11/javastringtrim/">Java’s String.trim has a strange idea of whitespace</a>
725 * @see <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4080617">JDK bug 4080617</a>
726 * @param str The string to strip
727 * @return <code>str</code>, without leading and trailing characters, according to
728 * {@link Character#isWhitespace(char)} and {@link Character#isSpaceChar(char)}.
729 * @since 5772
730 */
731 public static String strip(String str) {
732 if (str == null || str.isEmpty()) {
733 return str;
734 }
735 int start = 0, end = str.length();
736 boolean leadingWhite = true;
737 while (leadingWhite && start < end) {
738 char c = str.charAt(start);
739 // '\u200B' (ZERO WIDTH SPACE character) needs to be handled manually because of change in Unicode 6.0 (Java 7, see #8918)
740 // same for '\uFEFF' (ZERO WIDTH NO-BREAK SPACE)
741 leadingWhite = (Character.isWhitespace(c) || Character.isSpaceChar(c) || c == '\u200B' || c == '\uFEFF');
742 if (leadingWhite) {
743 start++;
744 }
745 }
746 boolean trailingWhite = true;
747 while (trailingWhite && end > start+1) {
748 char c = str.charAt(end-1);
749 trailingWhite = (Character.isWhitespace(c) || Character.isSpaceChar(c) || c == '\u200B' || c == '\uFEFF');
750 if (trailingWhite) {
751 end--;
752 }
753 }
754 return str.substring(start, end);
755 }
756
757 /**
758 * Runs an external command and returns the standard output.
759 *
760 * The program is expected to execute fast.
761 *
762 * @param command the command with arguments
763 * @return the output
764 * @throws IOException when there was an error, e.g. command does not exist
765 */
766 public static String execOutput(List<String> command) throws IOException {
767 Process p = new ProcessBuilder(command).start();
768 BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
769 StringBuilder all = null;
770 String line;
771 while ((line = input.readLine()) != null) {
772 if (all == null) {
773 all = new StringBuilder(line);
774 } else {
775 all.append("\n");
776 all.append(line);
777 }
778 }
779 Utils.close(input);
780 return all.toString();
781 }
782
783 /**
784 * Returns the JOSM temp directory.
785 * @return The JOSM temp directory ({@code <java.io.tmpdir>/JOSM}), or {@code null} if {@code java.io.tmpdir} is not defined
786 * @since 6245
787 */
788 public static File getJosmTempDir() {
789 String tmpDir = System.getProperty("java.io.tmpdir");
790 if (tmpDir == null) {
791 return null;
792 }
793 File josmTmpDir = new File(tmpDir, "JOSM");
794 if (!josmTmpDir.exists()) {
795 if (!josmTmpDir.mkdirs()) {
796 Main.warn("Unable to create temp directory "+josmTmpDir);
797 }
798 }
799 return josmTmpDir;
800 }
801
802 /**
803 * Returns a simple human readable (hours, minutes, seconds) string for a given duration in milliseconds.
804 * @param elapsedTime The duration in milliseconds
805 * @return A human redable string for the given duration
806 * @throws IllegalArgumentException if elapsedTime is < 0
807 * @since 6354
808 */
809 public static String getDurationString(long elapsedTime) throws IllegalArgumentException {
810 if (elapsedTime < 0) {
811 throw new IllegalArgumentException("elapsedTime must be > 0");
812 }
813 // Is it less than 1 second ?
814 if (elapsedTime < 1000) {
815 return String.format("%d %s", elapsedTime, tr("ms"));
816 }
817 // Is it less than 1 minute ?
818 if (elapsedTime < 60*1000) {
819 return String.format("%.1f %s", elapsedTime/1000f, tr("s"));
820 }
821 // Is it less than 1 hour ?
822 if (elapsedTime < 60*60*1000) {
823 return String.format("%d %s %d %s", elapsedTime/60000, tr("min"), elapsedTime/1000, tr("s"));
824 }
825 // Is it less than 1 day ?
826 if (elapsedTime < 24*60*60*1000) {
827 return String.format("%d %s %d %s", elapsedTime/3600000, tr("h"), elapsedTime/60000, tr("min"));
828 }
829 long days = elapsedTime/86400000;
830 return String.format("%d %s %d %s", days, trn("day", "days", days), elapsedTime/3600000, tr("h"));
831 }
832}
Note: See TracBrowser for help on using the repository browser.