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

org.qas.api.http.FastUrlEncoder Maven / Gradle / Ivy

The newest version!
package org.qas.api.http;

import org.qas.api.internal.util.google.base.Throwables;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.BitSet;

/**
 * FastUrlEncoder
 *
 * @author Dzung Nguyen
 * @version $Id FastUrlEncoder 2014-03-27 09:34:30z dungvnguyen $
 * @since 1.0
 */
public final class FastUrlEncoder {
  //~ class properties ========================================================
  /** HEX_DIGITS. */
  private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

  /**
   * These octets all go directly into the URL, all others are escaped.
   */
  private static final BitSet SAFE_OCTETS = new BitSet(256);
  static {
    for (int index = '0'; index <= '9'; index++) SAFE_OCTETS.set(index);
    for (int index = 'A'; index <= 'Z'; index++) SAFE_OCTETS.set(index);
    for (int index = 'a'; index <= 'z'; index++) SAFE_OCTETS.set(index);

    SAFE_OCTETS.set('-');
    SAFE_OCTETS.set('_');
    SAFE_OCTETS.set('.');
    SAFE_OCTETS.set('*');
  }

  /**
   * These octets all go directly into the URI, all others are escaped.
   */
  private static final BitSet URI_SAFE_OCTETS = new BitSet(256);
  static {
    for (int index = '0'; index <= '9'; index++) URI_SAFE_OCTETS.set(index);
    for (int index = 'A'; index <= 'Z'; index++) URI_SAFE_OCTETS.set(index);
    for (int index = 'a'; index <= 'z'; index++) URI_SAFE_OCTETS.set(index);

    URI_SAFE_OCTETS.set('-');
    URI_SAFE_OCTETS.set('_');
    URI_SAFE_OCTETS.set('.');
    URI_SAFE_OCTETS.set('~');
  }
  //~ class members ===========================================================
  private FastUrlEncoder() {
    throw new AssertionError("An FastUrlEncoder utility class must not be instantiated.");
  }

  /**
   * URL-escapes {@code source} by encoding it with the specified character encoding,
   * escaping all octets not included in safeOctets and then outputting the result
   * to an {@link Appendable}
   *
   * @param source the given String to encode.
   * @param encoding character encoding to used.
   * @param safeOctets set of octets that should not be escape.
   * @param plusForSpace whether octet 0x20, "space". should be encoded as a plus sign
   *                     rather than "%20". Note that this parameter is effectively
   *                     ignored if 0x20 is in safeOctets.
   * @param out the {@link Appendable} destination for encoded {@link String}
   * @return {@code true} if {@code source} did need escaping, false otherwise.
   *
   * @throws java.io.UnsupportedEncodingException if {@code encoding} is not supported.
   * @throws java.io.IOException if {@code out} does so when appended to.
   */
  private static boolean urlEncode(final String source, final String encoding, BitSet safeOctets,
                                   boolean plusForSpace, Appendable out)
      throws IOException {
    byte[] data = source.getBytes(encoding);
    boolean containsSpace = false;
    int outputLength = 0;

    for (int index = 0; index < data.length; index++) {
      int ch = data[index];
      if (ch < 0) ch += 256; // convert from [-128, 127] to [0, 255]

      if (safeOctets.get(ch)) {
        out.append((char) ch);
        outputLength += 1;
      } else if (plusForSpace && (ch == ' ')) {
        containsSpace = true;
        out.append('+');
        outputLength += 1;
      } else {
        out.append('%');
        out.append(HEX_DIGITS[ch >> 4]);
        out.append(HEX_DIGITS[ch & 0x0f]);
        outputLength+=3;
      }
    }

    return containsSpace || (outputLength != source.length());
  }

  /**
   * URL-escapes {@code source} by encoding it with the specified character encoding, and
   * then escape all octets not included in {@code safeOctets}
   *
   * @param source the given String to encode.
   * @param encoding character encoding to used.
   * @param safeOctets set of octets that should not be escape.
   * @param plusForSpace whether octet 0x20, "space". should be encoded as a plus sign
   *                     rather than "%20". Note that this parameter is effectively
   *                     ignored if 0x20 is in safeOctets.
   * @return the encoded version of {@code source}. Will return {@code source} itself
   *         if no encoding if necessary.
   * @throws UnsupportedEncodingException if {@code encoding} is not supported.
   */
  private static String urlEncode(final String source, final String encoding,
                                  BitSet safeOctets, boolean plusForSpace)
      throws UnsupportedEncodingException {
    StringBuilder out = new StringBuilder(source.length() * 2);
    boolean needsEncoding = false;

    try {
      needsEncoding = urlEncode(source, encoding, safeOctets, plusForSpace, out);
    } catch (IOException ioex) {
      Throwables.propagateIfInstanceOf(ioex, UnsupportedEncodingException.class);
      throw new AssertionError(ioex);
    }

    return needsEncoding ? out.toString() : source;
  }

  /**
   * This should be a direct replacement for java.net.URLEncode.encode().
   *
   * @param source String to encode.
   * @param encoding character encoding to use.
   * @return the encoded version of {@code source}.
   * @see java.net.URLEncoder#encode(String, String)
   */
  public static String urlEncode(final String source, final String encoding)
      throws UnsupportedEncodingException {
    return urlEncode(source, encoding, SAFE_OCTETS, true);
  }

  /**
   * URI-escapes {@code source} by encoding it with the specified character encoding.
   *
   * @param source String to encode.
   * @param encoding character encoding to use.
   * @return the encoded version of {@code source}.
   */
  public static String uriEncode(final String source, final String encoding)
      throws UnsupportedEncodingException {
    return urlEncode(source, encoding, URI_SAFE_OCTETS, false);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy