001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.tools; 003 004 import java.awt.Color; 005 import java.awt.Toolkit; 006 import java.awt.datatransfer.Clipboard; 007 import java.awt.datatransfer.ClipboardOwner; 008 import java.awt.datatransfer.DataFlavor; 009 import java.awt.datatransfer.StringSelection; 010 import java.awt.datatransfer.Transferable; 011 import java.awt.datatransfer.UnsupportedFlavorException; 012 import java.io.File; 013 import java.io.IOException; 014 import java.io.InputStream; 015 import java.io.OutputStream; 016 import java.io.Reader; 017 import java.io.UnsupportedEncodingException; 018 import java.security.MessageDigest; 019 import java.security.NoSuchAlgorithmException; 020 import java.text.MessageFormat; 021 import java.util.ArrayList; 022 import java.util.Collection; 023 import java.util.Iterator; 024 import java.util.List; 025 026 /** 027 * Basic utils, that can be useful in different parts of the program. 028 */ 029 public class Utils { 030 031 public static <T> boolean exists(Iterable<? extends T> collection, Predicate<? super T> predicate) { 032 for (T item : collection) { 033 if (predicate.evaluate(item)) 034 return true; 035 } 036 return false; 037 } 038 039 public static <T> boolean exists(Iterable<T> collection, Class<? extends T> klass) { 040 for (Object item : collection) { 041 if (klass.isInstance(item)) 042 return true; 043 } 044 return false; 045 } 046 047 public static <T> T find(Iterable<? extends T> collection, Predicate<? super T> predicate) { 048 for (T item : collection) { 049 if (predicate.evaluate(item)) 050 return item; 051 } 052 return null; 053 } 054 055 @SuppressWarnings("unchecked") 056 public static <T> T find(Iterable<? super T> collection, Class<? extends T> klass) { 057 for (Object item : collection) { 058 if (klass.isInstance(item)) 059 return (T) item; 060 } 061 return null; 062 } 063 064 public static <T> Collection<T> filter(Collection<? extends T> collection, Predicate<? super T> predicate) { 065 return new FilteredCollection<T>(collection, predicate); 066 } 067 068 public static <T> T firstNonNull(T... items) { 069 for (T i : items) { 070 if (i != null) { 071 return i; 072 } 073 } 074 return null; 075 } 076 077 /** 078 * Filter a collection by (sub)class. 079 * This is an efficient read-only implementation. 080 */ 081 public static <S, T extends S> SubclassFilteredCollection<S, T> filteredCollection(Collection<S> collection, final Class<T> klass) { 082 return new SubclassFilteredCollection<S, T>(collection, new Predicate<S>() { 083 @Override 084 public boolean evaluate(S o) { 085 return klass.isInstance(o); 086 } 087 }); 088 } 089 090 public static <T> int indexOf(Iterable<? extends T> collection, Predicate<? super T> predicate) { 091 int i = 0; 092 for (T item : collection) { 093 if (predicate.evaluate(item)) 094 return i; 095 i++; 096 } 097 return -1; 098 } 099 100 /** 101 * Get minimum of 3 values 102 */ 103 public static int min(int a, int b, int c) { 104 if (b < c) { 105 if (a < b) 106 return a; 107 return b; 108 } else { 109 if (a < c) 110 return a; 111 return c; 112 } 113 } 114 115 public static int max(int a, int b, int c, int d) { 116 return Math.max(Math.max(a, b), Math.max(c, d)); 117 } 118 119 /** 120 * for convenience: test whether 2 objects are either both null or a.equals(b) 121 */ 122 public static <T> boolean equal(T a, T b) { 123 if (a == b) 124 return true; 125 return (a != null && a.equals(b)); 126 } 127 128 public static void ensure(boolean condition, String message, Object...data) { 129 if (!condition) 130 throw new AssertionError( 131 MessageFormat.format(message,data) 132 ); 133 } 134 135 /** 136 * return the modulus in the range [0, n) 137 */ 138 public static int mod(int a, int n) { 139 if (n <= 0) 140 throw new IllegalArgumentException(); 141 int res = a % n; 142 if (res < 0) { 143 res += n; 144 } 145 return res; 146 } 147 148 /** 149 * Joins a list of strings (or objects that can be converted to string via 150 * Object.toString()) into a single string with fields separated by sep. 151 * @param sep the separator 152 * @param values collection of objects, null is converted to the 153 * empty string 154 * @return null if values is null. The joined string otherwise. 155 */ 156 public static String join(String sep, Collection<?> values) { 157 if (sep == null) 158 throw new IllegalArgumentException(); 159 if (values == null) 160 return null; 161 if (values.isEmpty()) 162 return ""; 163 StringBuilder s = null; 164 for (Object a : values) { 165 if (a == null) { 166 a = ""; 167 } 168 if (s != null) { 169 s.append(sep).append(a.toString()); 170 } else { 171 s = new StringBuilder(a.toString()); 172 } 173 } 174 return s.toString(); 175 } 176 177 public static String joinAsHtmlUnorderedList(Collection<?> values) { 178 StringBuilder sb = new StringBuilder(1024); 179 sb.append("<ul>"); 180 for (Object i : values) { 181 sb.append("<li>").append(i).append("</li>"); 182 } 183 sb.append("</ul>"); 184 return sb.toString(); 185 } 186 187 /** 188 * convert Color to String 189 * (Color.toString() omits alpha value) 190 */ 191 public static String toString(Color c) { 192 if (c == null) 193 return "null"; 194 if (c.getAlpha() == 255) 195 return String.format("#%06x", c.getRGB() & 0x00ffffff); 196 else 197 return String.format("#%06x(alpha=%d)", c.getRGB() & 0x00ffffff, c.getAlpha()); 198 } 199 200 /** 201 * convert float range 0 <= x <= 1 to integer range 0..255 202 * when dealing with colors and color alpha value 203 * @return null if val is null, the corresponding int if val is in the 204 * range 0...1. If val is outside that range, return 255 205 */ 206 public static Integer color_float2int(Float val) { 207 if (val == null) 208 return null; 209 if (val < 0 || val > 1) 210 return 255; 211 return (int) (255f * val + 0.5f); 212 } 213 214 /** 215 * convert back 216 */ 217 public static Float color_int2float(Integer val) { 218 if (val == null) 219 return null; 220 if (val < 0 || val > 255) 221 return 1f; 222 return ((float) val) / 255f; 223 } 224 225 public static Color complement(Color clr) { 226 return new Color(255 - clr.getRed(), 255 - clr.getGreen(), 255 - clr.getBlue(), clr.getAlpha()); 227 } 228 229 public static int copyStream(InputStream source, OutputStream destination) throws IOException { 230 int count = 0; 231 byte[] b = new byte[512]; 232 int read; 233 while ((read = source.read(b)) != -1) { 234 count += read; 235 destination.write(b, 0, read); 236 } 237 return count; 238 } 239 240 public static boolean deleteDirectory(File path) { 241 if( path.exists() ) { 242 File[] files = path.listFiles(); 243 for(int i=0; i<files.length; i++) { 244 if(files[i].isDirectory()) { 245 deleteDirectory(files[i]); 246 } 247 else { 248 files[i].delete(); 249 } 250 } 251 } 252 return( path.delete() ); 253 } 254 255 /** 256 * <p>Utility method for closing an input stream.</p> 257 * 258 * @param is the input stream. May be null. 259 */ 260 public static void close(InputStream is){ 261 if (is == null) return; 262 try { 263 is.close(); 264 } catch(IOException e){ 265 // ignore 266 } 267 } 268 269 /** 270 * <p>Utility method for closing an output stream.</p> 271 * 272 * @param os the output stream. May be null. 273 */ 274 public static void close(OutputStream os){ 275 if (os == null) return; 276 try { 277 os.close(); 278 } catch(IOException e){ 279 // ignore 280 } 281 } 282 283 /** 284 * <p>Utility method for closing a reader.</p> 285 * 286 * @param reader the reader. May be null. 287 */ 288 public static void close(Reader reader){ 289 if (reader == null) return; 290 try { 291 reader.close(); 292 } catch(IOException e){ 293 // ignore 294 } 295 } 296 297 private final static double EPSILION = 1e-11; 298 299 public static boolean equalsEpsilon(double a, double b) { 300 return Math.abs(a - b) <= EPSILION; 301 } 302 303 /** 304 * Copies the string {@code s} to system clipboard. 305 * @param s string to be copied to clipboard. 306 * @return true if succeeded, false otherwise. 307 */ 308 public static boolean copyToClipboard(String s) { 309 try { 310 Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(s), new ClipboardOwner() { 311 312 @Override 313 public void lostOwnership(Clipboard clpbrd, Transferable t) { 314 } 315 }); 316 return true; 317 } catch (IllegalStateException ex) { 318 ex.printStackTrace(); 319 return false; 320 } 321 } 322 323 /** 324 * Extracts clipboard content as string. 325 * @return string clipboard contents if available, {@code null} otherwise. 326 */ 327 public static String getClipboardContent() { 328 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 329 Transferable t = null; 330 for (int tries = 0; t == null && tries < 10; tries++) { 331 try { 332 t = clipboard.getContents(null); 333 } catch (IllegalStateException e) { 334 // Clipboard currently unavailable. On some platforms, the system clipboard is unavailable while it is accessed by another application. 335 try { 336 Thread.sleep(1); 337 } catch (InterruptedException ex) { 338 } 339 } 340 } 341 try { 342 if (t != null && t.isDataFlavorSupported(DataFlavor.stringFlavor)) { 343 String text = (String) t.getTransferData(DataFlavor.stringFlavor); 344 return text; 345 } 346 } catch (UnsupportedFlavorException ex) { 347 ex.printStackTrace(); 348 return null; 349 } catch (IOException ex) { 350 ex.printStackTrace(); 351 return null; 352 } 353 return null; 354 } 355 356 /** 357 * Calculate MD5 hash of a string and output in hexadecimal format. 358 * Output has length 32 with characters in range [0-9a-f] 359 */ 360 public static String md5Hex(String data) { 361 byte[] byteData = null; 362 try { 363 byteData = data.getBytes("UTF-8"); 364 } catch (UnsupportedEncodingException e) { 365 throw new RuntimeException(); 366 } 367 MessageDigest md = null; 368 try { 369 md = MessageDigest.getInstance("MD5"); 370 } catch (NoSuchAlgorithmException e) { 371 throw new RuntimeException(); 372 } 373 byte[] byteDigest = md.digest(byteData); 374 return toHexString(byteDigest); 375 } 376 377 /** 378 * Converts a byte array to a string of hexadecimal characters. Preserves leading zeros, so the 379 * size of the output string is always twice the number of input bytes. 380 */ 381 public static String toHexString(byte[] bytes) { 382 char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; 383 char[] hexChars = new char[bytes.length * 2]; 384 for (int j=0; j<bytes.length; j++) { 385 int v = bytes[j] & 0xFF; 386 hexChars[j*2] = hexArray[v/16]; 387 hexChars[j*2 + 1] = hexArray[v%16]; 388 } 389 return new String(hexChars); 390 } 391 392 /** 393 * Topological sort. 394 * 395 * @param dependencies contains mappings (key -> value). In the final list of sorted objects, the key will come 396 * after the value. (In other words, the key depends on the value(s).) 397 * There must not be cyclic dependencies. 398 * @return the list of sorted objects 399 */ 400 public static <T> List<T> topologicalSort(final MultiMap<T,T> dependencies) { 401 MultiMap<T,T> deps = new MultiMap<T,T>(); 402 for (T key : dependencies.keySet()) { 403 deps.putVoid(key); 404 for (T val : dependencies.get(key)) { 405 deps.putVoid(val); 406 deps.put(key, val); 407 } 408 } 409 410 int size = deps.size(); 411 List<T> sorted = new ArrayList<T>(); 412 for (int i=0; i<size; ++i) { 413 T parentless = null; 414 for (T key : deps.keySet()) { 415 if (deps.get(key).size() == 0) { 416 parentless = key; 417 break; 418 } 419 } 420 if (parentless == null) throw new RuntimeException(); 421 sorted.add(parentless); 422 deps.remove(parentless); 423 for (T key : deps.keySet()) { 424 deps.remove(key, parentless); 425 } 426 } 427 if (sorted.size() != size) throw new RuntimeException(); 428 return sorted; 429 } 430 431 /** 432 * Represents a function that can be applied to objects of {@code A} and 433 * returns objects of {@code B}. 434 * @param <A> class of input objects 435 * @param <B> class of transformed objects 436 */ 437 public static interface Function<A, B> { 438 439 /** 440 * Applies the function on {@code x}. 441 * @param x an object of 442 * @return the transformed object 443 */ 444 B apply(A x); 445 } 446 447 /** 448 * Transforms the collection {@code c} into an unmodifiable collection and 449 * applies the {@link Function} {@code f} on each element upon access. 450 * @param <A> class of input collection 451 * @param <B> class of transformed collection 452 * @param c a collection 453 * @param f a function that transforms objects of {@code A} to objects of {@code B} 454 * @return the transformed unmodifiable collection 455 */ 456 public static <A, B> Collection<B> transform(final Collection<? extends A> c, final Function<A, B> f) { 457 return new Collection<B>() { 458 459 @Override 460 public int size() { 461 return c.size(); 462 } 463 464 @Override 465 public boolean isEmpty() { 466 return c.isEmpty(); 467 } 468 469 @Override 470 public boolean contains(Object o) { 471 return c.contains(o); 472 } 473 474 @Override 475 public Object[] toArray() { 476 return c.toArray(); 477 } 478 479 @Override 480 public <T> T[] toArray(T[] a) { 481 return c.toArray(a); 482 } 483 484 @Override 485 public String toString() { 486 return c.toString(); 487 } 488 489 @Override 490 public Iterator<B> iterator() { 491 return new Iterator<B>() { 492 493 private Iterator<? extends A> it = c.iterator(); 494 495 @Override 496 public boolean hasNext() { 497 return it.hasNext(); 498 } 499 500 @Override 501 public B next() { 502 return f.apply(it.next()); 503 } 504 505 @Override 506 public void remove() { 507 throw new UnsupportedOperationException(); 508 } 509 }; 510 } 511 512 @Override 513 public boolean add(B e) { 514 throw new UnsupportedOperationException(); 515 } 516 517 @Override 518 public boolean remove(Object o) { 519 throw new UnsupportedOperationException(); 520 } 521 522 @Override 523 public boolean containsAll(Collection<?> c) { 524 throw new UnsupportedOperationException(); 525 } 526 527 @Override 528 public boolean addAll(Collection<? extends B> c) { 529 throw new UnsupportedOperationException(); 530 } 531 532 @Override 533 public boolean removeAll(Collection<?> c) { 534 throw new UnsupportedOperationException(); 535 } 536 537 @Override 538 public boolean retainAll(Collection<?> c) { 539 throw new UnsupportedOperationException(); 540 } 541 542 @Override 543 public void clear() { 544 throw new UnsupportedOperationException(); 545 } 546 }; 547 } 548 }