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