Chromium Code Reviews| 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 |