OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /// A comprehensive, cross-platform path manipulation library. | 5 /// A comprehensive, cross-platform path manipulation library. |
6 library path; | 6 library path; |
7 | 7 |
8 import 'dart:io' as io; | 8 import 'dart:io' as io; |
9 | 9 |
10 /// An internal builder for the current OS so we can provide a straight | 10 /// An internal builder for the current OS so we can provide a straight |
(...skipping 294 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
305 pathParsed.root = ''; | 305 pathParsed.root = ''; |
306 pathParsed.removeTrailingSeparator(); | 306 pathParsed.removeTrailingSeparator(); |
307 | 307 |
308 return pathParsed.toString(); | 308 return pathParsed.toString(); |
309 } | 309 } |
310 | 310 |
311 /// Removes a trailing extension from the last part of [path]. | 311 /// Removes a trailing extension from the last part of [path]. |
312 /// | 312 /// |
313 /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' | 313 /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' |
314 String withoutExtension(String path) { | 314 String withoutExtension(String path) { |
315 var lastSeparator = path.lastIndexOf(separator); | 315 var parsed = _parse(path); |
316 var lastDot = path.lastIndexOf('.'); | 316 if (parsed.hasTrailingSeparator) return parsed.toString(); |
nweiz
2012/12/11 02:09:42
I really don't like changing behavior based on whe
Bob Nystrom
2012/12/11 02:24:59
This is how Python works. I think it would be conf
nweiz
2012/12/11 02:34:46
But not Ruby :).
Bob Nystrom
2012/12/11 02:47:01
I'm more interested here in what dirname("foo/bar/
| |
317 | 317 |
318 // Ignore '.' in anything but the last component. | 318 if (!parsed.parts.isEmpty) { |
319 if (lastSeparator != -1 && lastDot <= lastSeparator + 1) lastDot = -1; | 319 var split = parsed._splitExtension(); |
320 parsed.parts[parsed.parts.length - 1] = split[0]; | |
nweiz
2012/12/11 02:09:42
Calling a private method here seems like a code sm
Bob Nystrom
2012/12/11 02:24:59
Done, though I don't think there's anything wrong
| |
321 } | |
320 | 322 |
321 if (lastDot <= 0) return path; | 323 return parsed.toString(); |
322 return path.substring(0, lastDot); | |
323 } | 324 } |
324 | 325 |
325 _ParsedPath _parse(String path) { | 326 _ParsedPath _parse(String path) { |
326 var before = path; | 327 var before = path; |
327 | 328 |
328 // Remove the root prefix, if any. | 329 // Remove the root prefix, if any. |
329 var root = style.getRoot(path); | 330 var root = style.getRoot(path); |
330 if (root != null) path = path.substring(root.length); | 331 if (root != null) path = path.substring(root.length); |
331 | 332 |
332 // Split the parts on path separators. | 333 // Split the parts on path separators. |
333 var parts = []; | 334 var parts = []; |
334 var separators = []; | 335 var separators = []; |
335 var start = 0; | 336 var start = 0; |
336 for (var match in style.separatorPattern.allMatches(path)) { | 337 for (var match in style.separatorPattern.allMatches(path)) { |
337 parts.add(path.substring(start, match.start)); | 338 parts.add(path.substring(start, match.start)); |
338 separators.add(match[0]); | 339 separators.add(match[0]); |
339 start = match.end; | 340 start = match.end; |
340 } | 341 } |
341 | 342 |
342 // Add the final part, if any. | 343 // Add the final part, if any. |
343 if (start < path.length) { | 344 if (start < path.length) { |
344 parts.add(path.substring(start)); | 345 parts.add(path.substring(start)); |
345 separators.add(''); | 346 separators.add(''); |
346 } | 347 } |
347 | 348 |
348 // Separate out the file extension. | 349 return new _ParsedPath(style, root, parts, separators); |
349 var extension = ''; | |
350 if (parts.length > 0) { | |
351 var file = parts.last; | |
352 if (file != '..') { | |
353 var lastDot = file.lastIndexOf('.'); | |
354 | |
355 // If there is a dot (and it's not the first character, like '.bashrc'). | |
356 if (lastDot > 0) { | |
357 parts[parts.length - 1] = file.substring(0, lastDot); | |
358 extension = file.substring(lastDot); | |
359 } | |
360 } | |
361 } | |
362 | |
363 return new _ParsedPath(style, root, parts, separators, extension); | |
364 } | 350 } |
365 } | 351 } |
366 | 352 |
367 /// An enum type describing a "flavor" of path. | 353 /// An enum type describing a "flavor" of path. |
368 class Style { | 354 class Style { |
369 /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths | 355 /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths |
370 /// start with "/". Used by UNIX, Linux, Mac OS X, and others. | 356 /// start with "/". Used by UNIX, Linux, Mac OS X, and others. |
371 static final posix = new Style._('posix', '/', '/', '/'); | 357 static final posix = new Style._('posix', '/', '/', '/'); |
372 | 358 |
373 /// Windows paths use "\" (backslash) as separators. Absolute paths start with | 359 /// Windows paths use "\" (backslash) as separators. Absolute paths start with |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
412 /// The [Style] that was used to parse this path. | 398 /// The [Style] that was used to parse this path. |
413 Style style; | 399 Style style; |
414 | 400 |
415 /// The absolute root portion of the path, or `null` if the path is relative. | 401 /// The absolute root portion of the path, or `null` if the path is relative. |
416 /// On POSIX systems, this will be `null` or "/". On Windows, it can be | 402 /// On POSIX systems, this will be `null` or "/". On Windows, it can be |
417 /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive | 403 /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive |
418 /// letters. | 404 /// letters. |
419 String root; | 405 String root; |
420 | 406 |
421 /// The path-separated parts of the path. All but the last will be | 407 /// The path-separated parts of the path. All but the last will be |
422 /// directories. The last could be a directory, or could be the file name | 408 /// directories. |
423 /// without its extension. | |
424 List<String> parts; | 409 List<String> parts; |
425 | 410 |
426 /// The path separators following each part. The last one will be an empty | 411 /// The path separators following each part. The last one will be an empty |
427 /// string unless the path ends with a trailing separator. | 412 /// string unless the path ends with a trailing separator. |
428 List<String> separators; | 413 List<String> separators; |
429 | 414 |
430 /// The file's extension, or "" if it doesn't have one. | 415 /// The file extension of the last part, or "" if it doesn't have one. |
431 String extension; | 416 String get extension => _splitExtension()[1]; |
432 | 417 |
433 /// `true` if the path ends with a trailing separator. | 418 /// `true` if the path ends with a trailing separator. |
434 bool get hasTrailingSeparator { | 419 bool get hasTrailingSeparator { |
435 if (separators.length == 0) return false; | 420 if (separators.length == 0) return false; |
436 return separators[separators.length - 1] != ''; | 421 return separators[separators.length - 1] != ''; |
437 } | 422 } |
438 | 423 |
439 /// `true` if this is an absolute path. | 424 /// `true` if this is an absolute path. |
440 bool get isAbsolute => root != null; | 425 bool get isAbsolute => root != null; |
441 | 426 |
442 _ParsedPath(this.style, this.root, this.parts, this.separators, | 427 _ParsedPath(this.style, this.root, this.parts, this.separators); |
443 this.extension); | |
444 | 428 |
445 String get filename { | 429 String get filename { |
446 if (parts.length == 0) return extension; | 430 if (parts.isEmpty) return ''; |
447 if (hasTrailingSeparator) return ''; | |
448 return '${parts.last}$extension'; | |
449 } | |
450 | |
451 String get filenameWithoutExtension { | |
452 if (parts.length == 0) return ''; | |
453 if (hasTrailingSeparator) return ''; | 431 if (hasTrailingSeparator) return ''; |
454 return parts.last; | 432 return parts.last; |
455 } | 433 } |
456 | 434 |
435 String get filenameWithoutExtension => _splitExtension()[0]; | |
436 | |
457 void removeTrailingSeparator() { | 437 void removeTrailingSeparator() { |
458 if (separators.length > 0) { | 438 if (separators.length > 0) { |
459 separators[separators.length - 1] = ''; | 439 separators[separators.length - 1] = ''; |
460 } | 440 } |
461 } | 441 } |
462 | 442 |
463 void normalize() { | 443 void normalize() { |
464 // Handle '.', '..', and empty parts. | 444 // Handle '.', '..', and empty parts. |
465 var leadingDoubles = 0; | 445 var leadingDoubles = 0; |
466 var newParts = []; | 446 var newParts = []; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
498 separators = newSeparators; | 478 separators = newSeparators; |
499 | 479 |
500 removeTrailingSeparator(); | 480 removeTrailingSeparator(); |
501 } | 481 } |
502 | 482 |
503 String toString() { | 483 String toString() { |
504 var builder = new StringBuffer(); | 484 var builder = new StringBuffer(); |
505 if (root != null) builder.add(root); | 485 if (root != null) builder.add(root); |
506 for (var i = 0; i < parts.length; i++) { | 486 for (var i = 0; i < parts.length; i++) { |
507 builder.add(parts[i]); | 487 builder.add(parts[i]); |
508 if (extension != null && i == parts.length - 1) builder.add(extension); | |
509 builder.add(separators[i]); | 488 builder.add(separators[i]); |
510 } | 489 } |
511 | 490 |
512 return builder.toString(); | 491 return builder.toString(); |
513 } | 492 } |
493 | |
494 /// Splits the last part of the path into a two-element list. The first is | |
495 /// the name of the file without any extension. The second is the extension | |
496 /// or "" if it has none. | |
497 List<String> _splitExtension() { | |
498 if (parts.isEmpty) return ['', '']; | |
499 if (hasTrailingSeparator) return ['', '']; | |
500 | |
501 var file = parts.last; | |
502 if (file == '..') return ['..', '']; | |
503 | |
504 var lastDot = file.lastIndexOf('.'); | |
505 | |
506 // If there is a dot (and it's not the first character, like '.bashrc'). | |
nweiz
2012/12/11 02:09:42
This looks like it's supposed to go a couple lines
Bob Nystrom
2012/12/11 02:24:59
Reworded the comment.
| |
507 if (lastDot <= 0) return [file, '']; | |
508 | |
509 parts[parts.length - 1] = file.substring(0, lastDot); | |
nweiz
2012/12/11 02:09:42
Why is this useful? Doesn't this mean that calling
Bob Nystrom
2012/12/11 02:24:59
Eek! Copy/paste bug. Removed.
| |
510 return [file.substring(0, lastDot), file.substring(lastDot)]; | |
511 } | |
514 } | 512 } |
OLD | NEW |