All Downloads are FREE. Search and download functionalities are using the official Maven repository.

juzu.impl.common.Tools Maven / Gradle / Ivy

There is a newer version: 1.2.0
Show newest version
/*
 * Copyright 2013 eXo Platform SAS
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package juzu.impl.common;

import juzu.io.UndeclaredIOException;
import juzu.request.ResponseParameter;
import juzu.impl.bridge.Parameters;

import org.w3c.dom.*;

import javax.annotation.processing.Completion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.xml.bind.DatatypeConverter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.Flushable;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;

/** @author Julien Viet */
public class Tools {

  /** . */
  private static final String[] EMPTY_STRING_ARRAY = new String[0];

  /** . */
  private static final ConcurrentMap> PACKAGE_INFO_CLASS_CACHE = new ConcurrentHashMap>();

  private static final Set PACKAGE_INFO_IGNORE_LIST = new HashSet();

  /** . */
  public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");

  /** . */
  public static final Charset ISO_8859_2 = Charset.forName("ISO-8859-2");

  /** . */
  public static final Charset UTF_8 = Charset.forName("UTF-8");


  /** . */
  private static final Iterator EMPTY_ITERATOR = new Iterator() {
    public boolean hasNext() {
      return false;
    }

    public Object next() {
      throw new NoSuchElementException();
    }

    public void remove() {
      throw new UnsupportedOperationException();
    }
  };

  /** . */
  public static final Comparator COMPLETION_COMPARATOR = new Comparator() {
    public int compare(Completion o1, Completion o2) {
      return o1.getValue().compareTo(o2.getValue());
    }
  };

  /** . */
  private static final Iterable EMPTY_ITERABLE = new Iterable() {
    public Iterator iterator() {
      return EMPTY_ITERATOR;
    }
  };

  /** . */
  public static Pattern EMPTY_NO_RECURSE = Pattern.compile("");

  /** . */
  public static Pattern EMPTY_RECURSE = Pattern.compile(".*");

  public static Pattern getPackageMatcher(String packageName, boolean recurse) {

    // PackageName       -> Identifier
    // PackageName       -> PackageName . Identifier
    // Identifier        -> IdentifierChars but not a Keyword or BooleanLiteral or NullLiteral
    // IdentifierChars   -> JavaLetter
    // IdentifierChars   -> IdentifierChars JavaLetterOrDigit
    // JavaLetter        -> any Unicode character that is a Java letter
    // JavaLetterOrDigit -> any Unicode character that is a Java letter-or-digit

    if (packageName.length() == 0) {
      return recurse ? EMPTY_RECURSE : EMPTY_NO_RECURSE;
    }
    else {
      String regex;
      if (recurse) {
        regex = Pattern.quote(packageName) + "(\\..*)?";
      }
      else {
        regex = Pattern.quote(packageName);
      }
      return Pattern.compile(regex);
    }
  }

  /**
   * Returns the parent package of the provided package. Null is returned if the provided package
   * was a top level package.
   *
   * @param pkgName the package name
   * @return the parent package
   */
  public static String parentPackageOf(String pkgName) {
    int index = pkgName.lastIndexOf('.');
    if (index == -1) {
      return null;
    } else {
      return pkgName.substring(0, index);
    }
  }

  public static  Iterable loadService(Class service, ClassLoader loader) {
    final Iterable i = ServiceLoader.load(service, loader);
    return new Iterable() {
      @Override
      public Iterator iterator() {
        return new Iterator() {

          Iterator a = i.iterator();
          T next;

          @Override
          public boolean hasNext() {
            while (a != null && a.hasNext()) {
              try {
                next = a.next();
              }
              catch (Exception e) {
                // Ignore
              }
            }
            return next != null;
          }

          @Override
          public T next() {
            if (!hasNext()) {
              throw new NoSuchElementException();
            } else {
              T tmp = next;
              next = null;
              return tmp;
            }
          }

          @Override
          public void remove() {
            throw new UnsupportedOperationException();
          }
        };
      }
    };
  }

  public static Class getPackageClass(ClassLoader loader, String pkgName) {
    // TODO: This cache should be taken care from AssetService as the root cause of the problem.
    // Let's improve it when working on JUZU-41
    if (PACKAGE_INFO_IGNORE_LIST.contains(pkgName)) {
      return null;
    }

    try {
      String className = pkgName + ".package-info";
      Class c = PACKAGE_INFO_CLASS_CACHE.get(className);
      if (c != null) {
        return c;
      }

      c = loader.loadClass(pkgName + ".package-info");
      PACKAGE_INFO_CLASS_CACHE.put(className, c);
      return c;
    }
    catch (ClassNotFoundException e) {
      PACKAGE_INFO_IGNORE_LIST.add(pkgName);
      return null;
    }
  }

  public static void escape(CharSequence s, StringBuilder appendable) {
    for (int i = 0;i < s.length();i++) {
      char c = s.charAt(i);
      if (c == '\n') {
        appendable.append("\\n");
      }
      else if (c == '\'') {
        appendable.append("\\\'");
      }
      else if (c == '\r') {
        // Skip carriage return
      } else {
        appendable.append(c);
      }
    }
  }

  public static boolean safeEquals(Object o1, Object o2) {
    return o1 == null ? o2 == null : (o2 != null && o1.equals(o2));
  }

  public static void safeClose(Closeable closeable) {
    if (closeable != null) {
      try {
        closeable.close();
      }
      catch (IOException ignore) {
      }
    }
  }

  public static void safeFlush(Flushable flushable) {
    if (flushable != null) {
      try {
        flushable.flush();
      }
      catch (IOException ignore) {
      }
    }
  }

  public static Method safeGetMethod(Class type, String name, Class... parameterTypes) {
    try {
      return type.getDeclaredMethod(name, parameterTypes);
    }
    catch (NoSuchMethodException e) {
      return null;
    }
  }

  public static , E> C addAll(C collection, Iterable elements) {
    for (E element : elements) {
      collection.add(element);
    }
    return collection;
  }

  public static  List safeUnmodifiableList(T... list) {
    return safeUnmodifiableList(Arrays.asList(list));
  }

  public static  List safeUnmodifiableList(List list) {
    if (list == null || list.isEmpty()) {
      return Collections.emptyList();
    }
    else {
      return Collections.unmodifiableList(new ArrayList(list));
    }
  }

  public static byte[] bytes(URL url) throws IOException {
    return bytes(url.openStream());
  }

  public static byte[] bytes(InputStream in) throws IOException {
    try {
      ByteArrayOutputStream baos = new ByteArrayOutputStream(in.available());
      copy(in, baos);
      return baos.toByteArray();
    }
    finally {
      safeClose(in);
    }
  }

  public static void write(String content, File f) throws IOException {
    write(content.getBytes(), f);
  }

  public static void write(byte[] content, File f) throws IOException {
    FileOutputStream out = new FileOutputStream(f);
    try {
      copy(new ByteArrayInputStream(content), out);
    }
    finally {
      safeClose(out);
    }
  }

  public static Map responseHeaders(HttpURLConnection conn) {
    Map headers = Collections.emptyMap();
    for (int i=0; ; i++) {
      String name = conn.getHeaderFieldKey(i);
      String value = conn.getHeaderField(i);
      if (name == null && value == null) {
        break;
      }
      if (name != null) {
        if (headers.isEmpty()) {
          headers = new HashMap();
        }
        headers.put(name, value);
      }
    }
    return headers;
  }

  public static String etag(String id, long lastModified) {
    byte[] tmp = id.getBytes(Tools.UTF_8);
    int len = tmp.length;
    tmp = Arrays.copyOf(tmp, len + 8);
    for (int i = 0;i < 8;i++) {
      tmp[len + i] = (byte)(lastModified & 0xFF);
      lastModified >>= 8;
    }
    long hash = Tools.md5(tmp);
    return Long.toHexString(hash);
  }

  public static String read(URL url) throws IOException {
    return read(url.openStream());
  }

  public static String read(File f) throws IOException {
    return read(new FileInputStream(f));
  }

  public static String read(InputStream in) throws IOException {
    return read(in, Tools.UTF_8);
  }

  public static String read(InputStream in, Charset charset) throws IOException {
    try {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      copy(in, baos);
      return new String(baos.toByteArray(), charset);
    }
    finally {
      safeClose(in);
    }
  }

  public static String read(Reader in) throws IOException {
    StringWriter buffer = new StringWriter();
    copy(in, buffer);
    return buffer.toString();
  }

  public static  O copy(InputStream in, O out) throws IOException {
    byte[] buffer = new byte[256];
    for (int l;(l = in.read(buffer)) != -1;) {
      out.write(buffer, 0, l);
    }
    return out;
  }

  public static  O copy(Reader in, O out) throws IOException {
    char[] buffer = new char[256];
    for (int l;(l = in.read(buffer)) != -1;) {
      out.write(buffer, 0, l);
    }
    return out;
  }

  public static String unquote(String s) throws NullPointerException {
    if (s == null) {
      throw new NullPointerException("Can't unquote null string");
    }
    if (s.length() > 1) {
      char c1 = s.charAt(0);
      char c2 = s.charAt(s.length() - 1);
      if ((c1 == '\'' || c1 == '"') && c1 == c2) {
        return s.substring(1, s.length() - 1);
      }
    }
    return s;
  }

  public static String join(char separator, String... names) {
    return join(separator, names, 0, names.length);
  }

  public static String join(char separator, String[] names, int from, int end) {
    int length = 0;
    for (int i = from;i < end;i++) {
      if (i > from) {
        length++;
      }
      length += names[i].length();
    }
    return join(new StringBuilder(length), separator, names, from, end).toString();
  }

  public static String join(char separator, Iterator names) {
    return join(new StringBuilder(), separator, names).toString();
  }

  public static String join(char separator, Iterable names) {
    return join(separator, names.iterator());
  }

  public static StringBuilder join(StringBuilder sb, char separator, String... names) {
    return join(sb, separator, names, 0, names.length);
  }

  public static StringBuilder join(StringBuilder sb, char separator, String[] names, int from, int end) {
    try {
      join((Appendable)sb, separator, names, from, end);
      return sb;
    }
    catch (IOException e) {
      throw new UndeclaredIOException(e);
    }
  }

  public static StringBuilder join(StringBuilder sb, char separator, Iterator names) {
    try {
      join((Appendable)sb, separator, names);
      return sb;
    }
    catch (IOException e) {
      throw new UndeclaredIOException(e);
    }
  }

  public static StringBuilder join(StringBuilder sb, char separator, Iterable names) {
    try {
      join((Appendable)sb, separator, names);
      return sb;
    }
    catch (IOException e) {
      throw new UndeclaredIOException(e);
    }
  }

  public static  Appendable join(A appendable, char separator, String... names) throws IOException {
    return join(appendable, separator, names, 0, names.length);
  }

  public static  Appendable join(A appendable, char separator, String[] names, int from, int end) throws IOException {
    int length = end - from;
    switch (length) {
      case 0:
        break;
      case 1:
        appendable.append(names[from]);
        break;
      default:
        for (int i = from;i < end;i++) {
          if (i > from) {
            appendable.append(separator);
          }
          appendable.append(names[i]);
        }
        break;
    }
    return appendable;
  }

  public static  Appendable join(A appendable, char separator, Iterable names) throws IOException {
    return join(appendable, separator, names.iterator());
  }

  public static  Appendable join(A appendable, char separator, Iterator names) throws IOException {
    if (names.hasNext()) {
      appendable.append(names.next());
      while (names.hasNext()) {
        appendable.append(separator);
        appendable.append(names.next());
      }
    }
    return appendable;
  }

  public static  A nameOf(Class clazz, A appendable) throws IOException {
    if (clazz.isMemberClass()) {
      nameOf(clazz.getEnclosingClass(), appendable).append('.').append(clazz.getSimpleName());
    } else {
      appendable.append(clazz.getSimpleName());
    }
    return appendable;
  }

  public static String getName(Class clazz) {
    if (clazz.isLocalClass() || clazz.isAnonymousClass()) {
      throw new IllegalArgumentException("Cannot use local or anonymous class");
    }
    else {
      try {
        return nameOf(clazz, new StringBuilder()).toString();
      }
      catch (IOException e) {
        throw new AssertionError(e);
      }
    }
  }

  public static String getImport(Class clazz) {
    if (clazz.isLocalClass() || clazz.isAnonymousClass()) {
      throw new IllegalArgumentException("Cannot use local or anonymous class");
    }
    else if (clazz.isMemberClass()) {
      StringBuilder sb = new StringBuilder();
      while (clazz.isMemberClass()) {
        sb.insert(0, clazz.getSimpleName());
        sb.insert(0, '.');
        clazz = clazz.getEnclosingClass();
      }
      sb.insert(0, clazz.getSimpleName());
      String pkg = clazz.getPackage().getName();
      if (pkg.length() > 0) {
        sb.insert(0, '.');
        sb.insert(0, pkg);
      }
      return sb.toString();
    }
    else {
      return clazz.getName();
    }
  }

  /**
   * 

Add the specified to the specified set and returns the result. When the set argument is an * instance of {@link HashSet} the element is directly added, otherwise a new HashSet is created by * cloning the set argument and the e argument is added.

*

*

Usage pattern : adding a set to a non modifiable set

*

   *    Set<String> set = Collections.emptySet();
   *    set = addToHashSet(set, "foo");
   * 
* * @param set the set * @param e the element * @param the set generic type * @return an HashSet containing the element */ public static HashSet addToHashSet(Set set, E e) { HashSet hashSet; if (set instanceof HashSet) { hashSet = (HashSet)set; } else { hashSet = new HashSet(set); } hashSet.add(e); return hashSet; } /** *

Add the specified to the specified list and returns the result. When the list argument is an * instance of {@link ArrayList} the element is directly added, otherwise a new ArrayList is created by * cloning the list argument and the e argument is added.

*

*

Usage pattern : adding a list to a non modifiable list

*

   *    List<String> list = Collections.emptyList();
   *    list = addToArrayList(list, "foo");
   * 
* * @param list the list * @param e the element * @param the set generic type * @return an ArrayList containing the element */ public static ArrayList addToArrayList(List list, E e) { ArrayList arrayList; if (list instanceof ArrayList) { arrayList = (ArrayList)list; } else { arrayList = new ArrayList(list); } arrayList.add(e); return arrayList; } public static HashSet set() { return new HashSet(); } public static HashSet set(E element) { HashSet set = new HashSet(); set.add(element); return set; } public static HashSet set(E... elements) { HashSet set = new HashSet(elements.length); Collections.addAll(set, elements); return set; } public static HashSet set(Iterable elements) { return set(elements.iterator()); } public static HashSet set(Iterator elements) { HashSet list = new HashSet(); while (elements.hasNext()) { list.add(elements.next()); } return list; } public static HashSet set(Enumeration elements) { HashSet list = new HashSet(); while (elements.hasMoreElements()) { list.add(elements.nextElement()); } return list; } public static ArrayList list(Iterable elements) { return list(elements.iterator()); } public static ArrayList list(Iterator elements) { ArrayList list = new ArrayList(); while (elements.hasNext()) { list.add(elements.next()); } return list; } public static ArrayList list(Enumeration elements) { ArrayList list = new ArrayList(); while (elements.hasMoreElements()) { list.add(elements.nextElement()); } return list; } public static ArrayList list(E... elements) { ArrayList set = new ArrayList(elements.length); Collections.addAll(set, elements); return set; } public static Iterable iterable(final Enumeration elements) throws NullPointerException { return new Iterable() { public Iterator iterator() { return Tools.iterator(elements); } }; } public static Iterator iterator(final Enumeration elements) throws NullPointerException { return new Iterator() { public boolean hasNext() { return elements.hasMoreElements(); } public E next() { return elements.nextElement(); } public void remove() { throw new UnsupportedOperationException("Read only"); } }; } public static Iterable iterable(final E element) throws NullPointerException { return new Iterable() { public Iterator iterator() { return Tools.iterator(element); } }; } public static Iterator iterator(final E element) throws NullPointerException { return new Iterator() { boolean hasNext = true; public boolean hasNext() { return hasNext; } public E next() { if (hasNext) { hasNext = false; return element; } else { throw new NoSuchElementException(); } } public void remove() { throw new UnsupportedOperationException(); } }; } public static class ArrayIterator implements Iterator { /** . */ private final E[] elements; /** . */ private final int to; /** . */ private int current; ArrayIterator(E[] elements, int to, int current) throws NullPointerException, IndexOutOfBoundsException, IllegalArgumentException { if (elements == null) { throw new NullPointerException("No null elements accepted"); } if (current < 0) { throw new IndexOutOfBoundsException("From index cannot be negative"); } if (to > elements.length + 1) { throw new IndexOutOfBoundsException("To index cannot be greater than the array size + 1"); } if (current > to) { throw new IllegalArgumentException("From index cannot be greater than the to index"); } // this.elements = elements; this.current = current; this.to = to; } public boolean hasNext() { return current < to; } public E next() { if (!hasNext()) { throw new NoSuchElementException(); } return elements[current++]; } public void remove() { throw new NoSuchElementException(); } } public static class IterableArray implements Iterable { /** . */ private final E[] elements; /** . */ private final int from; /** . */ private final int to; IterableArray(E[] elements, int from, int to) throws NullPointerException, IndexOutOfBoundsException, IllegalArgumentException { if (elements == null) { throw new NullPointerException("No null elements accepted"); } if (from < 0) { throw new IndexOutOfBoundsException("From index cannot be negative"); } if (to > elements.length + 1) { throw new IndexOutOfBoundsException("To index cannot be greater than the array size + 1"); } if (from > to) { throw new IllegalArgumentException("From index cannot be greater than the to index"); } // this.elements = elements; this.from = from; this.to = to; } public Iterator iterator() { return new ArrayIterator(elements, to, from); } } public static Iterable iterable(final E... elements) throws NullPointerException { return new IterableArray(elements, 0, elements.length); } public static Iterator iterator(E... elements) throws NullPointerException { return new ArrayIterator(elements, elements.length, 0); } /** * Create an iterable from the array and bounds * * @param elements the elements to wrap * @param from the from bound * @param to the to bound * @param the element generic type * @return the iterable * @throws NullPointerException if the array is null * @throws IndexOutOfBoundsException when the bounds are outside of the array * @throws IllegalArgumentException if the from argument is greater than the to index */ public static IterableArray iterable(E[] elements, int from, int to) throws NullPointerException, IndexOutOfBoundsException, IllegalArgumentException { return new IterableArray(elements, from, to); } public static Iterator iterator(int from, final E... elements) throws NullPointerException, IndexOutOfBoundsException { return new ArrayIterator(elements, elements.length, from); } public static Iterable iterable(final int from, final int to, final E... elements) throws NullPointerException, IndexOutOfBoundsException { return new IterableArray(elements, from, to); } public static Iterator iterator(final int from, final int to, final E... elements) throws NullPointerException, IndexOutOfBoundsException { return new ArrayIterator(elements, to, from); } public static Iterator emptyIterator() { @SuppressWarnings("unchecked") Iterator iterator = EMPTY_ITERATOR; return iterator; } public static Iterable emptyIterable() { @SuppressWarnings("unchecked") Iterable iterable = EMPTY_ITERABLE; return iterable; } public static Iterator append(final Iterator iterator, final E... elements) { return new Iterator() { /** -1 means the iterator should be used, otherwise it's the index. */ int index = -1; public boolean hasNext() { if (index == -1) { if (iterator.hasNext()) { return true; } else { index = 0; } } return index < elements.length; } public E next() { if (index == -1) { if (iterator.hasNext()) { return iterator.next(); } else { index = 0; } } if (index < elements.length) { return elements[index++]; } else { throw new NoSuchElementException(); } } public void remove() { throw new UnsupportedOperationException(); } }; } /** * Append an object to an array of objects. The original array is not modified. The returned array will be of the * same component type of the provided array and its first n elements where n is the size of the provided array will * be the elements of the provided array. The last element of the array will be the provided object to append. * * @param array the array to augment * @param o the object to append * @return a new array * @throws IllegalArgumentException if the array is null * @throws ClassCastException if the appended object class prevents it from being added to the array */ public static E[] appendTo(E[] array, E o) throws IllegalArgumentException, ClassCastException { if (array == null) { throw new IllegalArgumentException(); } // Class componentType = array.getClass().getComponentType(); if (o != null && !componentType.isAssignableFrom(o.getClass())) { throw new ClassCastException("Object with class " + o.getClass().getName() + " cannot be casted to class " + componentType.getName()); } // E[] copy = (E[])Array.newInstance(componentType, array.length + 1); System.arraycopy(array, 0, copy, 0, array.length); copy[array.length] = o; // return copy; } public static S unserialize(Class expectedType, File f) throws IOException, ClassNotFoundException { return unserialize(expectedType, new FileInputStream(f)); } public static S unserialize(Class expectedType, InputStream in) throws IOException, ClassNotFoundException { return unserialize(null, expectedType, in); } public static S unserialize(final ClassLoader loader, Class expectedType, InputStream in) throws IOException, ClassNotFoundException { try { ObjectInputStream ois = new ObjectInputStream(in) { @Override protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { if (loader != null) { return Class.forName(desc.getName(), true, loader); } else { return super.resolveClass(desc); } } }; Object o = ois.readObject(); return expectedType.cast(o); } finally { safeClose(in); } } public static void serialize(S value, File f) throws IOException { serialize(value, new FileOutputStream(f)); } public static void serialize(T value, OutputStream out) throws IOException { ObjectOutputStream ois = new ObjectOutputStream(out); try { ois.writeObject(value); } finally { safeClose(out); } } public static T clone(T value) throws IOException, ClassNotFoundException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); Tools.serialize(value, baos); byte[] bytes = baos.toByteArray(); return (T)unserialize(Object.class, new ByteArrayInputStream(bytes)); } public static int unsignedByteToInt(byte b) { return (int)b & 0xFF; } /** * Parses a date formatted as ISO 8601. * * @param date the date * @return the time in millis corresponding to the date */ public static long parseISO8601(String date) { return DatatypeConverter.parseDateTime(date).getTimeInMillis(); } /** * Format the time millis as an ISO 8601 date. * * @param timeMillis the time to format * @return the ISO 8601 corresponding dat */ public static String formatISO8601(long timeMillis) { Calendar c = Calendar.getInstance(); c.setTimeInMillis(timeMillis); return DatatypeConverter.printDateTime(c); } public static long handle(Element te) { long hash = 0; for (Element enclosed : te.getEnclosedElements()) { hash = 31 * hash + handle(enclosed); } hash = 31 * hash + te.getSimpleName().toString().hashCode(); return hash; } public static int indexOf(CharSequence s, char c, int from) { return indexOf(s, c, from, s.length()); } public static int indexOf(CharSequence s, char c, int from, int end) { if (from < end) { if (from < 0) { from = 0; } while (from < end) { if (s.charAt(from) == c) { return from; } from++; } } return -1; } public static int lastIndexOf(CharSequence s, char c) { return lastIndexOf(s, c, s.length() - 1); } public static int lastIndexOf(CharSequence s, char c, int from) { from = Math.min(from, s.length() - 1); while (from >= 0) { if (s.charAt(from) == c) { return from; } else { from--; } } return -1; } /** * Count the occurence of the separator string in the specified string with no overlapping. * * @param s the string to count in * @param separator the separator * @return the number of occurence */ public static int count(String s, String separator) { if (separator.length() == 0) { return s.length() + 1; } else { int count = 0; int prev = 0; while (true) { int pos = s.indexOf(separator, prev); if (pos == -1) { break; } else { count++; prev = pos + separator.length(); } } return count; } } public static String[] split(CharSequence s, char separator) { return foo(s, separator, 0, 0, 0); } public static String[] split(CharSequence s, char separator, int rightPadding) { if (rightPadding < 0) { throw new IllegalArgumentException("Right padding cannot be negative"); } return foo(s, separator, 0, 0, rightPadding); } private static String[] foo(CharSequence s, char separator, int count, int from, int rightPadding) { int len = s.length(); if (from < len) { int to = from; while (to < len && s.charAt(to) != separator) { to++; } String[] ret; if (to == len - 1) { ret = new String[count + 2 + rightPadding]; ret[count + 1] = ""; } else { ret = to == len ? new String[count + 1 + rightPadding] : foo(s, separator, count + 1, to + 1, rightPadding); } ret[count] = from == to ? "" : s.subSequence(from, to).toString(); return ret; } else if (from == len) { return new String[count + rightPadding]; } else { throw new AssertionError(); } } public static AnnotationMirror getAnnotation(Element element, String annotationFQN) { for (AnnotationMirror mirror : element.getAnnotationMirrors()) { if (mirror.getAnnotationType().toString().equals(annotationFQN)) { return mirror; } } return null; } public static StringBuilder toString(Iterable> entries, StringBuilder sb) { sb.append('{'); for (Iterator> i = entries.iterator();i.hasNext();) { Map.Entry entry = i.next(); sb.append(entry.getKey()).append("=["); String[] value = entry.getValue(); for (int j = 0;j < value.length;j++) { if (j > 0) { sb.append(','); } sb.append(value[j]); } sb.append(']'); if (i.hasNext()) { sb.append(','); } } sb.append('}'); return sb; } public static String nextUUID() { return UUID.randomUUID().toString(); } public static HashMap toHashMap(Parameters parameters) { HashMap map = new HashMap(); for (ResponseParameter parameter : parameters.values()) { map.put(parameter.getName(), parameter.toArray()); } return map; } public static BigInteger bitSet(CharSequence s) { BigInteger current = BigInteger.ZERO; for (int i = s.length() - 1;i >= 0;i--) { char c = s.charAt(i); current = current.setBit(c); } return current; } public static BigInteger bitSet(char... chars) { BigInteger current = BigInteger.ZERO; for (char c : chars) { current = current.setBit(c); } return current; } public static String[] safeConcat(String[] first, String[] second) { if (first != null) { if (second != null) { String[] concat = new String[first.length + second.length]; System.arraycopy(first, 0, concat, 0, first.length); System.arraycopy(second, 0, concat, first.length, second.length); return concat; } else { return first; } } else { if (second != null) { return second; } else { return EMPTY_STRING_ARRAY; } } } /** * Init the cause of the throwable argument with the cause argument. * * @param throwable the throwable to init * @param cause the cause to init with * @param the throwable generic cause * @return the throwable argument */ public static T initCause(T throwable, Throwable cause) { throwable.initCause(cause); return throwable; } /** * Returns the cause of the argument or the argument itself when its cause is null. So this method * should never return null. * * @param throwable the throwable * @return the cause * @throws NullPointerException if the throwable argument is null */ public static Throwable safeCause(Throwable throwable) throws NullPointerException { Throwable cause = throwable.getCause(); return cause != null ? cause : throwable; } /** Char to entity, we use 256x256 instead of 65536 for compressing space. */ private static final String[][] HTML_ESCAPES = new String[256][]; private static void addNamedHtmlEntity(int c, String entity) { addHtmlEscape(c, "&" + entity + ";"); } private static void addHtmlEscape(int c, String entity) { int i1 = c >> 8; String[] table = HTML_ESCAPES[i1]; if (table == null) { HTML_ESCAPES[i1] = table = new String[256]; } table[c & 0xFF] = entity; } /** * Lookup an html escape for the specified char. * * @param c the char to lookup * @return the corresponding html escape or null if it does not exist */ public static String getHtmlEscape(char c) { int lookup = (c >> 8); if (lookup < 256) { String[] table = HTML_ESCAPES[lookup]; int pointer = c & 0xFF; return table[pointer]; } return null; } static { // Common html entities addNamedHtmlEntity(160, "nbsp"); addNamedHtmlEntity(161, "iexcl"); addNamedHtmlEntity(162, "cent"); addNamedHtmlEntity(163, "pound"); addNamedHtmlEntity(164, "curren"); addNamedHtmlEntity(165, "yen"); addNamedHtmlEntity(166, "brvbar"); addNamedHtmlEntity(167, "sect"); addNamedHtmlEntity(168, "uml"); addNamedHtmlEntity(169, "copy"); addNamedHtmlEntity(170, "ordf"); addNamedHtmlEntity(171, "laquo"); addNamedHtmlEntity(172, "not"); addNamedHtmlEntity(173, "shy"); addNamedHtmlEntity(174, "reg"); addNamedHtmlEntity(175, "macr"); addNamedHtmlEntity(176, "deg"); addNamedHtmlEntity(177, "plusmn"); addNamedHtmlEntity(178, "sup2"); addNamedHtmlEntity(179, "sup3"); addNamedHtmlEntity(180, "acute"); addNamedHtmlEntity(181, "micro"); addNamedHtmlEntity(182, "para"); addNamedHtmlEntity(183, "middot"); addNamedHtmlEntity(184, "cedil"); addNamedHtmlEntity(185, "sup1"); addNamedHtmlEntity(186, "ordm"); addNamedHtmlEntity(187, "raquo"); addNamedHtmlEntity(188, "frac14"); addNamedHtmlEntity(189, "frac12"); addNamedHtmlEntity(190, "frac34"); addNamedHtmlEntity(191, "iquest"); addNamedHtmlEntity(192, "Agrave"); addNamedHtmlEntity(193, "Aacute"); addNamedHtmlEntity(194, "Acirc"); addNamedHtmlEntity(195, "Atilde"); addNamedHtmlEntity(196, "Auml"); addNamedHtmlEntity(197, "Aring"); addNamedHtmlEntity(198, "AElig"); addNamedHtmlEntity(199, "Ccedil"); addNamedHtmlEntity(200, "Egrave"); addNamedHtmlEntity(201, "Eacute"); addNamedHtmlEntity(202, "Ecirc"); addNamedHtmlEntity(203, "Euml"); addNamedHtmlEntity(204, "Igrave"); addNamedHtmlEntity(205, "Iacute"); addNamedHtmlEntity(206, "Icirc"); addNamedHtmlEntity(207, "Iuml"); addNamedHtmlEntity(208, "ETH"); addNamedHtmlEntity(209, "Ntilde"); addNamedHtmlEntity(210, "Ograve"); addNamedHtmlEntity(211, "Oacute"); addNamedHtmlEntity(212, "Ocirc"); addNamedHtmlEntity(213, "Otilde"); addNamedHtmlEntity(214, "Ouml"); addNamedHtmlEntity(215, "times"); addNamedHtmlEntity(216, "Oslash"); addNamedHtmlEntity(217, "Ugrave"); addNamedHtmlEntity(218, "Uacute"); addNamedHtmlEntity(219, "Ucirc"); addNamedHtmlEntity(220, "Uuml"); addNamedHtmlEntity(221, "Yacute"); addNamedHtmlEntity(222, "THORN"); addNamedHtmlEntity(223, "szlig"); addNamedHtmlEntity(224, "agrave"); addNamedHtmlEntity(225, "aacute"); addNamedHtmlEntity(226, "acirc"); addNamedHtmlEntity(227, "atilde"); addNamedHtmlEntity(228, "auml"); addNamedHtmlEntity(229, "aring"); addNamedHtmlEntity(230, "aelig"); addNamedHtmlEntity(231, "ccedil"); addNamedHtmlEntity(232, "egrave"); addNamedHtmlEntity(233, "eacute"); addNamedHtmlEntity(234, "ecirc"); addNamedHtmlEntity(235, "euml"); addNamedHtmlEntity(236, "igrave"); addNamedHtmlEntity(237, "iacute"); addNamedHtmlEntity(238, "icirc"); addNamedHtmlEntity(239, "iuml"); addNamedHtmlEntity(240, "eth"); addNamedHtmlEntity(241, "ntilde"); addNamedHtmlEntity(242, "ograve"); addNamedHtmlEntity(243, "oacute"); addNamedHtmlEntity(244, "ocirc"); addNamedHtmlEntity(245, "otilde"); addNamedHtmlEntity(246, "ouml"); addNamedHtmlEntity(247, "divide"); addNamedHtmlEntity(248, "oslash"); addNamedHtmlEntity(249, "ugrave"); addNamedHtmlEntity(250, "uacute"); addNamedHtmlEntity(251, "ucirc"); addNamedHtmlEntity(252, "uuml"); addNamedHtmlEntity(253, "yacute"); addNamedHtmlEntity(254, "thorn"); addNamedHtmlEntity(255, "yuml"); addNamedHtmlEntity(402, "fnof"); addNamedHtmlEntity(913, "Alpha"); addNamedHtmlEntity(914, "Beta"); addNamedHtmlEntity(915, "Gamma"); addNamedHtmlEntity(916, "Delta"); addNamedHtmlEntity(917, "Epsilon"); addNamedHtmlEntity(918, "Zeta"); addNamedHtmlEntity(919, "Eta"); addNamedHtmlEntity(920, "Theta"); addNamedHtmlEntity(921, "Iota"); addNamedHtmlEntity(922, "Kappa"); addNamedHtmlEntity(923, "Lambda"); addNamedHtmlEntity(924, "Mu"); addNamedHtmlEntity(925, "Nu"); addNamedHtmlEntity(926, "Xi"); addNamedHtmlEntity(927, "Omicron"); addNamedHtmlEntity(928, "Pi"); addNamedHtmlEntity(929, "Rho"); addNamedHtmlEntity(931, "Sigma"); addNamedHtmlEntity(932, "Tau"); addNamedHtmlEntity(933, "Upsilon"); addNamedHtmlEntity(934, "Phi"); addNamedHtmlEntity(935, "Chi"); addNamedHtmlEntity(936, "Psi"); addNamedHtmlEntity(937, "Omega"); addNamedHtmlEntity(945, "alpha"); addNamedHtmlEntity(946, "beta"); addNamedHtmlEntity(947, "gamma"); addNamedHtmlEntity(948, "delta"); addNamedHtmlEntity(949, "epsilon"); addNamedHtmlEntity(950, "zeta"); addNamedHtmlEntity(951, "eta"); addNamedHtmlEntity(952, "theta"); addNamedHtmlEntity(953, "iota"); addNamedHtmlEntity(954, "kappa"); addNamedHtmlEntity(955, "lambda"); addNamedHtmlEntity(956, "mu"); addNamedHtmlEntity(957, "nu"); addNamedHtmlEntity(958, "xi"); addNamedHtmlEntity(959, "omicron"); addNamedHtmlEntity(960, "pi"); addNamedHtmlEntity(961, "rho"); addNamedHtmlEntity(962, "sigmaf"); addNamedHtmlEntity(963, "sigma"); addNamedHtmlEntity(964, "tau"); addNamedHtmlEntity(965, "upsilon"); addNamedHtmlEntity(966, "phi"); addNamedHtmlEntity(967, "chi"); addNamedHtmlEntity(968, "psi"); addNamedHtmlEntity(969, "omega"); addNamedHtmlEntity(977, "thetasym"); addNamedHtmlEntity(978, "upsih"); addNamedHtmlEntity(982, "piv"); addNamedHtmlEntity(8226, "bull"); addNamedHtmlEntity(8230, "hellip"); addNamedHtmlEntity(8242, "prime"); addNamedHtmlEntity(8243, "Prime"); addNamedHtmlEntity(8254, "oline"); addNamedHtmlEntity(8260, "frasl"); addNamedHtmlEntity(8472, "weierp"); addNamedHtmlEntity(8465, "image"); addNamedHtmlEntity(8476, "real"); addNamedHtmlEntity(8482, "trade"); addNamedHtmlEntity(8501, "alefsym"); addNamedHtmlEntity(8592, "larr"); addNamedHtmlEntity(8593, "uarr"); addNamedHtmlEntity(8594, "rarr"); addNamedHtmlEntity(8595, "darr"); addNamedHtmlEntity(8596, "harr"); addNamedHtmlEntity(8629, "crarr"); addNamedHtmlEntity(8656, "lArr"); addNamedHtmlEntity(8657, "uArr"); addNamedHtmlEntity(8658, "rArr"); addNamedHtmlEntity(8659, "dArr"); addNamedHtmlEntity(8660, "hArr"); addNamedHtmlEntity(8704, "forall"); addNamedHtmlEntity(8706, "part"); addNamedHtmlEntity(8707, "exist"); addNamedHtmlEntity(8709, "empty"); addNamedHtmlEntity(8711, "nabla"); addNamedHtmlEntity(8712, "isin"); addNamedHtmlEntity(8713, "notin"); addNamedHtmlEntity(8715, "ni"); addNamedHtmlEntity(8719, "prod"); addNamedHtmlEntity(8721, "sum"); addNamedHtmlEntity(8722, "minus"); addNamedHtmlEntity(8727, "lowast"); addNamedHtmlEntity(8730, "radic"); addNamedHtmlEntity(8733, "prop"); addNamedHtmlEntity(8734, "infin"); addNamedHtmlEntity(8736, "ang"); addNamedHtmlEntity(8743, "and"); addNamedHtmlEntity(8744, "or"); addNamedHtmlEntity(8745, "cap"); addNamedHtmlEntity(8746, "cup"); addNamedHtmlEntity(8747, "int"); addNamedHtmlEntity(8756, "there4"); addNamedHtmlEntity(8764, "sim"); addNamedHtmlEntity(8773, "cong"); addNamedHtmlEntity(8776, "asymp"); addNamedHtmlEntity(8800, "ne"); addNamedHtmlEntity(8801, "equiv"); addNamedHtmlEntity(8804, "le"); addNamedHtmlEntity(8805, "ge"); addNamedHtmlEntity(8834, "sub"); addNamedHtmlEntity(8835, "sup"); addNamedHtmlEntity(8836, "nsub"); addNamedHtmlEntity(8838, "sube"); addNamedHtmlEntity(8839, "supe"); addNamedHtmlEntity(8853, "oplus"); addNamedHtmlEntity(8855, "otimes"); addNamedHtmlEntity(8869, "perp"); addNamedHtmlEntity(8901, "sdot"); addNamedHtmlEntity(8968, "lceil"); addNamedHtmlEntity(8969, "rceil"); addNamedHtmlEntity(8970, "lfloor"); addNamedHtmlEntity(8971, "rfloor"); addNamedHtmlEntity(9001, "lang"); addNamedHtmlEntity(9002, "rang"); addNamedHtmlEntity(9674, "loz"); addNamedHtmlEntity(9824, "spades"); addNamedHtmlEntity(9827, "clubs"); addNamedHtmlEntity(9829, "hearts"); addNamedHtmlEntity(9830, "diams"); addNamedHtmlEntity(34, "quot"); addNamedHtmlEntity(38, "amp"); addNamedHtmlEntity(60, "lt"); addNamedHtmlEntity(62, "gt"); addNamedHtmlEntity(338, "OElig"); addNamedHtmlEntity(339, "oelig"); addNamedHtmlEntity(352, "Scaron"); addNamedHtmlEntity(353, "scaron"); addNamedHtmlEntity(376, "Yuml"); addNamedHtmlEntity(710, "circ"); addNamedHtmlEntity(732, "tilde"); addNamedHtmlEntity(8194, "ensp"); addNamedHtmlEntity(8195, "emsp"); addNamedHtmlEntity(8201, "thinsp"); addNamedHtmlEntity(8204, "zwnj"); addNamedHtmlEntity(8205, "zwj"); addNamedHtmlEntity(8206, "lrm"); addNamedHtmlEntity(8207, "rlm"); addNamedHtmlEntity(8211, "ndash"); addNamedHtmlEntity(8212, "mdash"); addNamedHtmlEntity(8216, "lsquo"); addNamedHtmlEntity(8217, "rsquo"); addNamedHtmlEntity(8218, "sbquo"); addNamedHtmlEntity(8220, "ldquo"); addNamedHtmlEntity(8221, "rdquo"); addNamedHtmlEntity(8222, "bdquo"); addNamedHtmlEntity(8224, "dagger"); addNamedHtmlEntity(8225, "Dagger"); addNamedHtmlEntity(8240, "permil"); addNamedHtmlEntity(8249, "lsaquo"); addNamedHtmlEntity(8250, "rsaquo"); addNamedHtmlEntity(8364, "euro"); } public static void encodeHtmlText(CharSequence src, int from, int end, Appendable dst) throws IOException, IllegalArgumentException { encodeHtml(src, from, end, dst); } public static void encodeHtmlAttribute(CharSequence src, int from, int end, Appendable dst) throws IOException, IllegalArgumentException { encodeHtml(src, from, end, dst); } private static void encodeHtml(CharSequence src, int from, int to, Appendable dst) throws IOException, IllegalArgumentException { if (from < 0) { throw new IllegalArgumentException("From bound cannot be negative"); } if (to > src.length()) { throw new IllegalArgumentException("To bound cannot be greater than source length"); } if (from > to) { throw new IllegalArgumentException("From bound cannot be greater than to bound"); } while (from < to) { char c = src.charAt(from++); String escape = getHtmlEscape(c); if (escape != null) { dst.append(escape); } else { dst.append(c); } } } /** . */ private static final char[] HEX = "0123456789abcdef".toCharArray(); /** . */ private static final int[] MASKS_AND_SHIFTS = { 0xF0000000, 7 * 4, 0x0F000000, 6 * 4, 0x00F00000, 5 * 4, 0x000F0000, 4 * 4, 0x0000F000, 3 * 4, 0x00000F00, 2 * 4, 0x000000F0, 1 * 4, 0x0000000F, 0 * 4, }; public static
A toHex(int value, A appendable) throws IOException { boolean foo = false; for (int i = 0;i < MASKS_AND_SHIFTS.length;i += 2) { int tmp = value & MASKS_AND_SHIFTS[i]; if (tmp != 0) { tmp >>>= MASKS_AND_SHIFTS[i + 1]; char cc = HEX[tmp]; appendable.append(cc); foo= true; } else if (foo || i == MASKS_AND_SHIFTS.length - 2) { appendable.append("0"); } } return appendable; } public static A encodeHtml(org.w3c.dom.Element element, A dst) throws IOException { String tagName = element.getTagName(); // Determine if empty // Note that we won't accumulate the elements that would be serialized for performance reason // we will just reiterate later before ending the element boolean empty; if (tagName.equalsIgnoreCase("script")) { empty = false; } else { empty = true; NodeList children = element.getChildNodes(); int length = children.getLength(); for (int i = 0; i < length && empty; i++) { Node child = children.item(i); if (child instanceof CharacterData) { empty = false; } else if (child instanceof org.w3c.dom.Element) { empty = false; } } } // dst.append('<'); dst.append(tagName); // Write attributes if (element.hasAttributes()) { NamedNodeMap attrs = element.getAttributes(); int length = attrs.getLength(); for (int i = 0; i < length; i++) { Attr attr = (Attr) attrs.item(i); dst.append(' '); dst.append(attr.getName()); dst.append("=\""); encodeHtmlAttribute(attr.getValue(), 0, attr.getValue().length(), dst); dst.append("\""); } } // if (!empty) { // dst.append(">"); // Serialize children that are worth to be NodeList children = element.getChildNodes(); int length = children.getLength(); for (int i = 0; i < length; i++) { Node child = children.item(i); if (child instanceof CDATASection) { // writer.writeCData(((CDATASection) child).getData()); throw new UnsupportedOperationException("Encoding CDATA not yet supported"); } else if (child instanceof CharacterData) { String data = ((CharacterData)child).getData(); encodeHtmlText(data, 0, data.length(), dst); } else if (child instanceof org.w3c.dom.Element) { encodeHtml((org.w3c.dom.Element)child, dst); } } // dst.append("'); } else { dst.append("/>"); } // return dst; } public static Iterable children(final Node node) { return new Iterable() { public Iterator iterator() { return new Iterator() { int i = 0; NodeList children = node.getChildNodes(); public boolean hasNext() { return i < children.getLength(); } public Node next() { if (hasNext()) { return children.item(i++); } else { throw new NoSuchElementException(); } } public void remove() { throw new UnsupportedOperationException(); } }; } }; } public static long md5(byte[] bytes) { try { MessageDigest digest = MessageDigest.getInstance("MD5"); byte[] md5 = digest.digest(bytes); long value = 0; for (byte b : md5) { value = value * 256 + Tools.unsignedByteToInt(b); } return value; } catch (NoSuchAlgorithmException e) { throw new UnsupportedOperationException("Could not resolve MD5", e); } } /** . */ private static final int ESCAPE = -1; /** . */ private static final int SCANNING = 0; /** . */ private static final int DOLLAR = 1; /** . */ private static final int EVALUATING = 2; /** * Interpolate a string and replace the occurence from a context map. * * @param interpolated the value to interpolate * @param context the context * @return the interpolated string * @throws NullPointerException if the interpolated argument is null */ public static String interpolate(String interpolated, Map context) throws NullPointerException { StringBuilder sb = new StringBuilder(); int status = 0; int prev = 0; int length = interpolated.length(); for (int i = 0;i < length;i++) { char c = interpolated.charAt(i); switch (status) { case ESCAPE: if (c == '$') { sb.append('$'); } else { sb.append('\\').append(c); } status = SCANNING; break; case SCANNING: if (c == '$') { status = DOLLAR; } else if (c == '\\') { status = ESCAPE; } else { sb.append(c); } break; case DOLLAR: if (c == '{') { status = EVALUATING; prev = i + 1; } else { sb.append('$').append(c); } break; case EVALUATING: if (c == '}') { int j = i; while (j > prev) { if (interpolated.charAt(j) == ':') { break; } else { j--; } } Object value; if (j > prev) { String key = interpolated.substring(prev, j); value = context.get(key); if (value == null) { value = interpolated.substring(j + 1, i); } } else { String key = interpolated.substring(prev, i); value = context.get(key); } if (value != null) { sb.append(value); } status = SCANNING; } break; } } switch (status) { case DOLLAR: sb.append('$'); break; case EVALUATING: sb.append("${").append(interpolated, prev, interpolated.length()); break; } return sb.toString(); } }