Package com.google.k2crypto.storage.driver

Source Code of com.google.k2crypto.storage.driver.AddressUtilities

/*
* Copyright 2014 Google. Inc.
*
* 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 com.google.k2crypto.storage.driver;

import com.google.k2crypto.storage.IllegalAddressException;

import java.net.URI;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Utility methods for checking and manipulating storage URI addresses.
*
* <p>The {@link #encodeConvenience(String)} and
* {@link #decodeUnreserved(String)} utility methods are required because
* {@link java.net.URLEncoder} and {@link java.net.URLDecoder} do not provide
* the desired functionality. Details are provided in the documentation of the
* individual methods. It boils down to {@link java.net.URLEncoder} and
* {@link java.net.URLDecoder} being designed to encode and decode ALL
* percent-encoded characters and ONLY within the query portion of a URL. They
* are NOT designed for operating on an entire URI, which is what we want to do.
*
* @author darylseah@gmail.com (Daryl Seah)
*/
public class AddressUtilities {

  // Prevent instantiation
  private AddressUtilities() {}
 
  // Regex matching percent ('%') characters in the address string that are NOT
  // of the format %[HEX][HEX] and spaces (' ').
  private static final Pattern CONVENIENCE_ENCODABLE =
      Pattern.compile("\\%(?![0-9a-fA-F][0-9a-fA-F])|\\ ");

  // Buffer space for encoding expansion
  private static final int ENCODE_ALLOWANCE = 16;
 
  // Table converting decimal to hex
  private static final char[] HEX_TABLE = {
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    'A', 'B', 'C', 'D', 'E', 'F'
  };
 
  /**
   * Performs safe URI-escaping of the '%' and ' ' characters in the string
   * for convenience sake. This is NOT a complete percent-encoding procedure.
   * The idea is to escape these characters (and only these characters) across
   * the entire URI string so that addresses containing them can remain in a
   * readable form in user code.
   * 
   * <p>The Java-included {@link java.net.URLEncoder} class has similar
   * functionality, but its encoding is designed specifically for the query
   * portion of a URL, and does not work universally across an entire URI.
   * For example, it will encode {@code "/my keys/bank"} as
   * {@code "%2Fmy+keys%2Fbank"}, whereas this method will encode it as
   * {@code "/my%20keys/bank"}. The escaped backslashes and conversion of
   * spaces to {@code '+'} will effectively change the meaning of the address,
   * and this is undesirable behavior.
   *
   * @param str String to encode.
   *
   * @return the string with stray percents and spaces percent-encoded.
   */
  public static String encodeConvenience(String str) {
    Matcher m = CONVENIENCE_ENCODABLE.matcher(str);
    boolean found = m.find();
    if (found) {
      final char[] hexTable = HEX_TABLE;
      StringBuilder sb = new StringBuilder(str.length() + ENCODE_ALLOWANCE);
      int findStart = 0;
      do {
        final int pos = m.start();
        sb.append(str, findStart, pos);
       
        // Percent-encode the encodable character
        final char c = str.charAt(pos);
        sb.append('%').append(hexTable[c >>> 4]).append(hexTable[c & 7]);
       
        findStart = m.end();
        found = m.find();
      } while (found);
     
      sb.append(str, findStart, str.length());
      return sb.toString();
    }
    return str;
  }

  // Regex matching percent-encoded URI-unreserved characters
  // (i.e. letters, digits, '-', '.', '_' and '~')
  private static final Pattern ENCODED_UNRESERVED = Pattern.compile(
      "\\%(?:4[1-9A-F]|5[0-9A]|6[1-9A-F]|7[0-9A]|3[0-9]|2D|2E|5F|7E)",
      Pattern.CASE_INSENSITIVE);
 
  /**
   * Decodes any percent-encoded URI-unreserved characters in the URI address.
   *
   * <p>As mentioned in <a href="http://tools.ietf.org/html/rfc3986#section-2.3"
   * target="_blank">RFC 3986, Section 2.3</a>, any unreserved characters in a
   * URI should be decoded before the URI can be safely compared or normalized.
   * Unfortunately, Java's URI implementation does not do this for us.
   *
   * @param address URI to decode.
   *
   * @return a new URI with all unreserved characters decoded, or the same
   *    URI if there are no unreserved characters to decode.
   *
   * @see #decodeUnreserved(String)
   */
  public static URI decodeUnreserved(URI address) {
    String addressStr = address.toString();
    String decoded = decodeUnreserved(addressStr);
    return decoded == addressStr ? address : URI.create(decoded);
  }
 
  /**
   * Decodes any percent-encoded URI-unreserved characters in the string.
   *
   * <p>The Java-included {@link java.net.URLDecoder} class has similar
   * functionality, except it decodes ALL percent-encoded characters; it is
   * designed primarily for decoding individual key/value strings in the query
   * portion of a URL after they have been extracted. For example, it will
   * decode {@code "/my+keys%3F/%62%61%6E%6B"} as {@code "/my keys?/bank"},
   * which will result in an invalid URI because {@code "/bank"} would be
   * interpreted as a query. The {@code '+'} symbol should also only be
   * interpreted as a space in the query portion, and not in any other part of
   * a URI. This method will decode the string as {@code "/my+keys%3F/bank"},
   * preserving the meaning of the address.
   *
   * @param str String to decode.
   *
   * @return the string with all unreserved characters decoded.
   */
  public static String decodeUnreserved(String str) {
    Matcher m = ENCODED_UNRESERVED.matcher(str);
    boolean found = m.find();
    if (found) {
      StringBuilder sb = new StringBuilder(str.length());
      int findStart = 0;
      do {
        final int pos = m.start();
        sb.append(str, findStart, pos);
       
        // Assume that first hex is always a digit (restricted to the regex)
        int decoded = ((str.charAt(pos + 1) - '0') << 4);
        // Complete decoding by checking second hex
        final char hex = str.charAt(pos + 2);
        decoded += hex - (hex <= '9' ? '0' : (hex <= 'Z' ? 'A' : 'a') - 10);
        sb.append((char)decoded);
       
        findStart = m.end();
        found = m.find();
      } while (found);
     
      sb.append(str, findStart, str.length());
      return sb.toString();
    }
    return str;
  }

  /**
   * Checks that the address has no authority component.
   *
   * @param address Address to check.
   * @throws IllegalAddressException if the authority component exists.
   */
  public static void checkNoAuthority(URI address)
      throws IllegalAddressException {
    if (address.getAuthority() != null) {
      throw new IllegalAddressException(
          address, IllegalAddressException.Reason.AUTHORITY_UNSUPPORTED, null);
    }
  }
 
  /**
   * Checks that the address has no user component.
   *
   * @param address Address to check.
   * @throws IllegalAddressException if the user component exists.
   */
  public static void checkNoUser(URI address)
      throws IllegalAddressException {
    String user = address.getUserInfo();
    if (user != null && user.length() > 0) {
      throw new IllegalAddressException(
          address, IllegalAddressException.Reason.USER_UNSUPPORTED, null);
    }
  }
 
  /**
   * Checks that the address has no host/port component.
   *
   * @param address Address to check.
   * @throws IllegalAddressException if the host or port component exists.
   */
  public static void checkNoHostPort(URI address)
      throws IllegalAddressException {
    if (address.getHost() != null || address.getPort() >= 0) {
      throw new IllegalAddressException(
          address, IllegalAddressException.Reason.HOST_PORT_UNSUPPORTED, null);
    }
  }
 
  /**
   * Checks that the address has no path component.
   *
   * @param address Address to check.
   * @throws IllegalAddressException if the path component exists.
   */
  public static void checkNoPath(URI address)
      throws IllegalAddressException {
    String path = address.getPath();
    if (path != null && path.length() > 0) {
      throw new IllegalAddressException(
          address, IllegalAddressException.Reason.PATH_UNSUPPORTED, null);
    }
  }

  /**
   * Checks that the address has no query component.
   *
   * @param address Address to check.
   * @throws IllegalAddressException if the query component exists.
   */
  public static void checkNoQuery(URI address)
      throws IllegalAddressException {
    String query = address.getQuery();
    if (query != null && query.length() > 0) {
      throw new IllegalAddressException(
          address, IllegalAddressException.Reason.QUERY_UNSUPPORTED, null);
    }
  }

  /**
   * Checks that the address has no fragment component.
   *
   * @param address Address to check.
   * @throws IllegalAddressException if the fragment component exists.
   */
  public static void checkNoFragment(URI address)
      throws IllegalAddressException {
    String fragment = address.getFragment();
    if (fragment != null && fragment.length() > 0) {
      throw new IllegalAddressException(
          address, IllegalAddressException.Reason.FRAGMENT_UNSUPPORTED, null);
    }
  }

  /**
   * Obtains the host from the URI address.
   *
   * @param address Address to obtain the host from.
   * @throws IllegalAddressException if the address is missing a host.
   */
  public static String extractHost(URI address)
      throws IllegalAddressException {
    String host = address.getHost();
    if (host == null || host.length() == 0) {
      throw new IllegalAddressException(
          address, IllegalAddressException.Reason.MISSING_HOST_PORT, null);
    }
    return host;
  }
 
  /**
   * Obtains the raw path from the URI address.
   *
   * @param address Address to obtain the path from.
   * @throws IllegalAddressException if the address is missing a path.
   */
  public static String extractRawPath(URI address)
      throws IllegalAddressException {
    String path = address.getRawPath();
    if (path == null || path.length() == 0) {
      throw new IllegalAddressException(
          address, IllegalAddressException.Reason.MISSING_PATH, null);
    }
    return path;
  }
 
  /**
   * Obtains the raw query from the URI address.
   *
   * @param address Address to obtain the query from.
   * @throws IllegalAddressException if the address is missing a query.
   */
  public static String extractRawQuery(URI address)
      throws IllegalAddressException {
    String query = address.getRawQuery();
    if (query == null || query.length() == 0) {
      throw new IllegalAddressException(
          address, IllegalAddressException.Reason.MISSING_QUERY, null);
    }
    return query;
  }
 
  /**
   * Obtains the fragment from the URI address.
   *
   * @param address Address to obtain the fragment from.
   * @throws IllegalAddressException if the address is missing a fragment.
   */
  public static String extractFragment(URI address)
      throws IllegalAddressException {
    String frag = address.getFragment();
    if (frag == null || frag.length() == 0) {
      throw new IllegalAddressException(
          address, IllegalAddressException.Reason.MISSING_FRAGMENT, null);
    }
    return frag;
  }
}
TOP

Related Classes of com.google.k2crypto.storage.driver.AddressUtilities

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.