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    
020    package org.crsh.util;
021    
022    import javax.naming.Context;
023    import java.io.ByteArrayOutputStream;
024    import java.io.Closeable;
025    import java.io.Flushable;
026    import java.io.IOException;
027    import java.io.InputStream;
028    import java.io.OutputStream;
029    import java.lang.reflect.ParameterizedType;
030    import java.lang.reflect.Type;
031    import java.lang.reflect.TypeVariable;
032    import java.net.Socket;
033    import java.sql.*;
034    import java.io.File;
035    import java.net.URISyntaxException;
036    import java.net.URL;
037    import java.util.ArrayList;
038    import java.util.Arrays;
039    import java.util.Collections;
040    import java.util.HashMap;
041    import java.util.HashSet;
042    import java.util.Iterator;
043    import java.util.List;
044    import java.util.Map;
045    import java.util.NoSuchElementException;
046    import java.util.regex.Matcher;
047    import java.util.regex.Pattern;
048    
049    public 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    }