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

juzu.impl.common.PercentCodec 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.UndeclaredIOException;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.BitSet;

/**
 * Utility class for performing percent encoding / decoding.
 *
 * @author Julien Viet
 */
public final class PercentCodec {

  /** . */
  private static final byte[] codes;

  /** . */
  private static final int[] indices;

  static {

    Charset UTF8 = Charset.forName("UTF-8");
    CharsetEncoder encoder = UTF8.newEncoder();

    CharBuffer in = CharBuffer.allocate(1);
    ByteBuffer out = ByteBuffer.allocate(8);

    int size = 1000;

    // Original estimate size * 2
    byte[] _codes = new byte[size * 2];
    int[] _indices = new int[size + 1];

    int ptr = 0;

    for (char c = 0;c < size;c++) {
      switch (Character.getType(c)) {
        case Character.SURROGATE:
        case Character.PRIVATE_USE:
          break;
        default:
          if (encoder.canEncode(c)) {
            in.rewind();
            out.rewind();
            in.put(0, c);
            encoder.reset();
            encoder.encode(in, out, true);
            encoder.flush(out);
            int length = out.position();
            System.arraycopy(out.array(), 0, _codes, ptr, length);
            ptr += length;
          }
          else {
            //
          }
      }
      _indices[c + 1] = ptr;
    }

    //
    codes = _codes;
    indices = _indices;
  }

  static {
    BitSet allowed = new BitSet();

    // Unreserved
    for (char c = 'A';c <= 'Z';c++) {
      allowed.set(c);
    }
    for (char c = 'a';c <= 'z';c++) {
      allowed.set(c);
    }
    for (char c = '0';c <= '9';c++) {
      allowed.set(c);
    }
    allowed.set('_');
    allowed.set('.');
    allowed.set('-');
    allowed.set('~');

    // sub-delims
    allowed.set('!');
    allowed.set('$');
    allowed.set('&');
    allowed.set('\'');
    allowed.set('(');
    allowed.set(')');
    allowed.set('*');
    allowed.set('+');
    allowed.set(',');
    allowed.set(';');
    allowed.set('=');

    // ':' | '@'
    allowed.set(':');
    allowed.set('@');

    //
    PATH_SEGMENT = new PercentCodec(allowed);
  }

  static {
    BitSet allowed = new BitSet(128);
    for (char c = 'A';c <= 'Z';c++) {
      allowed.set(c);
    }
    for (char c = 'a';c <= 'z';c++) {
      allowed.set(c);
    }
    for (char c = '0';c <= '9';c++) {
      allowed.set(c);
    }
    allowed.set('_');
    allowed.set('.');
    allowed.set('-');
    allowed.set('~');

    // sub-delims without ( '&' | '=' )
    allowed.set('!');
    allowed.set('$');
    allowed.set('\'');
    allowed.set('(');
    allowed.set(')');
    allowed.set('*');
    allowed.set('+');
    allowed.set(',');
    allowed.set(';');

    // ':' | '@'
    allowed.set(':');
    allowed.set('@');

    // '?' | '/'
    allowed.set('?');
    allowed.set('/');

    //
    QUERY_PARAM = new PercentCodec(allowed);
  }

  /** . */
  private static final char[] ALPHABET = "0123456789ABCDEF".toCharArray();

  /** Path segment. */
  public static final PercentCodec PATH_SEGMENT;

  /** Query params name or value. */
  public static final PercentCodec QUERY_PARAM;

  /** . */
  private final BitSet allowed;

  private PercentCodec(BitSet allowed) {
    this.allowed = allowed;
  }

  public boolean accept(char c) {
    return c < allowed.length() && allowed.get(c);
  }

  public void encode(CharSequence s, Appendable appendable) throws IOException {
    for (int len = s.length(), i = 0;i < len;i++) {
      char c = s.charAt(i);
      encode(c, appendable);
    }
  }

  public String encode(CharSequence s) {
    try {
      StringBuilder sb = new StringBuilder(s.length());
      encode(s, sb);
      return sb.toString();
    }
    catch (IOException e) {
      throw new UndeclaredIOException(e);
    }
  }

  public void encode(char c, Appendable appendable) throws IOException {
    if (accept(c)) {
      appendable.append(c);
    }
    else {
      if (c < indices.length - 1) {
        int from = indices[c];
        int to = indices[c + 1];
        while (from < to) {
          byte b = codes[from++];
          appendable.append('%');
          appendable.append(ALPHABET[(b & 0xF0) >> 4]);
          appendable.append(ALPHABET[b & 0xF]);
        }
      }
      else {
        throw new UnsupportedOperationException("Not handled yet");
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy