001/*
002 * Copyright (C) 2012 eXo Platform SAS.
003 *
004 * This is free software; you can redistribute it and/or modify it
005 * under the terms of the GNU Lesser General Public License as
006 * published by the Free Software Foundation; either version 2.1 of
007 * the License, or (at your option) any later version.
008 *
009 * This software is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * You should have received a copy of the GNU Lesser General Public
015 * License along with this software; if not, write to the Free
016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018 */
019
020package org.crsh.util;
021
022import javax.naming.Context;
023import java.io.ByteArrayOutputStream;
024import java.io.Closeable;
025import java.io.Flushable;
026import java.io.IOException;
027import java.io.InputStream;
028import java.io.OutputStream;
029import java.lang.reflect.ParameterizedType;
030import java.lang.reflect.Type;
031import java.lang.reflect.TypeVariable;
032import java.net.Socket;
033import java.sql.*;
034import java.io.File;
035import java.net.URISyntaxException;
036import java.net.URL;
037import java.util.ArrayList;
038import java.util.Arrays;
039import java.util.Collections;
040import java.util.HashMap;
041import java.util.HashSet;
042import java.util.Iterator;
043import java.util.List;
044import java.util.Map;
045import java.util.NoSuchElementException;
046import java.util.regex.Matcher;
047import java.util.regex.Pattern;
048
049public class Utils {
050
051  /** . */
052  private static final Iterator EMPTY_ITERATOR = Collections.emptyList().iterator();
053  /** . */
054  private static final Pattern p = Pattern.compile("\\S+");
055
056  public static <E> Iterator<E> iterator() {
057    @SuppressWarnings("unchecked")
058    Iterator<E> iterator = (Iterator<E>)EMPTY_ITERATOR;
059    return iterator;
060  }
061
062  public static <E> Iterator<E> iterator(final E element) {
063    return new BaseIterator<E>() {
064      boolean hasNext = true;
065      @Override
066      public boolean hasNext() {
067        return hasNext;
068      }
069      @Override
070      public E next() {
071        if (hasNext) {
072          hasNext = false;
073          return element;
074        } else {
075          throw new NoSuchElementException();
076        }
077      }
078    };
079  }
080
081  public static <E> E first(Iterable<E> elements) {
082    Iterator<E> i = elements.iterator();
083    return i.hasNext() ? i.next() : null;
084  }
085
086  public static <K, V, M extends Map<K, V>> M map(M map, K key, V value) {
087    map.put(key, value);
088    return map;
089  }
090
091  public static <K, V> HashMap<K, V> map(K key, V value) {
092    HashMap<K, V> map = new HashMap<K, V>();
093    map.put(key, value);
094    return map;
095  }
096
097  public static <E> HashSet<E> set(E... elements) {
098    HashSet<E> set = new HashSet<E>(elements.length);
099    Collections.addAll(set, elements);
100    return set;
101  }
102
103  public static <E> List<E> list(E... elements) {
104    return Arrays.asList(elements);
105  }
106
107  public static <E> List<E> list(Iterable<E> iterable) {
108    return list(iterable.iterator());
109  }
110
111  public static <E> List<E> list(Iterator<E> iterator) {
112    ArrayList<E> list = new ArrayList<E>();
113    while (iterator.hasNext()) {
114      list.add(iterator.next());
115    }
116    return list;
117  }
118
119  public static int indexOf(CharSequence s, int off, char c) {
120    for (int len = s.length();off < len;off++) {
121      if (s.charAt(off) == c) {
122        return off;
123      }
124    }
125    return -1;
126  }
127
128  public static String trimLeft(String s) {
129    if (s == null) {
130      throw new NullPointerException("No null string accepted");
131    }
132    int index = 0;
133    int len = s.length();
134    while (index < len) {
135      if (s.charAt(index) == ' ') {
136        index++;
137      } else {
138        break;
139      }
140    }
141    if (index > 0) {
142      return s.substring(index);
143    } else {
144      return s;
145    }
146  }
147
148  private static final Pattern blankPattern = Pattern.compile("^\\s*$");
149
150  public static boolean notBlank(String s) {
151    return !blankPattern.matcher(s).find();
152  }
153
154  public static <E> E notNull(E e1, E e2) {
155    if (e1 != null) {
156      return e1;
157    } else {
158      return e2;
159    }
160  }
161
162  private static final Pattern FOO = Pattern.compile("" +
163      "(\\*)" + // Wildcard *
164      "|" +
165      "(\\?)" + // Wildcard ?
166      "|" +
167      "(?:\\[([^)]+)\\])" + // Range
168      "|" +
169      "(\\\\.)" // Escape
170  );
171
172  /**
173   * Create a pattern that transforms a glob expression into a regular expression, the following task are supported
174   * <ul>
175   *   <li>* : Match any number of unknown characters</li>
176   *   <li>? : Match one unknown character</li>
177   *   <li>[characters] : Match a character as part of a group of characters</li>
178   *   <li>\ : Escape character</li>
179   * </ul>
180   *
181   * @param globex the glob expression
182   * @return the regular expression
183   * @throws NullPointerException when the globex argument is null
184   */
185  public static String globexToRegex(String globex) throws NullPointerException {
186    if (globex == null) {
187      throw new NullPointerException("No null globex accepted");
188    }
189    StringBuilder regex = new StringBuilder();
190    int prev = 0;
191    Matcher matcher = FOO.matcher(globex);
192    while (matcher.find()) {
193      int next = matcher.start();
194      if (next > prev) {
195        regex.append(Pattern.quote(globex.substring(prev, next)));
196      }
197      if (matcher.group(1) != null) {
198        regex.append(".*");
199      } else if (matcher.group(2) != null) {
200        regex.append(".");
201      } else if (matcher.group(3) != null) {
202        regex.append("[");
203        regex.append(Pattern.quote(matcher.group(3)));
204        regex.append("]");
205      } else if (matcher.group(4) != null) {
206        regex.append(Pattern.quote(Character.toString(matcher.group(4).charAt(1))));
207      } else {
208        throw new UnsupportedOperationException("Not handled yet");
209      }
210      prev = matcher.end();
211    }
212    if (prev < globex.length()) {
213      regex.append(Pattern.quote(globex.substring(prev)));
214    }
215    return regex.toString();
216  }
217
218  /**
219<<<<<<< HEAD
220   * Close the socket and catch any exception thrown.
221   *
222   * @param socket the socket to close
223   * @return any Exception thrown during the <code>close</code> operation
224   */
225  public static Exception close(Socket socket) {
226    if (socket != null) {
227      try {
228        socket.close();
229      }
230      catch (Exception e) {
231        return e;
232      }
233    }
234    return null;
235  }
236
237  /**
238   * Close the closeable and catch any exception thrown.
239   *
240   * @param closeable the closeable to close
241   * @return any Exception thrown during the <code>close</code> operation
242   */
243  public static Exception close(Closeable closeable) {
244    if (closeable != null) {
245      try {
246        closeable.close();
247      }
248      catch (Exception e) {
249        return e;
250      }
251    }
252    return null;
253  }
254
255  /**
256   * Close the connection and catch any exception thrown.
257   *
258   * @param connection the socket to close
259   * @return any Exception thrown during the <code>close</code> operation
260   */
261  public static Exception close(Connection connection) {
262    if (connection != null) {
263      try {
264        connection.close();
265      }
266      catch (Exception e) {
267        return e;
268      }
269    }
270    return null;
271  }
272
273  /**
274   * Close the statement and catch any exception thrown.
275   *
276   * @param statement the statement to close
277   * @return any Exception thrown during the <code>close</code> operation
278   */
279  public static Exception close(java.sql.Statement statement) {
280    if (statement != null) {
281      try {
282        statement.close();
283      }
284      catch (Exception e) {
285        return e;
286      }
287    }
288    return null;
289  }
290
291  /**
292   * Close the result set and catch any exception thrown.
293   *
294   * @param rs the result set to close
295   * @return any Exception thrown during the <code>close</code> operation
296   */
297  public static Exception close(ResultSet rs) {
298    if (rs != null) {
299      try {
300        rs.close();
301      }
302      catch (Exception e) {
303        return e;
304      }
305    }
306    return null;
307  }
308
309  /**
310   * Close the context and catch any exception thrown.
311   *
312   * @param context the context to close
313   * @return any Exception thrown during the <code>close</code> operation
314   */
315   public static Exception close(Context context) {
316      if (context != null) {
317         try {
318            context.close();
319         }
320         catch (Exception e) {
321           return e;
322         }
323      }
324     return null;
325   }
326
327  public static <T extends Throwable> void rethrow(Class<T> throwableClass, Throwable cause) throws T {
328    T throwable;
329
330    //
331    try {
332      throwable = throwableClass.newInstance();
333    }
334    catch (Exception e) {
335      throw new AssertionError(e);
336    }
337
338    //
339    throwable.initCause(cause);
340
341    //
342    throw throwable;
343  }
344
345  public static boolean equals(Object o1, Object o2) {
346    return o1 == null ? o2 == null : (o2 != null && o1.equals(o2));
347  }
348
349  public static boolean notEquals(Object o1, Object o2) {
350    return !equals(o1, o2);
351  }
352
353  /**
354   * Flush the flushable and catch any exception thrown.
355   *
356   * @param flushable the flushable to flush
357   * @return any Exception thrown during the <code>flush</code> operation
358   */
359  public static Exception flush(Flushable flushable) {
360    if (flushable != null) {
361      try {
362        flushable.flush();
363      }
364      catch (Exception e) {
365        return e;
366      }
367    }
368    return null;
369  }
370
371  public static List<String> chunks(CharSequence s) {
372    List<String> chunks = new ArrayList<String>();
373    Matcher m = p.matcher(s);
374    while (m.find()) {
375      chunks.add(m.group());
376    }
377    return chunks;
378  }
379
380  public static String join(Iterable<String> strings, String separator) {
381    Iterator<String> i = strings.iterator();
382    if (i.hasNext()) {
383      String first = i.next();
384      if (i.hasNext()) {
385        StringBuilder buf = new StringBuilder();
386        buf.append(first);
387        while (i.hasNext()) {
388          buf.append(separator);
389          buf.append(i.next());
390        }
391        return buf.toString();
392      } else {
393        return first;
394      }
395    } else {
396      return "";
397    }
398  }
399
400  public static String[] split(CharSequence s, char separator) {
401    return foo(s, separator, 0, 0, 0);
402  }
403
404  public static String[] split(CharSequence s, char separator, int rightPadding) {
405    if (rightPadding < 0) {
406      throw new IllegalArgumentException("Right padding cannot be negative");
407    }
408    return foo(s, separator, 0, 0, rightPadding);
409  }
410
411  private static String[] foo(CharSequence s, char separator, int count, int from, int rightPadding) {
412    int len = s.length();
413    if (from < len) {
414      int to = from;
415      while (to < len && s.charAt(to) != separator) {
416        to++;
417      }
418      String[] ret;
419      if (to == len - 1) {
420        ret = new String[count + 2 + rightPadding];
421        ret[count + 1] = "";
422      }
423      else {
424        ret = to == len ? new String[count + 1 + rightPadding] : foo(s, separator, count + 1, to + 1, rightPadding);
425      }
426      ret[count] = from == to ? "" : s.subSequence(from, to).toString();
427      return ret;
428    }
429    else if (from == len) {
430      return new String[count + rightPadding];
431    }
432    else {
433      throw new AssertionError();
434    }
435  }
436
437  /**
438   * @see #findLongestCommonPrefix(Iterable)
439   */
440  public static String findLongestCommonPrefix(CharSequence... seqs) {
441    return findLongestCommonPrefix(Arrays.asList(seqs));
442  }
443
444  /**
445   * Find the longest possible common prefix of the provided char sequence.
446   *
447   * @param seqs the sequences
448   * @return the longest possible prefix
449   */
450  public static String findLongestCommonPrefix(Iterable<? extends CharSequence> seqs) {
451    String common = "";
452    out:
453    while (true) {
454      String candidate = null;
455      for (CharSequence s : seqs) {
456        if (common.length() + 1 > s.length()) {
457          break out;
458        } else {
459          if (candidate == null) {
460            candidate = s.subSequence(0, common.length() + 1).toString();
461          } else if (s.subSequence(0, common.length() + 1).toString().equals(candidate)) {
462            // Ok it is a prefix
463          } else {
464            break out;
465          }
466        }
467      }
468      if (candidate == null) {
469        break;
470      } else {
471        common = candidate;
472      }
473    }
474    return common;
475  }
476
477  public static byte[] readAsBytes(InputStream in) throws IOException {
478    return read(in).toByteArray();
479  }
480
481  public static String readAsUTF8(InputStream in) {
482    try {
483      ByteArrayOutputStream baos = read(in);
484      return baos.toString("UTF-8");
485    }
486    catch (IOException e) {
487      e.printStackTrace();
488      return null;
489    }
490  }
491
492  public static void copy(InputStream in, OutputStream out) throws IOException {
493    if (in == null) {
494      throw new NullPointerException();
495    }
496    try {
497      byte[] buffer = new byte[256];
498      for (int l = in.read(buffer); l != -1; l = in.read(buffer)) {
499        out.write(buffer, 0, l);
500      }
501    }
502    finally {
503      close(in);
504    }
505  }
506
507  private static ByteArrayOutputStream read(InputStream in) throws IOException {
508    if (in == null) {
509      throw new NullPointerException();
510    }
511    ByteArrayOutputStream baos = new ByteArrayOutputStream();
512    copy(in, baos);
513    return baos;
514  }
515
516  /*
517   * Convert an file URL to a file, avoids issues on windows with whitespaces.
518   *
519   * @param url the URL to convert
520   * @return the related file
521   * @throws java.lang.IllegalArgumentException if the url protocol is not file
522   * @throws java.lang.NullPointerException if the url argument is null
523   */
524  public static File toFile(URL url) throws IllegalArgumentException, NullPointerException {
525    if (url == null) {
526      throw new NullPointerException("No null URL accepted");
527    }
528    if (!url.getProtocol().equals("file")) {
529      throw new IllegalArgumentException("Not file protocol");
530    }
531    try {
532      return new File(url.toURI());
533    } catch(URISyntaxException e) {
534      return new File(url.getPath());
535    }
536  }
537
538  public static Class<?> resolveToClass(Type implementation, Class<?> type, int parameterIndex) {
539    if (implementation == null) {
540      throw new NullPointerException("No null type accepted");
541    }
542
543    // First resolve to type
544    Type resolvedType = resolve(implementation, type, parameterIndex);
545
546    //
547    if (resolvedType != null) {
548      return resolveToClass(resolvedType);
549    } else {
550      return null;
551    }
552  }
553
554  public static Class resolveToClass(Type type) {
555    if (type == null) {
556      throw new NullPointerException("No null type accepted");
557    }
558    if (type instanceof Class<?>) {
559      return (Class<?>)type;
560    } else if (type instanceof TypeVariable) {
561      TypeVariable resolvedTypeVariable = (TypeVariable)type;
562      return resolveToClass(resolvedTypeVariable.getBounds()[0]);
563    } else {
564      throw new UnsupportedOperationException("Type resolution of " + type + " not yet implemented");
565    }
566  }
567
568  /**
569   * A simplistic implementation, it may not handle all cases but it should handle enough.
570   *
571   * @param implementation the type for which the parameter requires a resolution
572   * @param type the type that owns the parameter
573   * @param parameterIndex the parameter index
574   * @return the resolved type
575   */
576  public static Type resolve(Type implementation, Class<?> type, int parameterIndex) {
577    if (implementation == null) {
578      throw new NullPointerException();
579    }
580
581    //
582    if (implementation == type) {
583      TypeVariable<? extends Class<?>>[] tp = type.getTypeParameters();
584      if (parameterIndex < tp.length) {
585        return tp[parameterIndex];
586      } else {
587        throw new IllegalArgumentException();
588      }
589    } else if (implementation instanceof Class<?>) {
590      Class<?> c = (Class<?>) implementation;
591      Type gsc = c.getGenericSuperclass();
592      Type resolved = null;
593      if (gsc != null) {
594        resolved = resolve(gsc, type, parameterIndex);
595        if (resolved == null) {
596          // Try with interface
597        }
598      }
599      return resolved;
600    } else if (implementation instanceof ParameterizedType) {
601      ParameterizedType pt = (ParameterizedType) implementation;
602      Type[] typeArgs = pt.getActualTypeArguments();
603      Type rawType = pt.getRawType();
604      if (rawType == type) {
605        return typeArgs[parameterIndex];
606      } else if (rawType instanceof Class<?>) {
607        Class<?> classRawType = (Class<?>)rawType;
608        Type resolved = resolve(classRawType, type, parameterIndex);
609        if (resolved == null) {
610          return null;
611        } else if (resolved instanceof TypeVariable) {
612          TypeVariable resolvedTV = (TypeVariable)resolved;
613          TypeVariable[] a = classRawType.getTypeParameters();
614          for (int i = 0;i < a.length;i++) {
615            if (a[i].equals(resolvedTV)) {
616              return resolve(implementation, classRawType, i);
617            }
618          }
619          throw new AssertionError();
620        } else {
621          throw new UnsupportedOperationException("Cannot support resolution of " + resolved);
622        }
623      } else {
624        throw new UnsupportedOperationException();
625      }
626    } else {
627      throw new UnsupportedOperationException("todo " + implementation + " " + implementation.getClass());
628    }
629  }
630
631  public static boolean instanceOf(Class c, List<String> types) {
632
633    for (String type: types) {
634      if (instanceOf(c, type)) {
635        return true;
636      }
637    }
638
639    return false;
640
641  }
642
643  public static boolean instanceOf(Class c, String type) {
644
645    if (c.getName().equals(type)) {
646      return true;
647    }
648
649    for (Class i : c.getInterfaces()) {
650      if (instanceOf(i, type)) {
651        return true;
652      }
653    }
654
655    if (c.getSuperclass() != null) {
656      return instanceOf(c.getSuperclass(), type);
657    }
658
659    return false;
660  }
661}