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    }