1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.mortbay.util;
16
17 import java.io.UnsupportedEncodingException;
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 public class URIUtil
33 implements Cloneable
34 {
35 public static final String SLASH="/";
36 public static final String HTTP="http";
37 public static final String HTTP_COLON="http:";
38 public static final String HTTPS="https";
39 public static final String HTTPS_COLON="https:";
40
41
42 public static final String __CHARSET=System.getProperty("org.mortbay.util.URI.charset",StringUtil.__UTF8);
43
44 private URIUtil()
45 {}
46
47
48
49
50
51
52
53
54 public static String encodePath(String path)
55 {
56 if (path==null || path.length()==0)
57 return path;
58
59 StringBuffer buf = encodePath(null,path);
60 return buf==null?path:buf.toString();
61 }
62
63
64
65
66
67
68
69 public static StringBuffer encodePath(StringBuffer buf, String path)
70 {
71 if (buf==null)
72 {
73 loop:
74 for (int i=0;i<path.length();i++)
75 {
76 char c=path.charAt(i);
77 switch(c)
78 {
79 case '%':
80 case '?':
81 case ';':
82 case '#':
83 case ' ':
84 buf=new StringBuffer(path.length()<<1);
85 break loop;
86 }
87 }
88 if (buf==null)
89 return null;
90 }
91
92 synchronized(buf)
93 {
94 for (int i=0;i<path.length();i++)
95 {
96 char c=path.charAt(i);
97 switch(c)
98 {
99 case '%':
100 buf.append("%25");
101 continue;
102 case '?':
103 buf.append("%3F");
104 continue;
105 case ';':
106 buf.append("%3B");
107 continue;
108 case '#':
109 buf.append("%23");
110 continue;
111 case ' ':
112 buf.append("%20");
113 continue;
114 default:
115 buf.append(c);
116 continue;
117 }
118 }
119 }
120
121 return buf;
122 }
123
124
125
126
127
128
129
130
131 public static StringBuffer encodeString(StringBuffer buf,
132 String path,
133 String encode)
134 {
135 if (buf==null)
136 {
137 loop:
138 for (int i=0;i<path.length();i++)
139 {
140 char c=path.charAt(i);
141 if (c=='%' || encode.indexOf(c)>=0)
142 {
143 buf=new StringBuffer(path.length()<<1);
144 break loop;
145 }
146 }
147 if (buf==null)
148 return null;
149 }
150
151 synchronized(buf)
152 {
153 for (int i=0;i<path.length();i++)
154 {
155 char c=path.charAt(i);
156 if (c=='%' || encode.indexOf(c)>=0)
157 {
158 buf.append('%');
159 StringUtil.append(buf,(byte)(0xff&c),16);
160 }
161 else
162 buf.append(c);
163 }
164 }
165
166 return buf;
167 }
168
169
170
171
172
173
174 public static String decodePath(String path)
175 {
176 if (path==null)
177 return null;
178 char[] chars=null;
179 int n=0;
180 byte[] bytes=null;
181 int b=0;
182
183 int len=path.length();
184
185 for (int i=0;i<len;i++)
186 {
187 char c = path.charAt(i);
188
189 if (c=='%' && (i+2)<len)
190 {
191 if (chars==null)
192 {
193 chars=new char[len];
194 bytes=new byte[len];
195 path.getChars(0,i,chars,0);
196 }
197 bytes[b++]=(byte)(0xff&TypeUtil.parseInt(path,i+1,2,16));
198 i+=2;
199 continue;
200 }
201 else if (bytes==null)
202 {
203 n++;
204 continue;
205 }
206
207 if (b>0)
208 {
209 String s;
210 try
211 {
212 s=new String(bytes,0,b,__CHARSET);
213 }
214 catch (UnsupportedEncodingException e)
215 {
216 s=new String(bytes,0,b);
217 }
218 s.getChars(0,s.length(),chars,n);
219 n+=s.length();
220 b=0;
221 }
222
223 chars[n++]=c;
224 }
225
226 if (chars==null)
227 return path;
228
229 if (b>0)
230 {
231 String s;
232 try
233 {
234 s=new String(bytes,0,b,__CHARSET);
235 }
236 catch (UnsupportedEncodingException e)
237 {
238 s=new String(bytes,0,b);
239 }
240 s.getChars(0,s.length(),chars,n);
241 n+=s.length();
242 }
243
244 return new String(chars,0,n);
245 }
246
247
248
249
250
251
252 public static String decodePath(byte[] buf, int offset, int length)
253 {
254 byte[] bytes=null;
255 int n=0;
256
257 for (int i=0;i<length;i++)
258 {
259 byte b = buf[i + offset];
260
261 if (b=='%' && (i+2)<length)
262 {
263 b=(byte)(0xff&TypeUtil.parseInt(buf,i+offset+1,2,16));
264 i+=2;
265 }
266 else if (bytes==null)
267 {
268 n++;
269 continue;
270 }
271
272 if (bytes==null)
273 {
274 bytes=new byte[length];
275 for (int j=0;j<n;j++)
276 bytes[j]=buf[j + offset];
277 }
278
279 bytes[n++]=b;
280 }
281
282 if (bytes==null)
283 return StringUtil.toString(buf,offset,length,__CHARSET);
284 return StringUtil.toString(bytes,0,n,__CHARSET);
285 }
286
287
288
289
290
291
292
293
294
295
296 public static String addPaths(String p1, String p2)
297 {
298 if (p1==null || p1.length()==0)
299 {
300 if (p1!=null && p2==null)
301 return p1;
302 return p2;
303 }
304 if (p2==null || p2.length()==0)
305 return p1;
306
307 int split=p1.indexOf(';');
308 if (split<0)
309 split=p1.indexOf('?');
310 if (split==0)
311 return p2+p1;
312 if (split<0)
313 split=p1.length();
314
315 StringBuffer buf = new StringBuffer(p1.length()+p2.length()+2);
316 buf.append(p1);
317
318 if (buf.charAt(split-1)=='/')
319 {
320 if (p2.startsWith(URIUtil.SLASH))
321 {
322 buf.deleteCharAt(split-1);
323 buf.insert(split-1,p2);
324 }
325 else
326 buf.insert(split,p2);
327 }
328 else
329 {
330 if (p2.startsWith(URIUtil.SLASH))
331 buf.insert(split,p2);
332 else
333 {
334 buf.insert(split,'/');
335 buf.insert(split+1,p2);
336 }
337 }
338
339 return buf.toString();
340 }
341
342
343
344
345
346 public static String parentPath(String p)
347 {
348 if (p==null || URIUtil.SLASH.equals(p))
349 return null;
350 int slash=p.lastIndexOf('/',p.length()-2);
351 if (slash>=0)
352 return p.substring(0,slash+1);
353 return null;
354 }
355
356
357
358
359
360 public static String stripPath(String path)
361 {
362 if (path==null)
363 return null;
364 int semi=path.indexOf(';');
365 if (semi<0)
366 return path;
367 return path.substring(0,semi);
368 }
369
370
371
372
373
374
375
376
377 public static String canonicalPath(String path)
378 {
379 if (path==null || path.length()==0)
380 return path;
381
382 int end=path.length();
383 int queryIdx=path.indexOf('?');
384 int start = path.lastIndexOf('/', (queryIdx > 0 ? queryIdx : end));
385
386 search:
387 while (end>0)
388 {
389 switch(end-start)
390 {
391 case 2:
392 if (path.charAt(start+1)!='.')
393 break;
394 break search;
395 case 3:
396 if (path.charAt(start+1)!='.' || path.charAt(start+2)!='.')
397 break;
398 break search;
399 }
400
401 end=start;
402 start=path.lastIndexOf('/',end-1);
403 }
404
405
406 if (start>=end)
407 return path;
408
409 StringBuffer buf = new StringBuffer(path);
410 int delStart=-1;
411 int delEnd=-1;
412 int skip=0;
413
414 while (end>0)
415 {
416 switch(end-start)
417 {
418 case 2:
419 if (buf.charAt(start+1)!='.')
420 {
421 if (skip>0 && --skip==0)
422 {
423 delStart=start>=0?start:0;
424 if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.')
425 delStart++;
426 }
427 break;
428 }
429
430 if(start<0 && buf.length()>2 && buf.charAt(1)=='/' && buf.charAt(2)=='/')
431 break;
432
433 if(delEnd<0)
434 delEnd=end;
435 delStart=start;
436 if (delStart<0 || delStart==0&&buf.charAt(delStart)=='/')
437 {
438 delStart++;
439 if (delEnd<buf.length() && buf.charAt(delEnd)=='/')
440 delEnd++;
441 break;
442 }
443 if (end==buf.length())
444 delStart++;
445
446 end=start--;
447 while (start>=0 && buf.charAt(start)!='/')
448 start--;
449 continue;
450
451 case 3:
452 if (buf.charAt(start+1)!='.' || buf.charAt(start+2)!='.')
453 {
454 if (skip>0 && --skip==0)
455 { delStart=start>=0?start:0;
456 if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.')
457 delStart++;
458 }
459 break;
460 }
461
462 delStart=start;
463 if (delEnd<0)
464 delEnd=end;
465
466 skip++;
467 end=start--;
468 while (start>=0 && buf.charAt(start)!='/')
469 start--;
470 continue;
471
472 default:
473 if (skip>0 && --skip==0)
474 {
475 delStart=start>=0?start:0;
476 if(delEnd==buf.length() && buf.charAt(delEnd-1)=='.')
477 delStart++;
478 }
479 }
480
481
482 if (skip<=0 && delStart>=0 && delStart>=0)
483 {
484 buf.delete(delStart,delEnd);
485 delStart=delEnd=-1;
486 if (skip>0)
487 delEnd=end;
488 }
489
490 end=start--;
491 while (start>=0 && buf.charAt(start)!='/')
492 start--;
493 }
494
495
496 if (skip>0)
497 return null;
498
499
500 if (delEnd>=0)
501 buf.delete(delStart,delEnd);
502
503 return buf.toString();
504 }
505
506
507
508
509
510
511
512 public static String compactPath(String path)
513 {
514 if (path==null || path.length()==0)
515 return path;
516
517 int state=0;
518 int end=path.length();
519 int i=0;
520
521 loop:
522 while (i<end)
523 {
524 char c=path.charAt(i);
525 switch(c)
526 {
527 case '?':
528 return path;
529 case '/':
530 state++;
531 if (state==2)
532 break loop;
533 break;
534 default:
535 state=0;
536 }
537 i++;
538 }
539
540 if (state<2)
541 return path;
542
543 StringBuffer buf = new StringBuffer(path.length());
544 char[] chars = path.toCharArray();
545 buf.append(chars,0,i);
546
547 loop2:
548 while (i<end)
549 {
550 char c=path.charAt(i);
551 switch(c)
552 {
553 case '?':
554 buf.append(chars,i,end-i);
555 break loop2;
556 case '/':
557 if (state++==0)
558 buf.append(c);
559 break;
560 default:
561 state=0;
562 buf.append(c);
563 }
564 i++;
565 }
566
567 return buf.toString();
568 }
569
570
571
572
573
574
575 public static boolean hasScheme(String uri)
576 {
577 for (int i=0;i<uri.length();i++)
578 {
579 char c=uri.charAt(i);
580 if (c==':')
581 return true;
582 if (!(c>='a'&&c<='z' ||
583 c>='A'&&c<='Z' ||
584 (i>0 &&(c>='0'&&c<='9' ||
585 c=='.' ||
586 c=='+' ||
587 c=='-'))
588 ))
589 break;
590 }
591 return false;
592 }
593
594 }
595
596
597