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.nio.charset.Charset;
034    import java.sql.*;
035    import java.io.File;
036    import java.net.URISyntaxException;
037    import java.net.URL;
038    import java.util.ArrayList;
039    import java.util.Arrays;
040    import java.util.Collections;
041    import java.util.HashMap;
042    import java.util.HashSet;
043    import java.util.Iterator;
044    import java.util.List;
045    import java.util.Map;
046    import java.util.NoSuchElementException;
047    import java.util.regex.Matcher;
048    import java.util.regex.Pattern;
049    
050    public class Utils {
051    
052      /** . */
053      private static final Iterator EMPTY_ITERATOR = Collections.emptyList().iterator();
054    
055      /** . */
056      private static final Pattern p = Pattern.compile("\\S+");
057    
058      public static <E> Iterator<E> iterator() {
059        @SuppressWarnings("unchecked")
060        Iterator<E> iterator = (Iterator<E>)EMPTY_ITERATOR;
061        return iterator;
062      }
063    
064      public static <E> Iterator<E> iterator(final E element) {
065        return new BaseIterator<E>() {
066          boolean hasNext = true;
067          @Override
068          public boolean hasNext() {
069            return hasNext;
070          }
071          @Override
072          public E next() {
073            if (hasNext) {
074              hasNext = false;
075              return element;
076            } else {
077              throw new NoSuchElementException();
078            }
079          }
080        };
081      }
082    
083      public static <E> E first(Iterable<E> elements) {
084        Iterator<E> i = elements.iterator();
085        return i.hasNext() ? i.next() : null;
086      }
087    
088      public static <K, V, M extends Map<K, V>> M map(M map, K key, V value) {
089        map.put(key, value);
090        return map;
091      }
092    
093      public static <K, V> HashMap<K, V> map(K key, V value) {
094        HashMap<K, V> map = new HashMap<K, V>();
095        map.put(key, value);
096        return map;
097      }
098    
099      public static <E> HashSet<E> set(E... elements) {
100        HashSet<E> set = new HashSet<E>(elements.length);
101        Collections.addAll(set, elements);
102        return set;
103      }
104    
105      public static <E> List<E> list(E... elements) {
106        return Arrays.asList(elements);
107      }
108    
109      public static <E> List<E> list(Iterable<E> iterable) {
110        return list(iterable.iterator());
111      }
112    
113      public static <E> List<E> list(Iterator<E> iterator) {
114        ArrayList<E> list = new ArrayList<E>();
115        while (iterator.hasNext()) {
116          list.add(iterator.next());
117        }
118        return list;
119      }
120    
121      public static int indexOf(CharSequence s, int off, char c) {
122        for (int len = s.length();off < len;off++) {
123          if (s.charAt(off) == c) {
124            return off;
125          }
126        }
127        return -1;
128      }
129    
130      public static String trimLeft(String s) {
131        if (s == null) {
132          throw new NullPointerException("No null string accepted");
133        }
134        int index = 0;
135        int len = s.length();
136        while (index < len) {
137          if (s.charAt(index) == ' ') {
138            index++;
139          } else {
140            break;
141          }
142        }
143        if (index > 0) {
144          return s.substring(index);
145        } else {
146          return s;
147        }
148      }
149    
150      private static final Pattern blankPattern = Pattern.compile("^\\s*$");
151    
152      public static boolean notBlank(String s) {
153        return !blankPattern.matcher(s).find();
154      }
155    
156      public static <E> E notNull(E e1, E e2) {
157        if (e1 != null) {
158          return e1;
159        } else {
160          return e2;
161        }
162      }
163    
164      /**
165       * Return the value when it is positive otherwise return 0.
166       *
167       * @param value the value
168       * @return the non negative value
169       */
170      public static int notNegative(int value) {
171        return value >= 0 ? value : 0;
172      }
173    
174      private static final Pattern FOO = Pattern.compile("" +
175          "(\\*)" + // Wildcard *
176          "|" +
177          "(\\?)" + // Wildcard ?
178          "|" +
179          "(?:\\[([^)]+)\\])" + // Range
180          "|" +
181          "(\\\\.)" // Escape
182      );
183    
184      /**
185       * Create a pattern that transforms a glob expression into a regular expression, the following task are supported
186       * <ul>
187       *   <li>* : Match any number of unknown characters</li>
188       *   <li>? : Match one unknown character</li>
189       *   <li>[characters] : Match a character as part of a group of characters</li>
190       *   <li>\ : Escape character</li>
191       * </ul>
192       *
193       * @param globex the glob expression
194       * @return the regular expression
195       * @throws NullPointerException when the globex argument is null
196       */
197      public static String globexToRegex(String globex) throws NullPointerException {
198        if (globex == null) {
199          throw new NullPointerException("No null globex accepted");
200        }
201        StringBuilder regex = new StringBuilder();
202        int prev = 0;
203        Matcher matcher = FOO.matcher(globex);
204        while (matcher.find()) {
205          int next = matcher.start();
206          if (next > prev) {
207            regex.append(Pattern.quote(globex.substring(prev, next)));
208          }
209          if (matcher.group(1) != null) {
210            regex.append(".*");
211          } else if (matcher.group(2) != null) {
212            regex.append(".");
213          } else if (matcher.group(3) != null) {
214            regex.append("[");
215            regex.append(Pattern.quote(matcher.group(3)));
216            regex.append("]");
217          } else if (matcher.group(4) != null) {
218            regex.append(Pattern.quote(Character.toString(matcher.group(4).charAt(1))));
219          } else {
220            throw new UnsupportedOperationException("Not handled yet");
221          }
222          prev = matcher.end();
223        }
224        if (prev < globex.length()) {
225          regex.append(Pattern.quote(globex.substring(prev)));
226        }
227        return regex.toString();
228      }
229    
230      /**
231    <<<<<<< HEAD
232       * Close the socket and catch any exception thrown.
233       *
234       * @param socket the socket to close
235       * @return any Exception thrown during the <code>close</code> operation
236       */
237      public static Exception close(Socket socket) {
238        if (socket != null) {
239          try {
240            socket.close();
241          }
242          catch (Exception e) {
243            return e;
244          }
245        }
246        return null;
247      }
248    
249      /**
250       * Close the closeable and catch any exception thrown.
251       *
252       * @param closeable the closeable to close
253       * @return any Exception thrown during the <code>close</code> operation
254       */
255      public static Exception close(Closeable closeable) {
256        if (closeable != null) {
257          try {
258            closeable.close();
259          }
260          catch (Exception e) {
261            return e;
262          }
263        }
264        return null;
265      }
266    
267      /**
268       * Close the connection and catch any exception thrown.
269       *
270       * @param connection the socket to close
271       * @return any Exception thrown during the <code>close</code> operation
272       */
273      public static Exception close(Connection connection) {
274        if (connection != null) {
275          try {
276            connection.close();
277          }
278          catch (Exception e) {
279            return e;
280          }
281        }
282        return null;
283      }
284    
285      /**
286       * Close the statement and catch any exception thrown.
287       *
288       * @param statement the statement to close
289       * @return any Exception thrown during the <code>close</code> operation
290       */
291      public static Exception close(java.sql.Statement statement) {
292        if (statement != null) {
293          try {
294            statement.close();
295          }
296          catch (Exception e) {
297            return e;
298          }
299        }
300        return null;
301      }
302    
303      /**
304       * Close the result set and catch any exception thrown.
305       *
306       * @param rs the result set to close
307       * @return any Exception thrown during the <code>close</code> operation
308       */
309      public static Exception close(ResultSet rs) {
310        if (rs != null) {
311          try {
312            rs.close();
313          }
314          catch (Exception e) {
315            return e;
316          }
317        }
318        return null;
319      }
320    
321      /**
322       * Close the context and catch any exception thrown.
323       *
324       * @param context the context to close
325       * @return any Exception thrown during the <code>close</code> operation
326       */
327       public static Exception close(Context context) {
328          if (context != null) {
329             try {
330                context.close();
331             }
332             catch (Exception e) {
333               return e;
334             }
335          }
336         return null;
337       }
338    
339      public static <T extends Throwable> void rethrow(Class<T> throwableClass, Throwable cause) throws T {
340        T throwable;
341    
342        //
343        try {
344          throwable = throwableClass.newInstance();
345        }
346        catch (Exception e) {
347          throw new AssertionError(e);
348        }
349    
350        //
351        throwable.initCause(cause);
352    
353        //
354        throw throwable;
355      }
356    
357      public static boolean equals(Object o1, Object o2) {
358        return o1 == null ? o2 == null : (o2 != null && o1.equals(o2));
359      }
360    
361      public static boolean notEquals(Object o1, Object o2) {
362        return !equals(o1, o2);
363      }
364    
365      /**
366       * Flush the flushable and catch any exception thrown.
367       *
368       * @param flushable the flushable to flush
369       * @return any Exception thrown during the <code>flush</code> operation
370       */
371      public static Exception flush(Flushable flushable) {
372        if (flushable != null) {
373          try {
374            flushable.flush();
375          }
376          catch (Exception e) {
377            return e;
378          }
379        }
380        return null;
381      }
382    
383      public static List<String> chunks(CharSequence s) {
384        List<String> chunks = new ArrayList<String>();
385        Matcher m = p.matcher(s);
386        while (m.find()) {
387          chunks.add(m.group());
388        }
389        return chunks;
390      }
391    
392      public static String join(Iterable<String> strings, String separator) {
393        Iterator<String> i = strings.iterator();
394        if (i.hasNext()) {
395          String first = i.next();
396          if (i.hasNext()) {
397            StringBuilder buf = new StringBuilder();
398            buf.append(first);
399            while (i.hasNext()) {
400              buf.append(separator);
401              buf.append(i.next());
402            }
403            return buf.toString();
404          } else {
405            return first;
406          }
407        } else {
408          return "";
409        }
410      }
411    
412      public static String[] split(CharSequence s, char separator) {
413        return foo(s, separator, 0, 0, 0);
414      }
415    
416      public static String[] split(CharSequence s, char separator, int rightPadding) {
417        if (rightPadding < 0) {
418          throw new IllegalArgumentException("Right padding cannot be negative");
419        }
420        return foo(s, separator, 0, 0, rightPadding);
421      }
422    
423      private static String[] foo(CharSequence s, char separator, int count, int from, int rightPadding) {
424        int len = s.length();
425        if (from < len) {
426          int to = from;
427          while (to < len && s.charAt(to) != separator) {
428            to++;
429          }
430          String[] ret;
431          if (to == len - 1) {
432            ret = new String[count + 2 + rightPadding];
433            ret[count + 1] = "";
434          }
435          else {
436            ret = to == len ? new String[count + 1 + rightPadding] : foo(s, separator, count + 1, to + 1, rightPadding);
437          }
438          ret[count] = from == to ? "" : s.subSequence(from, to).toString();
439          return ret;
440        }
441        else if (from == len) {
442          return new String[count + rightPadding];
443        }
444        else {
445          throw new AssertionError();
446        }
447      }
448    
449      /**
450       * @see #findLongestCommonPrefix(Iterable)
451       */
452      public static String findLongestCommonPrefix(CharSequence... seqs) {
453        return findLongestCommonPrefix(Arrays.asList(seqs));
454      }
455    
456      /**
457       * Find the longest possible common prefix of the provided char sequence.
458       *
459       * @param seqs the sequences
460       * @return the longest possible prefix
461       */
462      public static String findLongestCommonPrefix(Iterable<? extends CharSequence> seqs) {
463        String common = "";
464        out:
465        while (true) {
466          String candidate = null;
467          for (CharSequence s : seqs) {
468            if (common.length() + 1 > s.length()) {
469              break out;
470            } else {
471              if (candidate == null) {
472                candidate = s.subSequence(0, common.length() + 1).toString();
473              } else if (s.subSequence(0, common.length() + 1).toString().equals(candidate)) {
474                // Ok it is a prefix
475              } else {
476                break out;
477              }
478            }
479          }
480          if (candidate == null) {
481            break;
482          } else {
483            common = candidate;
484          }
485        }
486        return common;
487      }
488    
489      public static byte[] readAsBytes(InputStream in) throws IOException {
490        return read(in).toByteArray();
491      }
492    
493      public static String readAsUTF8(InputStream in) {
494        try {
495          ByteArrayOutputStream baos = read(in);
496          return baos.toString("UTF-8");
497        }
498        catch (IOException e) {
499          e.printStackTrace();
500          return null;
501        }
502      }
503    
504      public static void copy(InputStream in, OutputStream out) throws IOException {
505        if (in == null) {
506          throw new NullPointerException();
507        }
508        try {
509          byte[] buffer = new byte[256];
510          for (int l = in.read(buffer); l != -1; l = in.read(buffer)) {
511            out.write(buffer, 0, l);
512          }
513        }
514        finally {
515          close(in);
516        }
517      }
518    
519      private static ByteArrayOutputStream read(InputStream in) throws IOException {
520        if (in == null) {
521          throw new NullPointerException();
522        }
523        ByteArrayOutputStream baos = new ByteArrayOutputStream();
524        copy(in, baos);
525        return baos;
526      }
527    
528      /*
529       * Convert an file URL to a file, avoids issues on windows with whitespaces.
530       *
531       * @param url the URL to convert
532       * @return the related file
533       * @throws java.lang.IllegalArgumentException if the url protocol is not file
534       * @throws java.lang.NullPointerException if the url argument is null
535       */
536      public static File toFile(URL url) throws IllegalArgumentException, NullPointerException {
537        if (url == null) {
538          throw new NullPointerException("No null URL accepted");
539        }
540        if (!url.getProtocol().equals("file")) {
541          throw new IllegalArgumentException("Not file protocol");
542        }
543        try {
544          return new File(url.toURI());
545        } catch(URISyntaxException e) {
546          return new File(url.getPath());
547        }
548      }
549    
550      public static Class<?> resolveToClass(Type implementation, Class<?> type, int parameterIndex) {
551        if (implementation == null) {
552          throw new NullPointerException("No null type accepted");
553        }
554    
555        // First resolve to type
556        Type resolvedType = resolve(implementation, type, parameterIndex);
557    
558        //
559        if (resolvedType != null) {
560          return resolveToClass(resolvedType);
561        } else {
562          return null;
563        }
564      }
565    
566      public static Class resolveToClass(Type type) {
567        if (type == null) {
568          throw new NullPointerException("No null type accepted");
569        }
570        if (type instanceof Class<?>) {
571          return (Class<?>)type;
572        } else if (type instanceof TypeVariable) {
573          TypeVariable resolvedTypeVariable = (TypeVariable)type;
574          return resolveToClass(resolvedTypeVariable.getBounds()[0]);
575        } else {
576          throw new UnsupportedOperationException("Type resolution of " + type + " not yet implemented");
577        }
578      }
579    
580      /**
581       * A simplistic implementation, it may not handle all cases but it should handle enough.
582       *
583       * @param implementation the type for which the parameter requires a resolution
584       * @param type the type that owns the parameter
585       * @param parameterIndex the parameter index
586       * @return the resolved type
587       */
588      public static Type resolve(Type implementation, Class<?> type, int parameterIndex) {
589        if (implementation == null) {
590          throw new NullPointerException();
591        }
592    
593        //
594        if (implementation == type) {
595          TypeVariable<? extends Class<?>>[] tp = type.getTypeParameters();
596          if (parameterIndex < tp.length) {
597            return tp[parameterIndex];
598          } else {
599            throw new IllegalArgumentException();
600          }
601        } else if (implementation instanceof Class<?>) {
602          Class<?> c = (Class<?>) implementation;
603          Type gsc = c.getGenericSuperclass();
604          Type resolved = null;
605          if (gsc != null) {
606            resolved = resolve(gsc, type, parameterIndex);
607            if (resolved == null) {
608              // Try with interface
609            }
610          }
611          return resolved;
612        } else if (implementation instanceof ParameterizedType) {
613          ParameterizedType pt = (ParameterizedType) implementation;
614          Type[] typeArgs = pt.getActualTypeArguments();
615          Type rawType = pt.getRawType();
616          if (rawType == type) {
617            return typeArgs[parameterIndex];
618          } else if (rawType instanceof Class<?>) {
619            Class<?> classRawType = (Class<?>)rawType;
620            Type resolved = resolve(classRawType, type, parameterIndex);
621            if (resolved == null) {
622              return null;
623            } else if (resolved instanceof TypeVariable) {
624              TypeVariable resolvedTV = (TypeVariable)resolved;
625              TypeVariable[] a = classRawType.getTypeParameters();
626              for (int i = 0;i < a.length;i++) {
627                if (a[i].equals(resolvedTV)) {
628                  return resolve(implementation, classRawType, i);
629                }
630              }
631              throw new AssertionError();
632            } else {
633              throw new UnsupportedOperationException("Cannot support resolution of " + resolved);
634            }
635          } else {
636            throw new UnsupportedOperationException();
637          }
638        } else {
639          throw new UnsupportedOperationException("todo " + implementation + " " + implementation.getClass());
640        }
641      }
642    
643      public static boolean instanceOf(Class c, List<String> types) {
644    
645        for (String type: types) {
646          if (instanceOf(c, type)) {
647            return true;
648          }
649        }
650    
651        return false;
652    
653      }
654    
655      public static boolean instanceOf(Class c, String type) {
656    
657        if (c.getName().equals(type)) {
658          return true;
659        }
660    
661        for (Class i : c.getInterfaces()) {
662          if (instanceOf(i, type)) {
663            return true;
664          }
665        }
666    
667        if (c.getSuperclass() != null) {
668          return instanceOf(c.getSuperclass(), type);
669        }
670    
671        return false;
672      }
673    
674      /** . */
675      private static final int ESCAPE = -1, SCANNING = 0, DOLLAR = 1, EVALUATING = 2;
676    
677      /**
678       * Interpolate a string and replace the occurence from a context map, the syntax for a variable
679       * is <code>${}</code> and it can accept a default value used when the variable cannot be resolved
680       * with the <code>:-</code> separator:
681       *
682       * <ul>
683       *   <li><code>{}</code> + "foo" => "foo"</li>
684       *   <li><code>{}</code> + "${foo}" => ""</li>
685       *   <li><code>{}</code> + "\\${foo}" => "${foo}"</li>
686       *   <li><code>{foo:bar}</code> + "${foo}" => "bar"</li>
687       *   <li><code>{}</code> + "${foo:-bar}" => "bar"</li>
688       * </ul>
689       *
690       * @param interpolated the value to interpolate
691       * @param context the context
692       * @return the interpolated string
693       * @throws NullPointerException if the interpolated argument is null
694       */
695      public static String interpolate(String interpolated, Map<?, ?> context) throws NullPointerException {
696        StringBuilder sb = new StringBuilder();
697        int status = 0;
698        int prev = 0;
699        int length = interpolated.length();
700        for (int i = 0;i < length;i++) {
701          char c = interpolated.charAt(i);
702          switch (status) {
703            case ESCAPE:
704              if (c == '$') {
705                sb.append('$');
706              } else {
707                sb.append('\\').append(c);
708              }
709              status = SCANNING;
710              break;
711            case SCANNING:
712              if (c == '$') {
713                status = DOLLAR;
714              } else if (c == '\\') {
715                status = ESCAPE;
716              } else {
717                sb.append(c);
718              }
719              break;
720            case DOLLAR:
721              if (c == '{') {
722                status = EVALUATING;
723                prev = i + 1;
724              } else {
725                sb.append('$').append(c);
726              }
727              break;
728            case EVALUATING:
729              if (c == '}') {
730                int j = prev + 1;
731                while (j < i) {
732                  if (j < length && interpolated.charAt(j - 1) == ':' && interpolated.charAt(j) == '-') {
733                    break;
734                  } else {
735                    j++;
736                  }
737                }
738                Object value;
739                if (j < i) {
740                  String key = interpolated.substring(prev, j - 1);
741                  value = context.get(key);
742                  if (value == null) {
743                    value = interpolated.substring(j + 1, i);
744                  }
745                } else {
746                  String key = interpolated.substring(prev, i);
747                  value = context.get(key);
748                }
749                if (value != null) {
750                  sb.append(value);
751                }
752                status = SCANNING;
753              }
754              break;
755          }
756        }
757        switch (status) {
758          case DOLLAR:
759            sb.append('$');
760            break;
761          case EVALUATING:
762            sb.append("${").append(interpolated, prev, interpolated.length());
763            break;
764        }
765        return sb.toString();
766      }
767    
768      /**
769       * @return the current user directory
770       */
771      public static File getCurrentDirectory() {
772        String userDir = System.getProperty("user.dir");
773        return new File(userDir);
774      }
775    
776      /** . */
777      public static final Charset UTF_8;
778    
779      static {
780        UTF_8 = Charset.forName("UTF-8");
781      }
782    
783    }