View Javadoc

1   /***
2    *     Ambient - A music player for the Android platform
3    Copyright (C) 2007 Martin Vysny
4    
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9    
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14  
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  package sk.baka.ambient.commons;
19  
20  import java.io.ByteArrayInputStream;
21  import java.io.ByteArrayOutputStream;
22  import java.io.Closeable;
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.ObjectInputStream;
28  import java.io.ObjectOutputStream;
29  import java.io.ObjectStreamClass;
30  import java.io.OutputStream;
31  import java.io.PrintWriter;
32  import java.io.Serializable;
33  import java.io.StringWriter;
34  import java.net.ServerSocket;
35  import java.net.Socket;
36  import java.text.ParseException;
37  import java.text.SimpleDateFormat;
38  import java.util.ArrayList;
39  import java.util.Arrays;
40  import java.util.Date;
41  import java.util.Formatter;
42  import java.util.List;
43  import java.util.Locale;
44  import java.util.concurrent.Callable;
45  
46  import sk.baka.ambient.AmbientApplication;
47  import android.util.Log;
48  
49  /***
50   * Miscellaneous utility methods.
51   * 
52   * @author Martin Vysny
53   */
54  public final class MiscUtils {
55  	private MiscUtils() {
56  		throw new Error();
57  	}
58  
59  	/***
60  	 * Compares two objects. Handles <code>null</code>s correctly. For return
61  	 * value please see the {@link Comparable#compareTo(Object)} contract.
62  	 * 
63  	 * @param <T>
64  	 *            the object type
65  	 * @param o1
66  	 *            first object
67  	 * @param o2
68  	 *            second object
69  	 * @param nullFirst
70  	 *            if <code>true</code> then <code>null</code> is less than any
71  	 *            non-<code>null</code> object. If <code>false</code> then
72  	 *            <code>null</code> is greater than any non-null object.
73  	 * @return see {@link Comparable#compareTo(Object)} for details.
74  	 */
75  	public static <T extends Comparable<? super T>> int nullCompare(final T o1,
76  			final T o2, final boolean nullFirst) {
77  		final int nullCompareToObject = nullFirst ? -1 : 1;
78  		return o1 == null ? (o2 == null ? 0 : nullCompareToObject)
79  				: (o2 == null ? -nullCompareToObject : o1.compareTo(o2));
80  	}
81  
82  	/***
83  	 * Returns next enum constant. If there is no next constant then returns the
84  	 * first one.
85  	 * 
86  	 * @param <T>
87  	 *            the enum type
88  	 * @param constant
89  	 *            the constant, must not be <code>null</code>.
90  	 * @param next
91  	 *            if <code>true</code> then next constant is returned, if
92  	 *            <code>false</code> then simply given constant is returned.
93  	 * @return the next constant.
94  	 */
95  	public static <T extends Enum<T>> T nextOrThis(final T constant,
96  			final boolean next) {
97  		if (!next)
98  			return constant;
99  		final Class<T> clazz = constant.getDeclaringClass();
100 		final T[] values = clazz.getEnumConstants();
101 		int ordinal = constant.ordinal() + 1;
102 		if (ordinal >= values.length)
103 			ordinal = 0;
104 		return values[ordinal];
105 	}
106 
107 	/***
108 	 * Returns all interfaces that given class and all superclasses implements.
109 	 * 
110 	 * @param clazz
111 	 *            the class to analyze
112 	 * @return interface list that the class is castable to.
113 	 */
114 	public static List<Class<?>> getInterfaces(final Class<?> clazz) {
115 		Class<?> cl = clazz;
116 		final List<Class<?>> result = new ArrayList<Class<?>>();
117 		for (; cl != Object.class; cl = cl.getSuperclass()) {
118 			final List<Class<?>> interfaces = Arrays.<Class<?>> asList(cl
119 					.getInterfaces());
120 			result.addAll(interfaces);
121 		}
122 		return result;
123 	}
124 
125 	/***
126 	 * If given string is empty, contains spaces only or is <code>null</code>
127 	 * then <code>null</code> is returned.
128 	 * 
129 	 * @param str
130 	 *            the string.
131 	 * @return string or <code>null</code> if one of the above applies.
132 	 */
133 	public static String nullIfEmptyOrWhitespace(final String str) {
134 		if (str == null)
135 			return null;
136 		if (str.trim().length() == 0)
137 			return null;
138 		return str;
139 	}
140 
141 	/***
142 	 * Returns given string. If given string is <code>null</code> then an empty
143 	 * string is returned.
144 	 * 
145 	 * @param str
146 	 *            the string.
147 	 * @return non-<code>null</code> string.
148 	 */
149 	public static String emptyIfNull(final String str) {
150 		return str == null ? "" : str;
151 	}
152 
153 	/***
154 	 * Checks if given string is empty or consists of whitespace characters
155 	 * only.
156 	 * 
157 	 * @param str
158 	 *            the string to check.
159 	 * @return <code>true</code> if the string is empty, <code>false</code>
160 	 *         otherwise.
161 	 */
162 	public static boolean isEmptyOrWhitespace(final String str) {
163 		return null == nullIfEmptyOrWhitespace(str);
164 	}
165 
166 	/***
167 	 * Checks if given string is empty (<code>null</code> or zero-sized).
168 	 * 
169 	 * @param str
170 	 *            the string to check.
171 	 * @return <code>true</code> if the string is empty, <code>false</code>
172 	 *         otherwise.
173 	 */
174 	public static boolean isEmpty(final String str) {
175 		return (str == null) || (str.length() == 0);
176 	}
177 
178 	/***
179 	 * Formats given string. Uses {@link Formatter}.
180 	 * 
181 	 * @param resId
182 	 *            formatting string id
183 	 * @param args
184 	 *            parameters
185 	 * @return formatted string
186 	 */
187 	public static String format(final int resId, Object... args) {
188 		final String formatStr = AmbientApplication.getInstance().getString(
189 				resId);
190 		return new Formatter().format(formatStr, args).toString();
191 	}
192 
193 	/***
194 	 * Similar to {@link #nullCompare(Comparable, Comparable, boolean)}, but
195 	 * uses {@link #equals(Object)} instead.
196 	 * 
197 	 * @param <T>
198 	 *            the object type
199 	 * @param o1
200 	 *            first object
201 	 * @param o2
202 	 *            second object
203 	 * @return <code>true</code> if either o1 is equal to o2, or both are
204 	 *         <code>null</code>s; <code>false</code> otherwise.
205 	 */
206 	public static <T> boolean nullEquals(final T o1, final T o2) {
207 		return o1 == null ? o2 == null : o1.equals(o2);
208 	}
209 
210 	/***
211 	 * Deserializes an object from contents of given file.
212 	 * 
213 	 * @param file
214 	 *            the file to read
215 	 * @return deserialized object.
216 	 * @throws IOException
217 	 */
218 	public static Serializable deserializeFromFile(final File file)
219 			throws IOException {
220 		return deserialize(new FileInputStream(file));
221 	}
222 
223 	/***
224 	 * Deserializes an object from an input stream. The stream is closed.
225 	 * 
226 	 * @param stream
227 	 *            the stream to read
228 	 * @return deserialized object.
229 	 * @throws IOException
230 	 */
231 	public static Serializable deserialize(final InputStream stream)
232 			throws IOException {
233 		if (stream == null)
234 			return null;
235 		try {
236 			final ObjectInputStream in = new ObjectInputStream(stream) {
237 				@Override
238 				protected Class<?> resolveClass(
239 						ObjectStreamClass objectStreamClass)
240 						throws ClassNotFoundException {
241 					return Class.forName(objectStreamClass.getName(), true,
242 							MiscUtils.class.getClassLoader());
243 				}
244 			};
245 			try {
246 				return (Serializable) in.readObject();
247 			} catch (ClassNotFoundException e) {
248 				throw new RuntimeException(e);
249 			}
250 		} finally {
251 			closeQuietly(stream);
252 		}
253 	}
254 
255 	/***
256 	 * Serializes given object to the output stream. The stream is closed.
257 	 * 
258 	 * @param object
259 	 *            the object to serialize
260 	 * @param out
261 	 *            serialize here
262 	 * @throws IOException
263 	 */
264 	public static void serialize(final Serializable object,
265 			final OutputStream out) throws IOException {
266 		try {
267 			final ObjectOutputStream oout = new ObjectOutputStream(out);
268 			oout.writeObject(object);
269 			closeQuietly(oout);
270 		} finally {
271 			closeQuietly(out);
272 		}
273 	}
274 
275 	/***
276 	 * Serializes given object to the output stream. The stream is closed.
277 	 * 
278 	 * @param object
279 	 *            the object to serialize
280 	 * @return serialized form
281 	 */
282 	public static byte[] serializeToBytes(final Serializable object) {
283 		if (object == null)
284 			return null;
285 		final ByteArrayOutputStream out = new ByteArrayOutputStream();
286 		try {
287 			serialize(object, out);
288 		} catch (IOException e) {
289 			throw new RuntimeException(e);
290 		}
291 		return out.toByteArray();
292 	}
293 
294 	/***
295 	 * Deserializes an object from a byte array.
296 	 * 
297 	 * @param serialized
298 	 *            the serialized object form
299 	 * @return deserialized object.
300 	 */
301 	public static Serializable deserialize(final byte[] serialized) {
302 		if (serialized == null)
303 			return null;
304 		try {
305 			return deserialize(new ByteArrayInputStream(serialized));
306 		} catch (IOException e) {
307 			// shouldn't happen unless the stream is corrupted
308 			throw new RuntimeException(e);
309 		}
310 	}
311 
312 	/***
313 	 * Closes the closeable object. Catches any exception.
314 	 * 
315 	 * @param c
316 	 *            close this closeable. Does nothing if the stream is
317 	 *            <code>null</code>.
318 	 */
319 	public static void closeQuietly(final Closeable c) {
320 		if (c == null) {
321 			return;
322 		}
323 		try {
324 			c.close();
325 		} catch (Exception e) {
326 			Log.w(MiscUtils.class.getSimpleName(), "Failed to close object", e);
327 		}
328 	}
329 
330 	/***
331 	 * Closes a socket quietly.
332 	 * 
333 	 * @param socket
334 	 *            the socket to close.
335 	 */
336 	public static void closeQuietly(final Socket socket) {
337 		closeQuietly(new Closeable() {
338 			public void close() throws IOException {
339 				socket.close();
340 			}
341 		});
342 	}
343 
344 	/***
345 	 * Closes a server socket quietly.
346 	 * 
347 	 * @param socket
348 	 *            the socket to close.
349 	 */
350 	public static void closeQuietly(final ServerSocket socket) {
351 		closeQuietly(new Closeable() {
352 			public void close() throws IOException {
353 				socket.close();
354 			}
355 		});
356 	}
357 
358 	/***
359 	 * Iterates over given iterable and returns its items as a list.
360 	 * 
361 	 * @param <T>
362 	 *            the item type
363 	 * @param i
364 	 *            the iterable
365 	 * @return list, never <code>null</code>.
366 	 */
367 	public static <T> List<T> iterableToList(final Iterable<? extends T> i) {
368 		final List<T> result = new ArrayList<T>();
369 		for (final T t : i) {
370 			result.add(t);
371 		}
372 		return result;
373 	}
374 
375 	/***
376 	 * SI prefixes.
377 	 */
378 	private static final char[] PREFIXES = new char[] { 'k', 'M', 'G', 'T', 'P' };
379 
380 	/***
381 	 * Formats the length and returns it.
382 	 * 
383 	 * @param length
384 	 *            in bytes
385 	 * @return xxKb/xxMb/etc
386 	 */
387 	public static String formatByteLength(final long length) {
388 		long len = length * 10; // show a single-digit decimal fraction
389 		int prefix = -1;
390 		while ((len > 10240) && (prefix < PREFIXES.length)) {
391 			len /= 1024;
392 			prefix++;
393 		}
394 		final StringBuilder result = new StringBuilder();
395 		if (prefix < 0) {
396 			result.append(length);
397 			result.append('b');
398 			return result.toString();
399 		}
400 		result.append(len);
401 		result.insert(result.length() - 1, '.');
402 		if (result.charAt(result.length() - 1) == '0') {
403 			result.delete(result.length() - 2, result.length());
404 		}
405 		result.append(PREFIXES[prefix]);
406 		result.append('b');
407 		return result.toString();
408 	}
409 
410 	/***
411 	 * Moves the leading "the" word to the end of given name.
412 	 * 
413 	 * @param artistAlbum
414 	 *            an artist/album name
415 	 * @return fixed name
416 	 */
417 	public static String fixArtistAlbumName(final String artistAlbum) {
418 		if (artistAlbum == null) {
419 			return null;
420 		}
421 		final String name = artistAlbum.trim();
422 		if (name.length() < 5) {
423 			return name;
424 		}
425 		if (!name.substring(0, 3).equalsIgnoreCase("the")
426 				|| !Character.isWhitespace(name.charAt(3))) {
427 			return name;
428 		}
429 		int i = 4;
430 		for (; (i < name.length()) && Character.isWhitespace(name.charAt(i)); i++) {
431 		}
432 		return name.substring(i) + ", " + name.substring(0, 3);
433 	}
434 
435 	/***
436 	 * May be used in maps and/or sets which does not support <code>null</code>.
437 	 */
438 	public static final Object NULL = new Object();
439 
440 	/***
441 	 * Wraps given object to a reference. The purpose of this reference is to
442 	 * enforce reference-equality in place of object-equality. In other words,
443 	 * two references returned by this method are considered equal if and only
444 	 * if <code>r1.get() == r2.get()</code>.
445 	 * 
446 	 * @param <T>
447 	 *            the object type.
448 	 * @param object
449 	 *            the object instance. <code>null</code>s are handled correctly,
450 	 *            i.e. <code>null</code> is only equal to <code>null</code>.
451 	 * @return a reference to given object.
452 	 */
453 	public static <T> Ref<T> getIdentity(final T object) {
454 		return new Ref<T>() {
455 			public T get() {
456 				return object;
457 			}
458 
459 			@Override
460 			public boolean equals(Object o) {
461 				if (o == null)
462 					return false;
463 				if (o == this)
464 					return true;
465 				if (o.getClass() != getClass())
466 					return false;
467 				return ((Ref<?>) o).get() == object;
468 			}
469 
470 			@Override
471 			public int hashCode() {
472 				return object == null ? 0 : System.identityHashCode(object);
473 			}
474 		};
475 	}
476 
477 	/***
478 	 * Returns object hash code. Returns 0 if <code>null</code> was given.
479 	 * 
480 	 * @param o
481 	 *            the object to compute hash code for.
482 	 * @return the hash code.
483 	 */
484 	public static int hashCode(final Object o) {
485 		return o == null ? 0 : o.hashCode();
486 	}
487 
488 	/***
489 	 * Parses RFC 2822 date.
490 	 * 
491 	 * @param date
492 	 *            the date to parse.
493 	 * @return parsed date.
494 	 * @throws ParseException
495 	 *             if parse fails.
496 	 */
497 	public static Date parseRFC2822Date(final String date)
498 			throws ParseException {
499 		final String pattern = "EEE, d MMM yyyy HH:mm:ss Z";
500 		final SimpleDateFormat sdf = new SimpleDateFormat(pattern,
501 				Locale.ENGLISH);
502 		return sdf.parse(date);
503 	}
504 
505 	/***
506 	 * Formats given date as a RFC 2822 date.
507 	 * 
508 	 * @param date
509 	 *            the date to format.
510 	 * @return formatted date.
511 	 */
512 	public static String getRFC2822Date(final Date date) {
513 		final String pattern = "EEE, d MMM yyyy HH:mm:ss Z";
514 		final SimpleDateFormat sdf = new SimpleDateFormat(pattern,
515 				Locale.ENGLISH);
516 		return sdf.format(date);
517 	}
518 
519 	private static final char[] HEX = new char[] { '0', '1', '2', '3', '4',
520 			'5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
521 
522 	/***
523 	 * Formats each byte in the array in its hexadecimal form and returns it.
524 	 * 
525 	 * @param array
526 	 *            the array to print
527 	 * @return hexadecimal format
528 	 */
529 	public static String toHexa(final byte[] array) {
530 		if (array == null) {
531 			return "null";
532 		}
533 		final StringBuilder b = new StringBuilder(array.length * 2);
534 		for (final byte by : array) {
535 			int bval = by;
536 			if (bval < 0) {
537 				bval += 256;
538 			}
539 			b.append(HEX[bval >> 4]);
540 			b.append(HEX[bval & 15]);
541 		}
542 		return b.toString();
543 	}
544 
545 	/***
546 	 * Converts hexadecimal string returned by {@link #toHexa(byte[])} back to
547 	 * the original array.
548 	 * 
549 	 * @param hexaArray
550 	 *            the array returned by {@link #toHexa(byte[])}.
551 	 * @return the array.
552 	 */
553 	public static byte[] fromHexa(final String hexaArray) {
554 		if (hexaArray == null || hexaArray.equals("null")) {
555 			return null;
556 		}
557 		final int length = hexaArray.length() / 2;
558 		final byte[] result = new byte[length];
559 		for (int i = 0; i < length; i++) {
560 			final int pos = i * 2;
561 			result[i] = (byte) (getHexa(hexaArray.charAt(pos)) * 16 + getHexa(hexaArray
562 					.charAt(pos + 1)));
563 		}
564 		return result;
565 	}
566 
567 	private static byte getHexa(final char hexa) {
568 		final int result = Arrays.binarySearch(HEX, hexa);
569 		if (result < 0) {
570 			throw new IllegalArgumentException("Not a hexa character: " + hexa);
571 		}
572 		return (byte) result;
573 	}
574 
575 	/***
576 	 * Returns given exception (including its stack trace) as a string.
577 	 * 
578 	 * @param throwable
579 	 *            the throwable.
580 	 * @return string representation of the throwable.
581 	 */
582 	public static String getStackTrace(final Throwable throwable) {
583 		final StringWriter writer = new StringWriter();
584 		final PrintWriter pw = new PrintWriter(writer);
585 		throwable.printStackTrace(pw);
586 		pw.flush();
587 		return writer.toString();
588 	}
589 
590 	/***
591 	 * Sysout for the fucking Android.
592 	 * 
593 	 * @param string
594 	 *            the fucking string.
595 	 */
596 	public static void sysout(Object string) {
597 		Log.e("Sysout", "sysout: " + string);
598 	}
599 
600 	/***
601 	 * Converts a {@link Callable} to a {@link Runnable}.
602 	 * 
603 	 * @param callable
604 	 *            the callable to convert, must not be <code>null</code>.
605 	 * @return runnable instance.
606 	 */
607 	public static Runnable toRunnable(final Callable<?> callable) {
608 		if (callable == null) {
609 			throw new NullArgumentException("callable");
610 		}
611 		return new Runnable() {
612 			public void run() {
613 				try {
614 					callable.call();
615 				} catch (Exception e) {
616 					throw new RuntimeException(e);
617 				}
618 			}
619 		};
620 	}
621 }