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

Last change on this file since 5577 was 5577, checked in by bastiK, 11 years ago

fixed #8187 - eval(#colour) doesn't result in #colour value suitable for color

  • Property svn:eol-style set to native
File size: 16.8 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.File;
13import java.io.IOException;
14import java.io.InputStream;
15import java.io.OutputStream;
16import java.io.Reader;
17import java.io.UnsupportedEncodingException;
18import java.security.MessageDigest;
19import java.security.NoSuchAlgorithmException;
20import java.text.MessageFormat;
21import java.util.ArrayList;
22import java.util.Collection;
23import java.util.Iterator;
24import java.util.List;
25
26/**
27 * Basic utils, that can be useful in different parts of the program.
28 */
29public class Utils {
30
31 public static <T> boolean exists(Iterable<? extends T> collection, Predicate<? super T> predicate) {
32 for (T item : collection) {
33 if (predicate.evaluate(item))
34 return true;
35 }
36 return false;
37 }
38
39 public static <T> boolean exists(Iterable<T> collection, Class<? extends T> klass) {
40 for (Object item : collection) {
41 if (klass.isInstance(item))
42 return true;
43 }
44 return false;
45 }
46
47 public static <T> T find(Iterable<? extends T> collection, Predicate<? super T> predicate) {
48 for (T item : collection) {
49 if (predicate.evaluate(item))
50 return item;
51 }
52 return null;
53 }
54
55 @SuppressWarnings("unchecked")
56 public static <T> T find(Iterable<? super T> collection, Class<? extends T> klass) {
57 for (Object item : collection) {
58 if (klass.isInstance(item))
59 return (T) item;
60 }
61 return null;
62 }
63
64 public static <T> Collection<T> filter(Collection<? extends T> collection, Predicate<? super T> predicate) {
65 return new FilteredCollection<T>(collection, predicate);
66 }
67
68 public static <T> T firstNonNull(T... items) {
69 for (T i : items) {
70 if (i != null) {
71 return i;
72 }
73 }
74 return null;
75 }
76
77 /**
78 * Filter a collection by (sub)class.
79 * This is an efficient read-only implementation.
80 */
81 public static <S, T extends S> SubclassFilteredCollection<S, T> filteredCollection(Collection<S> collection, final Class<T> klass) {
82 return new SubclassFilteredCollection<S, T>(collection, new Predicate<S>() {
83 @Override
84 public boolean evaluate(S o) {
85 return klass.isInstance(o);
86 }
87 });
88 }
89
90 public static <T> int indexOf(Iterable<? extends T> collection, Predicate<? super T> predicate) {
91 int i = 0;
92 for (T item : collection) {
93 if (predicate.evaluate(item))
94 return i;
95 i++;
96 }
97 return -1;
98 }
99
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
549 /**
550 * Convert Hex String to Color.
551 * @param s Must be of the form "#34a300" or "#3f2", otherwise throws Exception.
552 * Upper/lower case does not matter.
553 * @return The corresponding color.
554 */
555 static public Color hexToColor(String s) {
556 String clr = s.substring(1);
557 if (clr.length() == 3) {
558 clr = new String(new char[] {
559 clr.charAt(0), clr.charAt(0), clr.charAt(1), clr.charAt(1), clr.charAt(2), clr.charAt(2)
560 });
561 }
562 if (clr.length() != 6)
563 throw new IllegalArgumentException();
564 return new Color(Integer.parseInt(clr, 16));
565 }
566
567}
Note: See TracBrowser for help on using the repository browser.