/* * Copyright 2010 Ning, Inc. * * Ning licenses this file to you 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.ning.http.util; /** * Convenience class that encapsulates details of "percent encoding" * (as per RFC-3986, see [http://www.ietf.org/rfc/rfc3986.txt]). */ public class UTF8UrlEncoder { private static final boolean encodeSpaceUsingPlus = System.getProperty("com.com.ning.http.util.UTF8UrlEncoder.encodeSpaceUsingPlus") == null ? false : true; /** * Encoding table used for figuring out ascii characters that must be escaped * (all non-Ascii characers need to be encoded anyway) */ private final static int[] SAFE_ASCII = new int[128]; static { for (int i = 'a'; i <= 'z'; ++i) { SAFE_ASCII[i] = 1; } for (int i = 'A'; i <= 'Z'; ++i) { SAFE_ASCII[i] = 1; } for (int i = '0'; i <= '9'; ++i) { SAFE_ASCII[i] = 1; } SAFE_ASCII['-'] = 1; SAFE_ASCII['.'] = 1; SAFE_ASCII['_'] = 1; SAFE_ASCII['~'] = 1; } private final static char[] HEX = "0123456789ABCDEF".toCharArray(); private UTF8UrlEncoder() { } public static String encode(String input) { StringBuilder sb = new StringBuilder(input.length() + 16); appendEncoded(sb, input); return sb.toString(); } public static StringBuilder appendEncoded(StringBuilder sb, String input) { final int[] safe = SAFE_ASCII; for (int i = 0, len = input.length(); i < len; ++i) { char c = input.charAt(i); if (c <= 127) { if (safe[c] != 0) { sb.append(c); } else { appendSingleByteEncoded(sb, c); } } else { appendMultiByteEncoded(sb, c); } } return sb; } private final static void appendSingleByteEncoded(StringBuilder sb, int value) { if (encodeSpaceUsingPlus && value == 32) { sb.append('+'); return; } sb.append('%'); sb.append(HEX[value >> 4]); sb.append(HEX[value & 0xF]); } private final static void appendMultiByteEncoded(StringBuilder sb, int value) { // two or three bytes? (ignoring surrogate pairs for now, which would yield 4 bytes) if (value < 0x800) { appendSingleByteEncoded(sb, (0xc0 | (value >> 6))); appendSingleByteEncoded(sb, (0x80 | (value & 0x3f))); } else { appendSingleByteEncoded(sb, (0xe0 | (value >> 12))); appendSingleByteEncoded(sb, (0x80 | ((value >> 6) & 0x3f))); appendSingleByteEncoded(sb, (0x80 | (value & 0x3f))); } } }