Mercurial Hosting > luan
comparison src/org/eclipse/jetty/util/URIUtil.java @ 1013:6939226e0ac4
simplify URIUtil
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 24 Oct 2016 01:06:33 -0600 |
parents | 8e9db0bbf4f9 |
children |
comparison
equal
deleted
inserted
replaced
1012:8d0bdd357e6e | 1013:6939226e0ac4 |
---|---|
33 * <P>UTF-8 encoding is used by default for % encoded characters. This | 33 * <P>UTF-8 encoding is used by default for % encoded characters. This |
34 * may be overridden with the org.eclipse.jetty.util.URI.charset system property. | 34 * may be overridden with the org.eclipse.jetty.util.URI.charset system property. |
35 * @see UrlEncoded | 35 * @see UrlEncoded |
36 * | 36 * |
37 */ | 37 */ |
38 public class URIUtil | 38 public final class URIUtil { |
39 implements Cloneable | 39 // Use UTF-8 as per http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars |
40 { | 40 public static final String __CHARSET = StringUtil.__UTF8; |
41 public static final String SLASH="/"; | 41 |
42 public static final String HTTP="http"; | 42 private URIUtil() |
43 public static final String HTTP_COLON="http:"; | 43 {} |
44 public static final String HTTPS="https"; | 44 |
45 public static final String HTTPS_COLON="https:"; | 45 /* ------------------------------------------------------------ */ |
46 | 46 /** Encode a URI path. |
47 // Use UTF-8 as per http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars | 47 * This is the same encoding offered by URLEncoder, except that |
48 public static final String __CHARSET=System.getProperty("org.eclipse.jetty.util.URI.charset",StringUtil.__UTF8); | 48 * the '/' character is not encoded. |
49 | 49 * @param path The path the encode |
50 private URIUtil() | 50 * @return The encoded path |
51 {} | 51 */ |
52 | 52 public static String encodePath(String path) |
53 /* ------------------------------------------------------------ */ | 53 { |
54 /** Encode a URI path. | 54 if (path==null || path.length()==0) |
55 * This is the same encoding offered by URLEncoder, except that | 55 return path; |
56 * the '/' character is not encoded. | 56 |
57 * @param path The path the encode | 57 StringBuilder buf = encodePath(null,path); |
58 * @return The encoded path | 58 return buf==null?path:buf.toString(); |
59 */ | 59 } |
60 public static String encodePath(String path) | 60 |
61 { | 61 /* ------------------------------------------------------------ */ |
62 if (path==null || path.length()==0) | 62 /** Encode a URI path. |
63 return path; | 63 * @param path The path the encode |
64 | 64 * @param buf StringBuilder to encode path into (or null) |
65 StringBuilder buf = encodePath(null,path); | 65 * @return The StringBuilder or null if no substitutions required. |
66 return buf==null?path:buf.toString(); | 66 */ |
67 } | 67 public static StringBuilder encodePath(StringBuilder buf, String path) |
68 | 68 { |
69 /* ------------------------------------------------------------ */ | 69 byte[] bytes=null; |
70 /** Encode a URI path. | 70 if (buf==null) |
71 * @param path The path the encode | 71 { |
72 * @param buf StringBuilder to encode path into (or null) | 72 loop: |
73 * @return The StringBuilder or null if no substitutions required. | 73 for (int i=0;i<path.length();i++) |
74 */ | 74 { |
75 public static StringBuilder encodePath(StringBuilder buf, String path) | 75 char c=path.charAt(i); |
76 { | 76 switch(c) |
77 byte[] bytes=null; | 77 { |
78 if (buf==null) | 78 case '%': |
79 { | 79 case '?': |
80 loop: | 80 case ';': |
81 for (int i=0;i<path.length();i++) | 81 case '#': |
82 { | 82 case '\'': |
83 char c=path.charAt(i); | 83 case '"': |
84 switch(c) | 84 case '<': |
85 { | 85 case '>': |
86 case '%': | 86 case ' ': |
87 case '?': | 87 buf=new StringBuilder(path.length()*2); |
88 case ';': | 88 break loop; |
89 case '#': | 89 default: |
90 case '\'': | 90 if (c>127) |
91 case '"': | 91 { |
92 case '<': | 92 try |
93 case '>': | 93 { |
94 case ' ': | 94 bytes=path.getBytes(URIUtil.__CHARSET); |
95 buf=new StringBuilder(path.length()*2); | 95 } |
96 break loop; | 96 catch (UnsupportedEncodingException e) |
97 default: | 97 { |
98 if (c>127) | 98 throw new IllegalStateException(e); |
99 { | 99 } |
100 try | 100 buf=new StringBuilder(path.length()*2); |
101 { | 101 break loop; |
102 bytes=path.getBytes(URIUtil.__CHARSET); | 102 } |
103 } | 103 |
104 catch (UnsupportedEncodingException e) | 104 } |
105 { | 105 } |
106 throw new IllegalStateException(e); | 106 if (buf==null) |
107 } | 107 return null; |
108 buf=new StringBuilder(path.length()*2); | 108 } |
109 break loop; | 109 |
110 } | 110 synchronized(buf) |
111 | 111 { |
112 } | 112 if (bytes!=null) |
113 } | 113 { |
114 if (buf==null) | 114 for (int i=0;i<bytes.length;i++) |
115 return null; | 115 { |
116 } | 116 byte c=bytes[i]; |
117 | 117 switch(c) |
118 synchronized(buf) | 118 { |
119 { | 119 case '%': |
120 if (bytes!=null) | 120 buf.append("%25"); |
121 { | 121 continue; |
122 for (int i=0;i<bytes.length;i++) | 122 case '?': |
123 { | 123 buf.append("%3F"); |
124 byte c=bytes[i]; | 124 continue; |
125 switch(c) | 125 case ';': |
126 { | 126 buf.append("%3B"); |
127 case '%': | 127 continue; |
128 buf.append("%25"); | 128 case '#': |
129 continue; | 129 buf.append("%23"); |
130 case '?': | 130 continue; |
131 buf.append("%3F"); | 131 case '"': |
132 continue; | 132 buf.append("%22"); |
133 case ';': | 133 continue; |
134 buf.append("%3B"); | 134 case '\'': |
135 continue; | 135 buf.append("%27"); |
136 case '#': | 136 continue; |
137 buf.append("%23"); | 137 case '<': |
138 continue; | 138 buf.append("%3C"); |
139 case '"': | 139 continue; |
140 buf.append("%22"); | 140 case '>': |
141 continue; | 141 buf.append("%3E"); |
142 case '\'': | 142 continue; |
143 buf.append("%27"); | 143 case ' ': |
144 continue; | 144 buf.append("%20"); |
145 case '<': | 145 continue; |
146 buf.append("%3C"); | 146 default: |
147 continue; | 147 if (c<0) |
148 case '>': | 148 { |
149 buf.append("%3E"); | 149 buf.append('%'); |
150 continue; | 150 TypeUtil.toHex(c,buf); |
151 case ' ': | 151 } |
152 buf.append("%20"); | 152 else |
153 continue; | 153 buf.append((char)c); |
154 default: | 154 continue; |
155 if (c<0) | 155 } |
156 { | 156 } |
157 buf.append('%'); | 157 |
158 TypeUtil.toHex(c,buf); | 158 } |
159 } | 159 else |
160 else | 160 { |
161 buf.append((char)c); | 161 for (int i=0;i<path.length();i++) |
162 continue; | 162 { |
163 } | 163 char c=path.charAt(i); |
164 } | 164 switch(c) |
165 | 165 { |
166 } | 166 case '%': |
167 else | 167 buf.append("%25"); |
168 { | 168 continue; |
169 for (int i=0;i<path.length();i++) | 169 case '?': |
170 { | 170 buf.append("%3F"); |
171 char c=path.charAt(i); | 171 continue; |
172 switch(c) | 172 case ';': |
173 { | 173 buf.append("%3B"); |
174 case '%': | 174 continue; |
175 buf.append("%25"); | 175 case '#': |
176 continue; | 176 buf.append("%23"); |
177 case '?': | 177 continue; |
178 buf.append("%3F"); | 178 case '"': |
179 continue; | 179 buf.append("%22"); |
180 case ';': | 180 continue; |
181 buf.append("%3B"); | 181 case '\'': |
182 continue; | 182 buf.append("%27"); |
183 case '#': | 183 continue; |
184 buf.append("%23"); | 184 case '<': |
185 continue; | 185 buf.append("%3C"); |
186 case '"': | 186 continue; |
187 buf.append("%22"); | 187 case '>': |
188 continue; | 188 buf.append("%3E"); |
189 case '\'': | 189 continue; |
190 buf.append("%27"); | 190 case ' ': |
191 continue; | 191 buf.append("%20"); |
192 case '<': | 192 continue; |
193 buf.append("%3C"); | 193 default: |
194 continue; | 194 buf.append(c); |
195 case '>': | 195 continue; |
196 buf.append("%3E"); | 196 } |
197 continue; | 197 } |
198 case ' ': | 198 } |
199 buf.append("%20"); | 199 } |
200 continue; | 200 |
201 default: | 201 return buf; |
202 buf.append(c); | 202 } |
203 continue; | 203 |
204 } | 204 /* ------------------------------------------------------------ */ |
205 } | 205 /* Decode a URI path and strip parameters |
206 } | 206 * @param path The path the encode |
207 } | 207 * @param buf StringBuilder to encode path into |
208 | 208 */ |
209 return buf; | 209 public static String decodePath(String path) |
210 } | 210 { |
211 | 211 if (path==null) |
212 /* ------------------------------------------------------------ */ | 212 return null; |
213 /** Encode a URI path. | 213 // Array to hold all converted characters |
214 * @param path The path the encode | 214 char[] chars=null; |
215 * @param buf StringBuilder to encode path into (or null) | 215 int n=0; |
216 * @param encode String of characters to encode. % is always encoded. | 216 // Array to hold a sequence of %encodings |
217 * @return The StringBuilder or null if no substitutions required. | 217 byte[] bytes=null; |
218 */ | 218 int b=0; |
219 public static StringBuilder encodeString(StringBuilder buf, | 219 |
220 String path, | 220 int len=path.length(); |
221 String encode) | 221 |
222 { | 222 for (int i=0;i<len;i++) |
223 if (buf==null) | 223 { |
224 { | 224 char c = path.charAt(i); |
225 loop: | 225 |
226 for (int i=0;i<path.length();i++) | 226 if (c=='%' && (i+2)<len) |
227 { | 227 { |
228 char c=path.charAt(i); | 228 if (chars==null) |
229 if (c=='%' || encode.indexOf(c)>=0) | 229 { |
230 { | 230 chars=new char[len]; |
231 buf=new StringBuilder(path.length()<<1); | 231 bytes=new byte[len]; |
232 break loop; | 232 path.getChars(0,i,chars,0); |
233 } | 233 } |
234 } | 234 bytes[b++]=(byte)(0xff&TypeUtil.parseInt(path,i+1,2,16)); |
235 if (buf==null) | 235 i+=2; |
236 return null; | 236 continue; |
237 } | 237 } |
238 | 238 else if (c==';') |
239 synchronized(buf) | 239 { |
240 { | 240 if (chars==null) |
241 for (int i=0;i<path.length();i++) | 241 { |
242 { | 242 chars=new char[len]; |
243 char c=path.charAt(i); | 243 path.getChars(0,i,chars,0); |
244 if (c=='%' || encode.indexOf(c)>=0) | 244 n=i; |
245 { | 245 } |
246 buf.append('%'); | 246 break; |
247 StringUtil.append(buf,(byte)(0xff&c),16); | 247 } |
248 } | 248 else if (bytes==null) |
249 else | 249 { |
250 buf.append(c); | 250 n++; |
251 } | 251 continue; |
252 } | 252 } |
253 | 253 |
254 return buf; | 254 // Do we have some bytes to convert? |
255 } | 255 if (b>0) |
256 | 256 { |
257 /* ------------------------------------------------------------ */ | 257 // convert series of bytes and add to chars |
258 /* Decode a URI path and strip parameters | 258 String s; |
259 * @param path The path the encode | 259 try |
260 * @param buf StringBuilder to encode path into | 260 { |
261 */ | 261 s=new String(bytes,0,b,__CHARSET); |
262 public static String decodePath(String path) | 262 } |
263 { | 263 catch (UnsupportedEncodingException e) |
264 if (path==null) | 264 { |
265 return null; | 265 s=new String(bytes,0,b); |
266 // Array to hold all converted characters | 266 } |
267 char[] chars=null; | 267 s.getChars(0,s.length(),chars,n); |
268 int n=0; | 268 n+=s.length(); |
269 // Array to hold a sequence of %encodings | 269 b=0; |
270 byte[] bytes=null; | 270 } |
271 int b=0; | 271 |
272 | 272 chars[n++]=c; |
273 int len=path.length(); | 273 } |
274 | 274 |
275 for (int i=0;i<len;i++) | 275 if (chars==null) |
276 { | 276 return path; |
277 char c = path.charAt(i); | 277 |
278 | 278 // if we have a remaining sequence of bytes |
279 if (c=='%' && (i+2)<len) | 279 if (b>0) |
280 { | 280 { |
281 if (chars==null) | 281 // convert series of bytes and add to chars |
282 { | 282 String s; |
283 chars=new char[len]; | 283 try |
284 bytes=new byte[len]; | 284 { |
285 path.getChars(0,i,chars,0); | 285 s=new String(bytes,0,b,__CHARSET); |
286 } | 286 } |
287 bytes[b++]=(byte)(0xff&TypeUtil.parseInt(path,i+1,2,16)); | 287 catch (UnsupportedEncodingException e) |
288 i+=2; | 288 { |
289 continue; | 289 s=new String(bytes,0,b); |
290 } | 290 } |
291 else if (c==';') | 291 s.getChars(0,s.length(),chars,n); |
292 { | 292 n+=s.length(); |
293 if (chars==null) | 293 } |
294 { | 294 |
295 chars=new char[len]; | 295 return new String(chars,0,n); |
296 path.getChars(0,i,chars,0); | 296 } |
297 n=i; | 297 |
298 } | 298 /* ------------------------------------------------------------ */ |
299 break; | 299 /* Decode a URI path and strip parameters. |
300 } | 300 * @param path The path the encode |
301 else if (bytes==null) | 301 * @param buf StringBuilder to encode path into |
302 { | 302 */ |
303 n++; | 303 public static String decodePath(byte[] buf, int offset, int length) |
304 continue; | 304 { |
305 } | 305 byte[] bytes=null; |
306 | 306 int n=0; |
307 // Do we have some bytes to convert? | 307 |
308 if (b>0) | 308 for (int i=0;i<length;i++) |
309 { | 309 { |
310 // convert series of bytes and add to chars | 310 byte b = buf[i + offset]; |
311 String s; | 311 |
312 try | 312 if (b=='%' && (i+2)<length) |
313 { | 313 { |
314 s=new String(bytes,0,b,__CHARSET); | 314 b=(byte)(0xff&TypeUtil.parseInt(buf,i+offset+1,2,16)); |
315 } | 315 i+=2; |
316 catch (UnsupportedEncodingException e) | 316 } |
317 { | 317 else if (b==';') |
318 s=new String(bytes,0,b); | 318 { |
319 } | 319 length=i; |
320 s.getChars(0,s.length(),chars,n); | 320 break; |
321 n+=s.length(); | 321 } |
322 b=0; | 322 else if (bytes==null) |
323 } | 323 { |
324 | 324 n++; |
325 chars[n++]=c; | 325 continue; |
326 } | 326 } |
327 | 327 |
328 if (chars==null) | 328 if (bytes==null) |
329 return path; | 329 { |
330 | 330 bytes=new byte[length]; |
331 // if we have a remaining sequence of bytes | 331 for (int j=0;j<n;j++) |
332 if (b>0) | 332 bytes[j]=buf[j + offset]; |
333 { | 333 } |
334 // convert series of bytes and add to chars | 334 |
335 String s; | 335 bytes[n++]=b; |
336 try | 336 } |
337 { | 337 |
338 s=new String(bytes,0,b,__CHARSET); | 338 if (bytes==null) |
339 } | 339 return StringUtil.toString(buf,offset,length,__CHARSET); |
340 catch (UnsupportedEncodingException e) | 340 return StringUtil.toString(bytes,0,n,__CHARSET); |
341 { | 341 } |
342 s=new String(bytes,0,b); | 342 |
343 } | 343 |
344 s.getChars(0,s.length(),chars,n); | 344 /* ------------------------------------------------------------ */ |
345 n+=s.length(); | 345 /** Add two URI path segments. |
346 } | 346 * Handles null and empty paths, path and query params (eg ?a=b or |
347 | 347 * ;JSESSIONID=xxx) and avoids duplicate '/' |
348 return new String(chars,0,n); | 348 * @param p1 URI path segment (should be encoded) |
349 } | 349 * @param p2 URI path segment (should be encoded) |
350 | 350 * @return Legally combined path segments. |
351 /* ------------------------------------------------------------ */ | 351 */ |
352 /* Decode a URI path and strip parameters. | 352 public static String addPaths(String p1, String p2) |
353 * @param path The path the encode | 353 { |
354 * @param buf StringBuilder to encode path into | 354 if (p1==null || p1.length()==0) |
355 */ | 355 { |
356 public static String decodePath(byte[] buf, int offset, int length) | 356 if (p1!=null && p2==null) |
357 { | 357 return p1; |
358 byte[] bytes=null; | 358 return p2; |
359 int n=0; | 359 } |
360 | 360 if (p2==null || p2.length()==0) |
361 for (int i=0;i<length;i++) | 361 return p1; |
362 { | 362 |
363 byte b = buf[i + offset]; | 363 int split=p1.indexOf(';'); |
364 | 364 if (split<0) |
365 if (b=='%' && (i+2)<length) | 365 split=p1.indexOf('?'); |
366 { | 366 if (split==0) |
367 b=(byte)(0xff&TypeUtil.parseInt(buf,i+offset+1,2,16)); | 367 return p2+p1; |
368 i+=2; | 368 if (split<0) |
369 } | 369 split=p1.length(); |
370 else if (b==';') | 370 |
371 { | 371 StringBuilder buf = new StringBuilder(p1.length()+p2.length()+2); |
372 length=i; | 372 buf.append(p1); |
373 break; | 373 |
374 } | 374 if (buf.charAt(split-1)=='/') |
375 else if (bytes==null) | 375 { |
376 { | 376 if (p2.startsWith("/")) |
377 n++; | 377 { |
378 continue; | 378 buf.deleteCharAt(split-1); |
379 } | 379 buf.insert(split-1,p2); |
380 | 380 } |
381 if (bytes==null) | 381 else |
382 { | 382 buf.insert(split,p2); |
383 bytes=new byte[length]; | 383 } |
384 for (int j=0;j<n;j++) | 384 else |
385 bytes[j]=buf[j + offset]; | 385 { |
386 } | 386 if (p2.startsWith("/")) |
387 | 387 buf.insert(split,p2); |
388 bytes[n++]=b; | 388 else |
389 } | 389 { |
390 | 390 buf.insert(split,'/'); |
391 if (bytes==null) | 391 buf.insert(split+1,p2); |
392 return StringUtil.toString(buf,offset,length,__CHARSET); | 392 } |
393 return StringUtil.toString(bytes,0,n,__CHARSET); | 393 } |
394 } | 394 |
395 | 395 return buf.toString(); |
396 | 396 } |
397 /* ------------------------------------------------------------ */ | 397 |
398 /** Add two URI path segments. | 398 /* ------------------------------------------------------------ */ |
399 * Handles null and empty paths, path and query params (eg ?a=b or | 399 /** Return the parent Path. |
400 * ;JSESSIONID=xxx) and avoids duplicate '/' | 400 * Treat a URI like a directory path and return the parent directory. |
401 * @param p1 URI path segment (should be encoded) | 401 */ |
402 * @param p2 URI path segment (should be encoded) | 402 public static String parentPath(String p) |
403 * @return Legally combined path segments. | 403 { |
404 */ | 404 if (p==null || "/".equals(p)) |
405 public static String addPaths(String p1, String p2) | 405 return null; |
406 { | 406 int slash=p.lastIndexOf('/',p.length()-2); |
407 if (p1==null || p1.length()==0) | 407 if (slash>=0) |
408 { | 408 return p.substring(0,slash+1); |
409 if (p1!=null && p2==null) | 409 return null; |
410 return p1; | 410 } |
411 return p2; | 411 |
412 } | 412 /* ------------------------------------------------------------ */ |
413 if (p2==null || p2.length()==0) | 413 /** Convert a path to a cananonical form. |
414 return p1; | 414 * All instances of "." and ".." are factored out. Null is returned |
415 | 415 * if the path tries to .. above its root. |
416 int split=p1.indexOf(';'); | 416 * @param path |
417 if (split<0) | 417 * @return path or null. |
418 split=p1.indexOf('?'); | 418 */ |
419 if (split==0) | 419 public static String canonicalPath(String path) |
420 return p2+p1; | 420 { |
421 if (split<0) | 421 if (path==null || path.length()==0) |
422 split=p1.length(); | 422 return path; |
423 | 423 |
424 StringBuilder buf = new StringBuilder(p1.length()+p2.length()+2); | 424 int end=path.length(); |
425 buf.append(p1); | 425 int start = path.lastIndexOf('/', end); |
426 | 426 |
427 if (buf.charAt(split-1)=='/') | 427 search: |
428 { | 428 while (end>0) |
429 if (p2.startsWith(URIUtil.SLASH)) | 429 { |
430 { | 430 switch(end-start) |
431 buf.deleteCharAt(split-1); | 431 { |
432 buf.insert(split-1,p2); | 432 case 2: // possible single dot |
433 } | 433 if (path.charAt(start+1)!='.') |
434 else | 434 break; |
435 buf.insert(split,p2); | 435 break search; |
436 } | 436 case 3: // possible double dot |
437 else | 437 if (path.charAt(start+1)!='.' || path.charAt(start+2)!='.') |
438 { | 438 break; |
439 if (p2.startsWith(URIUtil.SLASH)) | 439 break search; |
440 buf.insert(split,p2); | 440 } |
441 else | 441 |
442 { | 442 end=start; |
443 buf.insert(split,'/'); | 443 start=path.lastIndexOf('/',end-1); |
444 buf.insert(split+1,p2); | 444 } |
445 } | 445 |
446 } | 446 // If we have checked the entire string |
447 | 447 if (start>=end) |
448 return buf.toString(); | 448 return path; |
449 } | 449 |
450 | 450 StringBuilder buf = new StringBuilder(path); |
451 /* ------------------------------------------------------------ */ | 451 int delStart=-1; |
452 /** Return the parent Path. | 452 int delEnd=-1; |
453 * Treat a URI like a directory path and return the parent directory. | 453 int skip=0; |
454 */ | 454 |
455 public static String parentPath(String p) | 455 while (end>0) |
456 { | 456 { |
457 if (p==null || URIUtil.SLASH.equals(p)) | 457 switch(end-start) |
458 return null; | 458 { |
459 int slash=p.lastIndexOf('/',p.length()-2); | 459 case 2: // possible single dot |
460 if (slash>=0) | 460 if (buf.charAt(start+1)!='.') |
461 return p.substring(0,slash+1); | 461 { |
462 return null; | 462 if (skip>0 && --skip==0) |
463 } | 463 { |
464 | 464 delStart=start>=0?start:0; |
465 /* ------------------------------------------------------------ */ | 465 if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.') |
466 /** Convert a path to a cananonical form. | 466 delStart++; |
467 * All instances of "." and ".." are factored out. Null is returned | 467 } |
468 * if the path tries to .. above its root. | 468 break; |
469 * @param path | 469 } |
470 * @return path or null. | 470 |
471 */ | 471 if(start<0 && buf.length()>2 && buf.charAt(1)=='/' && buf.charAt(2)=='/') |
472 public static String canonicalPath(String path) | 472 break; |
473 { | 473 |
474 if (path==null || path.length()==0) | 474 if(delEnd<0) |
475 return path; | 475 delEnd=end; |
476 | 476 delStart=start; |
477 int end=path.length(); | 477 if (delStart<0 || delStart==0&&buf.charAt(delStart)=='/') |
478 int start = path.lastIndexOf('/', end); | 478 { |
479 | 479 delStart++; |
480 search: | 480 if (delEnd<buf.length() && buf.charAt(delEnd)=='/') |
481 while (end>0) | 481 delEnd++; |
482 { | 482 break; |
483 switch(end-start) | 483 } |
484 { | 484 if (end==buf.length()) |
485 case 2: // possible single dot | 485 delStart++; |
486 if (path.charAt(start+1)!='.') | 486 |
487 break; | 487 end=start--; |
488 break search; | 488 while (start>=0 && buf.charAt(start)!='/') |
489 case 3: // possible double dot | 489 start--; |
490 if (path.charAt(start+1)!='.' || path.charAt(start+2)!='.') | 490 continue; |
491 break; | 491 |
492 break search; | 492 case 3: // possible double dot |
493 } | 493 if (buf.charAt(start+1)!='.' || buf.charAt(start+2)!='.') |
494 | 494 { |
495 end=start; | 495 if (skip>0 && --skip==0) |
496 start=path.lastIndexOf('/',end-1); | 496 { delStart=start>=0?start:0; |
497 } | 497 if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.') |
498 | 498 delStart++; |
499 // If we have checked the entire string | 499 } |
500 if (start>=end) | 500 break; |
501 return path; | 501 } |
502 | 502 |
503 StringBuilder buf = new StringBuilder(path); | 503 delStart=start; |
504 int delStart=-1; | 504 if (delEnd<0) |
505 int delEnd=-1; | 505 delEnd=end; |
506 int skip=0; | 506 |
507 | 507 skip++; |
508 while (end>0) | 508 end=start--; |
509 { | 509 while (start>=0 && buf.charAt(start)!='/') |
510 switch(end-start) | 510 start--; |
511 { | 511 continue; |
512 case 2: // possible single dot | 512 |
513 if (buf.charAt(start+1)!='.') | 513 default: |
514 { | 514 if (skip>0 && --skip==0) |
515 if (skip>0 && --skip==0) | 515 { |
516 { | 516 delStart=start>=0?start:0; |
517 delStart=start>=0?start:0; | 517 if(delEnd==buf.length() && buf.charAt(delEnd-1)=='.') |
518 if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.') | 518 delStart++; |
519 delStart++; | 519 } |
520 } | 520 } |
521 break; | 521 |
522 } | 522 // Do the delete |
523 | 523 if (skip<=0 && delStart>=0 && delEnd>=delStart) |
524 if(start<0 && buf.length()>2 && buf.charAt(1)=='/' && buf.charAt(2)=='/') | 524 { |
525 break; | 525 buf.delete(delStart,delEnd); |
526 | 526 delStart=delEnd=-1; |
527 if(delEnd<0) | 527 if (skip>0) |
528 delEnd=end; | 528 delEnd=end; |
529 delStart=start; | 529 } |
530 if (delStart<0 || delStart==0&&buf.charAt(delStart)=='/') | 530 |
531 { | 531 end=start--; |
532 delStart++; | 532 while (start>=0 && buf.charAt(start)!='/') |
533 if (delEnd<buf.length() && buf.charAt(delEnd)=='/') | 533 start--; |
534 delEnd++; | 534 } |
535 break; | 535 |
536 } | 536 // Too many .. |
537 if (end==buf.length()) | 537 if (skip>0) |
538 delStart++; | 538 return null; |
539 | 539 |
540 end=start--; | 540 // Do the delete |
541 while (start>=0 && buf.charAt(start)!='/') | 541 if (delEnd>=0) |
542 start--; | 542 buf.delete(delStart,delEnd); |
543 continue; | 543 |
544 | 544 return buf.toString(); |
545 case 3: // possible double dot | 545 } |
546 if (buf.charAt(start+1)!='.' || buf.charAt(start+2)!='.') | 546 |
547 { | 547 /* ------------------------------------------------------------ */ |
548 if (skip>0 && --skip==0) | 548 /** |
549 { delStart=start>=0?start:0; | 549 * @param uri URI |
550 if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.') | 550 * @return True if the uri has a scheme |
551 delStart++; | 551 */ |
552 } | 552 public static boolean hasScheme(String uri) |
553 break; | 553 { |
554 } | 554 for (int i=0;i<uri.length();i++) |
555 | 555 { |
556 delStart=start; | 556 char c=uri.charAt(i); |
557 if (delEnd<0) | 557 if (c==':') |
558 delEnd=end; | 558 return true; |
559 | 559 if (!(c>='a'&&c<='z' || |
560 skip++; | 560 c>='A'&&c<='Z' || |
561 end=start--; | 561 (i>0 &&(c>='0'&&c<='9' || |
562 while (start>=0 && buf.charAt(start)!='/') | 562 c=='.' || |
563 start--; | 563 c=='+' || |
564 continue; | 564 c=='-')) |
565 | 565 )) |
566 default: | 566 break; |
567 if (skip>0 && --skip==0) | 567 } |
568 { | 568 return false; |
569 delStart=start>=0?start:0; | 569 } |
570 if(delEnd==buf.length() && buf.charAt(delEnd-1)=='.') | 570 |
571 delStart++; | |
572 } | |
573 } | |
574 | |
575 // Do the delete | |
576 if (skip<=0 && delStart>=0 && delEnd>=delStart) | |
577 { | |
578 buf.delete(delStart,delEnd); | |
579 delStart=delEnd=-1; | |
580 if (skip>0) | |
581 delEnd=end; | |
582 } | |
583 | |
584 end=start--; | |
585 while (start>=0 && buf.charAt(start)!='/') | |
586 start--; | |
587 } | |
588 | |
589 // Too many .. | |
590 if (skip>0) | |
591 return null; | |
592 | |
593 // Do the delete | |
594 if (delEnd>=0) | |
595 buf.delete(delStart,delEnd); | |
596 | |
597 return buf.toString(); | |
598 } | |
599 | |
600 /* ------------------------------------------------------------ */ | |
601 /** Convert a path to a compact form. | |
602 * All instances of "//" and "///" etc. are factored out to single "/" | |
603 * @param path | |
604 * @return path | |
605 */ | |
606 public static String compactPath(String path) | |
607 { | |
608 if (path==null || path.length()==0) | |
609 return path; | |
610 | |
611 int state=0; | |
612 int end=path.length(); | |
613 int i=0; | |
614 | |
615 loop: | |
616 while (i<end) | |
617 { | |
618 char c=path.charAt(i); | |
619 switch(c) | |
620 { | |
621 case '?': | |
622 return path; | |
623 case '/': | |
624 state++; | |
625 if (state==2) | |
626 break loop; | |
627 break; | |
628 default: | |
629 state=0; | |
630 } | |
631 i++; | |
632 } | |
633 | |
634 if (state<2) | |
635 return path; | |
636 | |
637 StringBuffer buf = new StringBuffer(path.length()); | |
638 buf.append(path,0,i); | |
639 | |
640 loop2: | |
641 while (i<end) | |
642 { | |
643 char c=path.charAt(i); | |
644 switch(c) | |
645 { | |
646 case '?': | |
647 buf.append(path,i,end); | |
648 break loop2; | |
649 case '/': | |
650 if (state++==0) | |
651 buf.append(c); | |
652 break; | |
653 default: | |
654 state=0; | |
655 buf.append(c); | |
656 } | |
657 i++; | |
658 } | |
659 | |
660 return buf.toString(); | |
661 } | |
662 | |
663 /* ------------------------------------------------------------ */ | |
664 /** | |
665 * @param uri URI | |
666 * @return True if the uri has a scheme | |
667 */ | |
668 public static boolean hasScheme(String uri) | |
669 { | |
670 for (int i=0;i<uri.length();i++) | |
671 { | |
672 char c=uri.charAt(i); | |
673 if (c==':') | |
674 return true; | |
675 if (!(c>='a'&&c<='z' || | |
676 c>='A'&&c<='Z' || | |
677 (i>0 &&(c>='0'&&c<='9' || | |
678 c=='.' || | |
679 c=='+' || | |
680 c=='-')) | |
681 )) | |
682 break; | |
683 } | |
684 return false; | |
685 } | |
686 | |
687 } | 571 } |
688 | 572 |
689 | 573 |
690 | 574 |