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

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

drop unnecessary properties for upload to the OSM API in order to save bandwidth

The properties are: user, uid, visible, action and timestamp. In addition drop lat & lon for deleted nodes. (To undelete a primitive, it suffices to include it in a <modify> section, the 'visible' property is ignored by the server.)

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