001
014
015 package com.liferay.portal.kernel.util;
016
017 import com.liferay.portal.kernel.log.Log;
018 import com.liferay.portal.kernel.log.LogFactoryUtil;
019 import com.liferay.portal.kernel.nio.charset.CharsetDecoderUtil;
020 import com.liferay.portal.kernel.nio.charset.CharsetEncoderUtil;
021
022 import java.nio.ByteBuffer;
023 import java.nio.CharBuffer;
024 import java.nio.charset.CharacterCodingException;
025 import java.nio.charset.CharsetDecoder;
026 import java.nio.charset.CharsetEncoder;
027
028 import java.util.BitSet;
029
030
034 public class URLCodec {
035
036 public static String decodeURL(String encodedURLString) {
037 return decodeURL(encodedURLString, StringPool.UTF8, false);
038 }
039
040 public static String decodeURL(
041 String encodedURLString, boolean unescapeSpaces) {
042
043 return decodeURL(encodedURLString, StringPool.UTF8, unescapeSpaces);
044 }
045
046 public static String decodeURL(
047 String encodedURLString, String charsetName, boolean unescapeSpaces) {
048
049 if (encodedURLString == null) {
050 return null;
051 }
052
053 if (encodedURLString.length() == 0) {
054 return StringPool.BLANK;
055 }
056
057
061
062 StringBuilder sb = new StringBuilder(encodedURLString.length());
063
064 CharsetDecoder charsetDecoder = null;
065
066 boolean modified = false;
067
068 for (int i = 0; i < encodedURLString.length(); i++) {
069 char c = encodedURLString.charAt(i);
070
071 if (c == CharPool.PERCENT) {
072 ByteBuffer byteBuffer = _getEncodedByteBuffer(
073 encodedURLString, i);
074
075 if (charsetDecoder == null) {
076 charsetDecoder = CharsetDecoderUtil.getCharsetDecoder(
077 charsetName);
078 }
079
080 CharBuffer charBuffer = null;
081
082 try {
083 charBuffer = charsetDecoder.decode(byteBuffer);
084 }
085 catch (CharacterCodingException cce) {
086 _log.error(cce, cce);
087
088 return StringPool.BLANK;
089 }
090
091 sb.append(charBuffer);
092
093 i += byteBuffer.capacity() * 3 - 1;
094 }
095 else if (c == CharPool.PLUS) {
096 sb.append(CharPool.SPACE);
097
098 modified = true;
099 }
100 else {
101 sb.append(c);
102 }
103 }
104
105 if (!modified && (sb.length() == encodedURLString.length())) {
106 return encodedURLString;
107 }
108 else {
109 return sb.toString();
110 }
111 }
112
113 public static String encodeURL(String rawURLString) {
114 return encodeURL(rawURLString, StringPool.UTF8, false);
115 }
116
117 public static String encodeURL(String rawURLString, boolean escapeSpaces) {
118 return encodeURL(rawURLString, StringPool.UTF8, escapeSpaces);
119 }
120
121 public static String encodeURL(
122 String rawURLString, String charsetName, boolean escapeSpaces) {
123
124 if (rawURLString == null) {
125 return null;
126 }
127
128 if (rawURLString.length() == 0) {
129 return StringPool.BLANK;
130 }
131
132 StringBuilder sb = new StringBuilder(rawURLString.length());
133
134 CharsetEncoder charsetEncoder = null;
135
136 char[] hexes = new char[2];
137
138 boolean modified = false;
139
140 for (int i = 0; i < rawURLString.length(); i++) {
141 char c = rawURLString.charAt(i);
142
143 if (_validChars.get(c)) {
144 sb.append(c);
145 }
146 else if (c == CharPool.SPACE) {
147 if (escapeSpaces) {
148 sb.append("%20");
149 }
150 else {
151 sb.append(CharPool.PLUS);
152 }
153
154 modified = true;
155 }
156 else {
157 CharBuffer charBuffer = _getRawCharBuffer(rawURLString, i);
158
159 if (charsetEncoder == null) {
160 charsetEncoder = CharsetEncoderUtil.getCharsetEncoder(
161 charsetName);
162 }
163
164 i += charBuffer.length() - 1;
165
166 ByteBuffer byteBuffer = null;
167
168 try {
169 byteBuffer = charsetEncoder.encode(charBuffer);
170 }
171 catch (CharacterCodingException cce) {
172 _log.error(cce, cce);
173
174 return StringPool.BLANK;
175 }
176
177 for (int j = byteBuffer.position(); j < byteBuffer.limit();
178 j++) {
179
180 sb.append(CharPool.PERCENT);
181 sb.append(
182 UnicodeFormatter.byteToHex(byteBuffer.get(), hexes));
183 }
184 }
185 }
186
187 if (!modified && (sb.length() == rawURLString.length())) {
188 return rawURLString;
189 }
190 else {
191 return sb.toString();
192 }
193 }
194
195 private static int _charToHex(char c) {
196 if ((c >= CharPool.LOWER_CASE_A) && (c <= CharPool.LOWER_CASE_Z)) {
197 return c - CharPool.LOWER_CASE_A + 10;
198 }
199
200 if ((c >= CharPool.UPPER_CASE_A) && (c <= CharPool.UPPER_CASE_Z)) {
201 return c - CharPool.UPPER_CASE_A + 10;
202 }
203
204 if ((c >= CharPool.NUMBER_0) && (c <= CharPool.NUMBER_9)) {
205 return c - CharPool.NUMBER_0;
206 }
207
208 throw new IllegalArgumentException(c + " is not a hex char");
209 }
210
211 private static ByteBuffer _getEncodedByteBuffer(
212 String encodedString, int start) {
213
214 int count = 1;
215
216 for (int i = start + 3; i < encodedString.length(); i += 3) {
217 if (encodedString.charAt(i) == CharPool.PERCENT) {
218 count++;
219 }
220 else {
221 break;
222 }
223 }
224
225 ByteBuffer byteBuffer = ByteBuffer.allocate(count);
226
227 for (int i = start; i < start + count * 3; i += 3) {
228 int high = _charToHex(encodedString.charAt(i + 1));
229 int low = _charToHex(encodedString.charAt(i + 2));
230
231 byteBuffer.put((byte)((high << 4) + low));
232 }
233
234 byteBuffer.flip();
235
236 return byteBuffer;
237 }
238
239 private static CharBuffer _getRawCharBuffer(String rawString, int start) {
240 int count = 0;
241
242 for (int i = start; i < rawString.length(); i++) {
243 char rawChar = rawString.charAt(i);
244
245 if (!_validChars.get(rawChar)) {
246 count++;
247
248 if (Character.isHighSurrogate(rawChar)) {
249 if (((i + 1) < rawString.length()) &&
250 Character.isLowSurrogate(rawString.charAt(i + 1))) {
251
252 count++;
253 }
254 }
255 }
256 else {
257 break;
258 }
259 }
260
261 return CharBuffer.wrap(rawString, start, start + count);
262 }
263
264 private static Log _log = LogFactoryUtil.getLog(URLCodec.class);
265
266 private static BitSet _validChars = new BitSet(256);
267
268 static {
269 for (int i = 'a'; i <= 'z'; i++) {
270 _validChars.set(i);
271 }
272
273 for (int i = 'A'; i <= 'Z'; i++) {
274 _validChars.set(i);
275 }
276
277 for (int i = '0'; i <= '9'; i++) {
278 _validChars.set(i);
279 }
280
281 _validChars.set('-');
282 _validChars.set('_');
283 _validChars.set('.');
284 _validChars.set('*');
285 }
286
287 }