1
14
15 package com.liferay.portal.kernel.util;
16
17 import com.liferay.portal.kernel.log.Log;
18 import com.liferay.portal.kernel.log.LogFactoryUtil;
19 import com.liferay.portal.kernel.nio.charset.CharsetDecoderUtil;
20 import com.liferay.portal.kernel.nio.charset.CharsetEncoderUtil;
21
22 import java.nio.ByteBuffer;
23 import java.nio.CharBuffer;
24 import java.nio.charset.CharacterCodingException;
25 import java.nio.charset.CharsetDecoder;
26 import java.nio.charset.CharsetEncoder;
27
28 import java.util.BitSet;
29
30
36 public class URLCodec {
37
38 public static String decodeURL(String encodedURLString) {
39 return decodeURL(encodedURLString, StringPool.UTF8, false);
40 }
41
42 public static String decodeURL(
43 String encodedURLString, boolean unescapeSpaces) {
44
45 return decodeURL(encodedURLString, StringPool.UTF8, unescapeSpaces);
46 }
47
48 public static String decodeURL(
49 String encodedURLString, String charsetName, boolean unescapeSpaces) {
50
51 if (encodedURLString == null) {
52 return null;
53 }
54
55 if (encodedURLString.length() == 0) {
56 return StringPool.BLANK;
57 }
58
59
63
64 StringBuilder sb = new StringBuilder(encodedURLString.length());
65
66 CharsetDecoder charsetDecoder = null;
67
68 boolean modified = false;
69
70 for (int i = 0; i < encodedURLString.length(); i++) {
71 char c = encodedURLString.charAt(i);
72
73 if (c == CharPool.PERCENT) {
74 ByteBuffer byteBuffer = _getEncodedByteBuffer(
75 encodedURLString, i);
76
77 if (charsetDecoder == null) {
78 charsetDecoder = CharsetDecoderUtil.getCharsetDecoder(
79 charsetName);
80 }
81
82 CharBuffer charBuffer = null;
83
84 try {
85 charBuffer = charsetDecoder.decode(byteBuffer);
86 }
87 catch (CharacterCodingException cce) {
88 _log.error(cce, cce);
89
90 return StringPool.BLANK;
91 }
92
93 sb.append(charBuffer);
94
95 i += byteBuffer.capacity() * 3 - 1;
96 }
97 else if (c == CharPool.PLUS) {
98 sb.append(CharPool.SPACE);
99
100 modified = true;
101 }
102 else {
103 sb.append(c);
104 }
105 }
106
107 if (!modified && (sb.length() == encodedURLString.length())) {
108 return encodedURLString;
109 }
110 else {
111 return sb.toString();
112 }
113 }
114
115 public static String encodeURL(String rawURLString) {
116 return encodeURL(rawURLString, StringPool.UTF8, false);
117 }
118
119 public static String encodeURL(String rawURLString, boolean escapeSpaces) {
120 return encodeURL(rawURLString, StringPool.UTF8, escapeSpaces);
121 }
122
123 public static String encodeURL(
124 String rawURLString, String charsetName, boolean escapeSpaces) {
125
126 if (rawURLString == null) {
127 return null;
128 }
129
130 if (rawURLString.length() == 0) {
131 return StringPool.BLANK;
132 }
133
134 StringBuilder sb = new StringBuilder(rawURLString.length());
135
136 CharsetEncoder charsetEncoder = null;
137
138 char[] hexes = new char[2];
139
140 boolean modified = false;
141
142 for (int i = 0; i < rawURLString.length(); i++) {
143 char c = rawURLString.charAt(i);
144
145 if (_validChars.get(c)) {
146 sb.append(c);
147 }
148 else if (c == CharPool.SPACE) {
149 if (escapeSpaces) {
150 sb.append("%20");
151 }
152 else {
153 sb.append(CharPool.PLUS);
154 }
155
156 modified = true;
157 }
158 else {
159 CharBuffer charBuffer = _getRawCharBuffer(rawURLString, i);
160
161 if (charsetEncoder == null) {
162 charsetEncoder = CharsetEncoderUtil.getCharsetEncoder(
163 charsetName);
164 }
165
166 i += charBuffer.length() - 1;
167
168 ByteBuffer byteBuffer = null;
169
170 try {
171 byteBuffer = charsetEncoder.encode(charBuffer);
172 }
173 catch (CharacterCodingException cce) {
174 _log.error(cce, cce);
175
176 return StringPool.BLANK;
177 }
178
179 for (int j = byteBuffer.position(); j < byteBuffer.limit();
180 j++) {
181
182 sb.append(CharPool.PERCENT);
183 sb.append(
184 UnicodeFormatter.byteToHex(byteBuffer.get(), hexes));
185 }
186 }
187 }
188
189 if (!modified && (sb.length() == rawURLString.length())) {
190 return rawURLString;
191 }
192 else {
193 return sb.toString();
194 }
195 }
196
197 private static int _charToHex(char c) {
198 if ((c >= CharPool.LOWER_CASE_A) && (c <= CharPool.LOWER_CASE_Z)) {
199 return c - CharPool.LOWER_CASE_A + 10;
200 }
201
202 if ((c >= CharPool.UPPER_CASE_A) && (c <= CharPool.UPPER_CASE_Z)) {
203 return c - CharPool.UPPER_CASE_A + 10;
204 }
205
206 if ((c >= CharPool.NUMBER_0) && (c <= CharPool.NUMBER_9)) {
207 return c - CharPool.NUMBER_0;
208 }
209
210 throw new IllegalArgumentException(c + " is not a hex char");
211 }
212
213 private static ByteBuffer _getEncodedByteBuffer(
214 String encodedString, int start) {
215
216 int count = 1;
217
218 for (int i = start + 3; i < encodedString.length(); i += 3) {
219 if (encodedString.charAt(i) == CharPool.PERCENT) {
220 count++;
221 }
222 else {
223 break;
224 }
225 }
226
227 ByteBuffer byteBuffer = ByteBuffer.allocate(count);
228
229 for (int i = start; i < start + count * 3; i += 3) {
230 int high = _charToHex(encodedString.charAt(i + 1));
231 int low = _charToHex(encodedString.charAt(i + 2));
232
233 byteBuffer.put((byte)((high << 4) + low));
234 }
235
236 byteBuffer.flip();
237
238 return byteBuffer;
239 }
240
241 private static CharBuffer _getRawCharBuffer(String rawString, int start) {
242 int count = 0;
243
244 for (int i = start; i < rawString.length(); i++) {
245 char rawChar = rawString.charAt(i);
246
247 if (!_validChars.get(rawChar)) {
248 count++;
249
250 if (Character.isHighSurrogate(rawChar)) {
251 if (((i + 1) < rawString.length()) &&
252 Character.isLowSurrogate(rawString.charAt(i + 1))) {
253
254 count++;
255 }
256 }
257 }
258 else {
259 break;
260 }
261 }
262
263 return CharBuffer.wrap(rawString, start, start + count);
264 }
265
266 private static Log _log = LogFactoryUtil.getLog(URLCodec.class);
267
268 private static BitSet _validChars = new BitSet(256);
269
270 static {
271 for (int i = 'a'; i <= 'z'; i++) {
272 _validChars.set(i);
273 }
274
275 for (int i = 'A'; i <= 'Z'; i++) {
276 _validChars.set(i);
277 }
278
279 for (int i = '0'; i <= '9'; i++) {
280 _validChars.set(i);
281 }
282
283 _validChars.set('-');
284 _validChars.set('_');
285 _validChars.set('.');
286 _validChars.set('*');
287 }
288
289 }