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 |
