1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
13   */
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  /**
31   * <a href="URLCodec.java.html"><b><i>View Source</i></b></a>
32   *
33   * @author Shuyang Zhou
34   * @author Brian Wing Shun Chan
35   */
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          /*if (unescapeSpaces) {
60              encodedURLString = StringUtil.replace(
61                  encodedURLString, "%20", StringPool.PLUS);
62          }*/
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 }