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

Last change on this file since 5868 was 5868, checked in by stoecker, 11 years ago

see #8606 - use JOSM agent also for WebStart, join help browser and WikiReader

  • Property svn:eol-style set to native
File size: 20.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import java.awt.Color;
5import java.awt.Toolkit;
6import java.awt.datatransfer.Clipboard;
7import java.awt.datatransfer.ClipboardOwner;
8import java.awt.datatransfer.DataFlavor;
9import java.awt.datatransfer.StringSelection;
10import java.awt.datatransfer.Transferable;
11import java.awt.datatransfer.UnsupportedFlavorException;
12import java.io.BufferedReader;
13import java.io.File;
14import java.io.IOException;
15import java.io.InputStream;
16import java.io.InputStreamReader;
17import java.io.OutputStream;
18import java.io.Reader;
19import java.io.UnsupportedEncodingException;
20import java.net.HttpURLConnection;
21import java.net.URL;
22import java.net.URLConnection;
23import java.security.MessageDigest;
24import java.security.NoSuchAlgorithmException;
25import java.text.MessageFormat;
26import java.util.AbstractCollection;
27import java.util.AbstractList;
28import java.util.ArrayList;
29import java.util.Collection;
30import java.util.Iterator;
31import java.util.List;
32
33import org.openstreetmap.josm.Main;
34import org.openstreetmap.josm.data.Version;
35
36/**
37 * Basic utils, that can be useful in different parts of the program.
38 */
39public class Utils {
40
41 public static <T> boolean exists(Iterable<? extends T> collection, Predicate<? super T> predicate) {
42 for (T item : collection) {
43 if (predicate.evaluate(item))
44 return true;
45 }
46 return false;
47 }
48
49 public static <T> boolean exists(Iterable<T> collection, Class<? extends T> klass) {
50 for (Object item : collection) {
51 if (klass.isInstance(item))
52 return true;
53 }
54 return false;
55 }
56
57 public static <T> T find(Iterable<? extends T> collection, Predicate<? super T> predicate) {
58 for (T item : collection) {
59 if (predicate.evaluate(item))
60 return item;
61 }
62 return null;
63 }
64
65 @SuppressWarnings("unchecked")
66 public static <T> T find(Iterable<? super T> collection, Class<? extends T> klass) {
67 for (Object item : collection) {
68 if (klass.isInstance(item))
69 return (T) item;
70 }
71 return null;
72 }
73
74 public static <T> Collection<T> filter(Collection<? extends T> collection, Predicate<? super T> predicate) {
75 return new FilteredCollection<T>(collection, predicate);
76 }
77
78 public static <T> T firstNonNull(T... items) {
79 for (T i : items) {
80 if (i != null) {
81 return i;
82 }
83 }
84 return null;
85 }
86
87 /**
88 * Filter a collection by (sub)class.
89 * This is an efficient read-only implementation.
90 */
91 public static <S, T extends S> SubclassFilteredCollection<S, T> filteredCollection(Collection<S> collection, final Class<T> klass) {
92 return new SubclassFilteredCollection<S, T>(collection, new Predicate<S>() {
93 @Override
94 public boolean evaluate(S o) {
95 return klass.isInstance(o);
96 }
97 });
98 }
99
100 public static <T> int indexOf(Iterable<? extends T> collection, Predicate<? super T> predicate) {
101 int i = 0;
102 for (T item : collection) {
103 if (predicate.evaluate(item))
104 return i;
105 i++;
106 }
107 return -1;
108 }
109
110 /**
111 * Get minimum of 3 values
112 */
113 public static int min(int a, int b, int c) {
114 if (b < c) {
115 if (a < b)
116 return a;
117 return b;
118 } else {
119 if (a < c)
120 return a;
121 return c;
122 }
123 }
124
125 public static int max(int a, int b, int c, int d) {
126 return Math.max(Math.max(a, b), Math.max(c, d));
127 }
128
129 /**
130 * for convenience: test whether 2 objects are either both null or a.equals(b)
131 */
132 public static <T> boolean equal(T a, T b) {
133 if (a == b)
134 return true;
135 return (a != null && a.equals(b));
136 }
137
138 public static void ensure(boolean condition, String message, Object...data) {
139 if (!condition)
140 throw new AssertionError(
141 MessageFormat.format(message,data)
142 );
143 }
144
145 /**
146 * return the modulus in the range [0, n)
147 */
148 public static int mod(int a, int n) {
149 if (n <= 0)
150 throw new IllegalArgumentException();
151 int res = a % n;
152 if (res < 0) {
153 res += n;
154 }
155 return res;
156 }
157
158 /**
159 * Joins a list of strings (or objects that can be converted to string via
160 * Object.toString()) into a single string with fields separated by sep.
161 * @param sep the separator
162 * @param values collection of objects, null is converted to the
163 * empty string
164 * @return null if values is null. The joined string otherwise.
165 */
166 public static String join(String sep, Collection<?> values) {
167 if (sep == null)
168 throw new IllegalArgumentException();
169 if (values == null)
170 return null;
171 if (values.isEmpty())
172 return "";
173 StringBuilder s = null;
174 for (Object a : values) {
175 if (a == null) {
176 a = "";
177 }
178 if (s != null) {
179 s.append(sep).append(a.toString());
180 } else {
181 s = new StringBuilder(a.toString());
182 }
183 }
184 return s.toString();
185 }
186
187 public static String joinAsHtmlUnorderedList(Collection<?> values) {
188 StringBuilder sb = new StringBuilder(1024);
189 sb.append("<ul>");
190 for (Object i : values) {
191 sb.append("<li>").append(i).append("</li>");
192 }
193 sb.append("</ul>");
194 return sb.toString();
195 }
196
197 /**
198 * convert Color to String
199 * (Color.toString() omits alpha value)
200 */
201 public static String toString(Color c) {
202 if (c == null)
203 return "null";
204 if (c.getAlpha() == 255)
205 return String.format("#%06x", c.getRGB() & 0x00ffffff);
206 else
207 return String.format("#%06x(alpha=%d)", c.getRGB() & 0x00ffffff, c.getAlpha());
208 }
209
210 /**
211 * convert float range 0 <= x <= 1 to integer range 0..255
212 * when dealing with colors and color alpha value
213 * @return null if val is null, the corresponding int if val is in the
214 * range 0...1. If val is outside that range, return 255
215 */
216 public static Integer color_float2int(Float val) {
217 if (val == null)
218 return null;
219 if (val < 0 || val > 1)
220 return 255;
221 return (int) (255f * val + 0.5f);
222 }
223
224 /**
225 * convert back
226 */
227 public static Float color_int2float(Integer val) {
228 if (val == null)
229 return null;
230 if (val < 0 || val > 255)
231 return 1f;
232 return ((float) val) / 255f;
233 }
234
235 public static Color complement(Color clr) {
236 return new Color(255 - clr.getRed(), 255 - clr.getGreen(), 255 - clr.getBlue(), clr.getAlpha());
237 }
238
239 public static int copyStream(InputStream source, OutputStream destination) throws IOException {
240 int count = 0;
241 byte[] b = new byte[512];
242 int read;
243 while ((read = source.read(b)) != -1) {
244 count += read;
245 destination.write(b, 0, read);
246 }
247 return count;
248 }
249
250 public static boolean deleteDirectory(File path) {
251 if( path.exists() ) {
252 File[] files = path.listFiles();
253 for(int i=0; i<files.length; i++) {
254 if(files[i].isDirectory()) {
255 deleteDirectory(files[i]);
256 }
257 else {
258 files[i].delete();
259 }
260 }
261 }
262 return( path.delete() );
263 }
264
265 /**
266 * <p>Utility method for closing an input stream.</p>
267 *
268 * @param is the input stream. May be null.
269 */
270 public static void close(InputStream is){
271 if (is == null) return;
272 try {
273 is.close();
274 } catch(IOException e){
275 // ignore
276 }
277 }
278
279 /**
280 * <p>Utility method for closing an output stream.</p>
281 *
282 * @param os the output stream. May be null.
283 */
284 public static void close(OutputStream os){
285 if (os == null) return;
286 try {
287 os.close();
288 } catch(IOException e){
289 // ignore
290 }
291 }
292
293 /**
294 * <p>Utility method for closing a reader.</p>
295 *
296 * @param reader the reader. May be null.
297 */
298 public static void close(Reader reader){
299 if (reader == null) return;
300 try {
301 reader.close();
302 } catch(IOException e){
303 // ignore
304 }
305 }
306
307 private final static double EPSILION = 1e-11;
308
309 public static boolean equalsEpsilon(double a, double b) {
310 return Math.abs(a - b) <= EPSILION;
311 }
312
313 /**
314 * Copies the string {@code s} to system clipboard.
315 * @param s string to be copied to clipboard.
316 * @return true if succeeded, false otherwise.
317 */
318 public static boolean copyToClipboard(String s) {
319 try {
320 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(s), new ClipboardOwner() {
321
322 @Override
323 public void lostOwnership(Clipboard clpbrd, Transferable t) {
324 }
325 });
326 return true;
327 } catch (IllegalStateException ex) {
328 ex.printStackTrace();
329 return false;
330 }
331 }
332
333 /**
334 * Extracts clipboard content as string.
335 * @return string clipboard contents if available, {@code null} otherwise.
336 */
337 public static String getClipboardContent() {
338 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
339 Transferable t = null;
340 for (int tries = 0; t == null && tries < 10; tries++) {
341 try {
342 t = clipboard.getContents(null);
343 } catch (IllegalStateException e) {
344 // Clipboard currently unavailable. On some platforms, the system clipboard is unavailable while it is accessed by another application.
345 try {
346 Thread.sleep(1);
347 } catch (InterruptedException ex) {
348 }
349 }
350 }
351 try {
352 if (t != null && t.isDataFlavorSupported(DataFlavor.stringFlavor)) {
353 String text = (String) t.getTransferData(DataFlavor.stringFlavor);
354 return text;
355 }
356 } catch (UnsupportedFlavorException ex) {
357 ex.printStackTrace();
358 return null;
359 } catch (IOException ex) {
360 ex.printStackTrace();
361 return null;
362 }
363 return null;
364 }
365
366 /**
367 * Calculate MD5 hash of a string and output in hexadecimal format.
368 * @param data arbitrary String
369 * @return MD5 hash of data, string of length 32 with characters in range [0-9a-f]
370 */
371 public static String md5Hex(String data) {
372 byte[] byteData = null;
373 try {
374 byteData = data.getBytes("UTF-8");
375 } catch (UnsupportedEncodingException e) {
376 throw new RuntimeException();
377 }
378 MessageDigest md = null;
379 try {
380 md = MessageDigest.getInstance("MD5");
381 } catch (NoSuchAlgorithmException e) {
382 throw new RuntimeException();
383 }
384 byte[] byteDigest = md.digest(byteData);
385 return toHexString(byteDigest);
386 }
387
388 /**
389 * Converts a byte array to a string of hexadecimal characters.
390 * Preserves leading zeros, so the size of the output string is always twice
391 * the number of input bytes.
392 * @param bytes the byte array
393 * @return hexadecimal representation
394 */
395 public static String toHexString(byte[] bytes) {
396 char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
397 char[] hexChars = new char[bytes.length * 2];
398 for (int j=0; j<bytes.length; j++) {
399 int v = bytes[j] & 0xFF;
400 hexChars[j*2] = hexArray[v/16];
401 hexChars[j*2 + 1] = hexArray[v%16];
402 }
403 return new String(hexChars);
404 }
405
406 /**
407 * Topological sort.
408 *
409 * @param dependencies contains mappings (key -> value). In the final list of sorted objects, the key will come
410 * after the value. (In other words, the key depends on the value(s).)
411 * There must not be cyclic dependencies.
412 * @return the list of sorted objects
413 */
414 public static <T> List<T> topologicalSort(final MultiMap<T,T> dependencies) {
415 MultiMap<T,T> deps = new MultiMap<T,T>();
416 for (T key : dependencies.keySet()) {
417 deps.putVoid(key);
418 for (T val : dependencies.get(key)) {
419 deps.putVoid(val);
420 deps.put(key, val);
421 }
422 }
423
424 int size = deps.size();
425 List<T> sorted = new ArrayList<T>();
426 for (int i=0; i<size; ++i) {
427 T parentless = null;
428 for (T key : deps.keySet()) {
429 if (deps.get(key).size() == 0) {
430 parentless = key;
431 break;
432 }
433 }
434 if (parentless == null) throw new RuntimeException();
435 sorted.add(parentless);
436 deps.remove(parentless);
437 for (T key : deps.keySet()) {
438 deps.remove(key, parentless);
439 }
440 }
441 if (sorted.size() != size) throw new RuntimeException();
442 return sorted;
443 }
444
445 /**
446 * Represents a function that can be applied to objects of {@code A} and
447 * returns objects of {@code B}.
448 * @param <A> class of input objects
449 * @param <B> class of transformed objects
450 */
451 public static interface Function<A, B> {
452
453 /**
454 * Applies the function on {@code x}.
455 * @param x an object of
456 * @return the transformed object
457 */
458 B apply(A x);
459 }
460
461 /**
462 * Transforms the collection {@code c} into an unmodifiable collection and
463 * applies the {@link Function} {@code f} on each element upon access.
464 * @param <A> class of input collection
465 * @param <B> class of transformed collection
466 * @param c a collection
467 * @param f a function that transforms objects of {@code A} to objects of {@code B}
468 * @return the transformed unmodifiable collection
469 */
470 public static <A, B> Collection<B> transform(final Collection<? extends A> c, final Function<A, B> f) {
471 return new AbstractCollection<B>() {
472
473 @Override
474 public int size() {
475 return c.size();
476 }
477
478 @Override
479 public Iterator<B> iterator() {
480 return new Iterator<B>() {
481
482 private Iterator<? extends A> it = c.iterator();
483
484 @Override
485 public boolean hasNext() {
486 return it.hasNext();
487 }
488
489 @Override
490 public B next() {
491 return f.apply(it.next());
492 }
493
494 @Override
495 public void remove() {
496 throw new UnsupportedOperationException();
497 }
498 };
499 }
500 };
501 }
502
503 /**
504 * Transforms the list {@code l} into an unmodifiable list and
505 * applies the {@link Function} {@code f} on each element upon access.
506 * @param <A> class of input collection
507 * @param <B> class of transformed collection
508 * @param l a collection
509 * @param f a function that transforms objects of {@code A} to objects of {@code B}
510 * @return the transformed unmodifiable list
511 */
512 public static <A, B> List<B> transform(final List<? extends A> l, final Function<A, B> f) {
513 return new AbstractList<B>() {
514
515
516 @Override
517 public int size() {
518 return l.size();
519 }
520
521 @Override
522 public B get(int index) {
523 return f.apply(l.get(index));
524 }
525
526
527 };
528 }
529
530 /**
531 * Convert Hex String to Color.
532 * @param s Must be of the form "#34a300" or "#3f2", otherwise throws Exception.
533 * Upper/lower case does not matter.
534 * @return The corresponding color.
535 */
536 static public Color hexToColor(String s) {
537 String clr = s.substring(1);
538 if (clr.length() == 3) {
539 clr = new String(new char[] {
540 clr.charAt(0), clr.charAt(0), clr.charAt(1), clr.charAt(1), clr.charAt(2), clr.charAt(2)
541 });
542 }
543 if (clr.length() != 6)
544 throw new IllegalArgumentException();
545 return new Color(Integer.parseInt(clr, 16));
546 }
547
548 /**
549 * Opens a HTTP connection to the given URL and sets the User-Agent property to JOSM's one.
550 * @param httpURL The HTTP url to open (must use http:// or https://)
551 * @return An open HTTP connection to the given URL
552 * @throws IOException if an I/O exception occurs.
553 * @since 5587
554 */
555 public static HttpURLConnection openHttpConnection(URL httpURL) throws IOException {
556 if (httpURL == null || !httpURL.getProtocol().matches("https?")) {
557 throw new IllegalArgumentException("Invalid HTTP url");
558 }
559 HttpURLConnection connection = (HttpURLConnection) httpURL.openConnection();
560 connection.setRequestProperty("User-Agent", Version.getInstance().getFullAgentString());
561 return connection;
562 }
563
564 /**
565 * Opens a connection to the given URL and sets the User-Agent property to JOSM's one.
566 * @param url The url to open
567 * @return An stream for the given URL
568 * @throws IOException if an I/O exception occurs.
569 * @since 5867
570 */
571 public static InputStream openURL(URL url) throws IOException {
572 URLConnection connection = url.openConnection();
573 connection.setRequestProperty("User-Agent", Version.getInstance().getFullAgentString());
574 connection.setConnectTimeout(Main.pref.getInteger("socket.timeout.connect",15)*1000);
575 connection.setReadTimeout(Main.pref.getInteger("socket.timeout.read",30)*1000);
576 return connection.getInputStream();
577 }
578
579 /**
580 * Opens a connection to the given URL and sets the User-Agent property to JOSM's one.
581 * @param url The url to open
582 * @return An buffered stream reader for the given URL (using UTF-8)
583 * @throws IOException if an I/O exception occurs.
584 * @since 5867
585 */
586 public static BufferedReader openURLReader(URL url) throws IOException {
587 return new BufferedReader(new InputStreamReader(openURL(url), "utf-8"));
588 }
589
590 /**
591 * Opens a HTTP connection to the given URL, sets the User-Agent property to JOSM's one and optionnaly disables Keep-Alive.
592 * @param httpURL The HTTP url to open (must use http:// or https://)
593 * @param keepAlive
594 * @return An open HTTP connection to the given URL
595 * @throws IOException if an I/O exception occurs.
596 * @since 5587
597 */
598 public static HttpURLConnection openHttpConnection(URL httpURL, boolean keepAlive) throws IOException {
599 HttpURLConnection connection = openHttpConnection(httpURL);
600 if (!keepAlive) {
601 connection.setRequestProperty("Connection", "close");
602 }
603 return connection;
604 }
605
606 /**
607 * An alternative to {@link String#trim()} to effectively remove all leading and trailing white characters, including Unicode ones.
608 * @see <a href="http://closingbraces.net/2008/11/11/javastringtrim/">Java’s String.trim has a strange idea of whitespace</a>
609 * @see <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4080617">JDK bug 4080617</a>
610 * @param str The string to strip
611 * @return <code>str</code>, without leading and trailing characters, according to
612 * {@link Character#isWhitespace(char)} and {@link Character#isSpaceChar(char)}.
613 * @since 5772
614 */
615 public static String strip(String str) {
616 if (str == null || str.isEmpty()) {
617 return str;
618 }
619 int start = 0, end = str.length();
620 boolean leadingWhite = true;
621 while (leadingWhite && start < end) {
622 char c = str.charAt(start);
623 leadingWhite = (Character.isWhitespace(c) || Character.isSpaceChar(c));
624 if (leadingWhite) {
625 start++;
626 }
627 }
628 boolean trailingWhite = true;
629 while (trailingWhite && end > start+1) {
630 char c = str.charAt(end-1);
631 trailingWhite = (Character.isWhitespace(c) || Character.isSpaceChar(c));
632 if (trailingWhite) {
633 end--;
634 }
635 }
636 return str.substring(start, end);
637 }
638}
Note: See TracBrowser for help on using the repository browser.