Make dirname and basename ignore trailing separators and double slashes.
Separators are still preserved for other path methods.
Review URL: https://codereview.chromium.org//11638021
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@16397 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/lib/path.dart b/lib/path.dart
index 388c084..99eee7b 100644
--- a/lib/path.dart
+++ b/lib/path.dart
@@ -28,12 +28,20 @@
///
/// path.basename('path/to/foo.dart'); // -> 'foo.dart'
/// path.basename('path/to'); // -> 'to'
+///
+/// Trailing separators are ignored.
+///
+/// builder.dirname('path/to/'); // -> 'to'
String basename(String path) => _builder.basename(path);
/// Gets the part of [path] after the last separator, and without any trailing
/// file extension.
///
/// path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo'
+///
+/// Trailing separators are ignored.
+///
+/// builder.dirname('path/to/foo.dart/'); // -> 'foo'
String basenameWithoutExtension(String path) =>
_builder.basenameWithoutExtension(path);
@@ -41,6 +49,10 @@
///
/// path.dirname('path/to/foo.dart'); // -> 'path/to'
/// path.dirname('path/to'); // -> 'to'
+///
+/// Trailing separators are ignored.
+///
+/// builder.dirname('path/to/'); // -> 'path'
String dirname(String path) => _builder.dirname(path);
/// Gets the file extension of [path]: the portion of [basename] from the last
@@ -190,30 +202,41 @@
///
/// builder.basename('path/to/foo.dart'); // -> 'foo.dart'
/// builder.basename('path/to'); // -> 'to'
+ ///
+ /// Trailing separators are ignored.
+ ///
+ /// builder.dirname('path/to/'); // -> 'to'
String basename(String path) => _parse(path).basename;
/// Gets the part of [path] after the last separator on the builder's
/// platform, and without any trailing file extension.
///
/// builder.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo'
+ ///
+ /// Trailing separators are ignored.
+ ///
+ /// builder.dirname('path/to/foo.dart/'); // -> 'foo'
String basenameWithoutExtension(String path) =>
- _parse(path).basenameWithoutExtension;
+ _parse(path).basenameWithoutExtension;
/// Gets the part of [path] before the last separator.
///
/// builder.dirname('path/to/foo.dart'); // -> 'path/to'
- /// builder.dirname('path/to'); // -> 'to'
+ /// builder.dirname('path/to'); // -> 'path'
+ ///
+ /// Trailing separators are ignored.
+ ///
+ /// builder.dirname('path/to/'); // -> 'path'
String dirname(String path) {
var parsed = _parse(path);
+ parsed.removeTrailingSeparators();
if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root;
- if (!parsed.hasTrailingSeparator) {
- if (parsed.parts.length == 1) {
- return parsed.root == null ? '.' : parsed.root;
- }
- parsed.parts.removeLast();
- parsed.separators.removeLast();
+ if (parsed.parts.length == 1) {
+ return parsed.root == null ? '.' : parsed.root;
}
- parsed.separators[parsed.separators.length - 1] = '';
+ parsed.parts.removeLast();
+ parsed.separators.removeLast();
+ parsed.removeTrailingSeparators();
return parsed.toString();
}
@@ -440,7 +463,7 @@
// Make it relative.
pathParsed.root = '';
- pathParsed.removeTrailingSeparator();
+ pathParsed.removeTrailingSeparators();
return pathParsed.toString();
}
@@ -450,10 +473,12 @@
/// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
String withoutExtension(String path) {
var parsed = _parse(path);
- if (parsed.hasTrailingSeparator) return parsed.toString();
- if (!parsed.parts.isEmpty) {
- parsed.parts[parsed.parts.length - 1] = parsed.basenameWithoutExtension;
+ for (var i = parsed.parts.length - 1; i >= 0; i--) {
+ if (!parsed.parts[i].isEmpty) {
+ parsed.parts[i] = parsed.basenameWithoutExtension;
+ break;
+ }
}
return parsed.toString();
@@ -555,29 +580,31 @@
/// The file extension of the last part, or "" if it doesn't have one.
String get extension => _splitExtension()[1];
- /// `true` if the path ends with a trailing separator.
- bool get hasTrailingSeparator {
- if (separators.length == 0) return false;
- return separators[separators.length - 1] != '';
- }
-
/// `true` if this is an absolute path.
bool get isAbsolute => root != null;
_ParsedPath(this.style, this.root, this.parts, this.separators);
String get basename {
- if (parts.length == 0) return extension;
- if (hasTrailingSeparator) return '';
- return parts.last;
+ var copy = this.clone();
+ copy.removeTrailingSeparators();
+ if (copy.parts.isEmpty) return root == null ? '' : root;
+ return copy.parts.last;
}
- String get basenameWithoutExtension => _splitExtension()[0];
+ String get basenameWithoutExtension {
+ var copy = this.clone();
+ copy.removeTrailingSeparators();
+ if (copy.parts.isEmpty) return root == null ? '' : root;
+ return copy._splitExtension()[0];
+ }
- void removeTrailingSeparator() {
- if (separators.length > 0) {
- separators[separators.length - 1] = '';
+ void removeTrailingSeparators() {
+ while (!parts.isEmpty && parts.last == '') {
+ parts.removeLast();
+ separators.removeLast();
}
+ if (separators.length > 0) separators[separators.length - 1] = '';
}
void normalize() {
@@ -617,7 +644,7 @@
parts = newParts;
separators = newSeparators;
- removeTrailingSeparator();
+ removeTrailingSeparators();
}
String toString() {
@@ -636,7 +663,6 @@
/// or "" if it has none.
List<String> _splitExtension() {
if (parts.isEmpty) return ['', ''];
- if (hasTrailingSeparator) return ['', ''];
var file = parts.last;
if (file == '..') return ['..', ''];
@@ -649,4 +675,7 @@
return [file.substring(0, lastDot), file.substring(lastDot)];
}
+
+ _ParsedPath clone() => new _ParsedPath(
+ style, root, new List.from(parts), new List.from(separators));
}
diff --git a/test/path_posix_test.dart b/test/path_posix_test.dart
index f929c2a..b917e93 100644
--- a/test/path_posix_test.dart
+++ b/test/path_posix_test.dart
@@ -48,14 +48,18 @@
expect(builder.dirname('a/b'), 'a');
expect(builder.dirname('a/b/c'), 'a/b');
expect(builder.dirname('a/b.c'), 'a');
- expect(builder.dirname('a/'), 'a');
+ expect(builder.dirname('a/'), '.');
expect(builder.dirname('a/.'), 'a');
expect(builder.dirname(r'a\b/c'), r'a\b');
expect(builder.dirname('/a'), '/');
+ expect(builder.dirname('///a'), '/');
expect(builder.dirname('/'), '/');
- expect(builder.dirname('a/b/'), 'a/b');
+ expect(builder.dirname('///'), '/');
+ expect(builder.dirname('a/b/'), 'a');
expect(builder.dirname(r'a/b\c'), 'a');
- expect(builder.dirname('a//'), 'a/');
+ expect(builder.dirname('a//'), '.');
+ expect(builder.dirname('a/b//'), 'a');
+ expect(builder.dirname('a//b'), 'a');
});
test('basename', () {
@@ -64,15 +68,16 @@
expect(builder.basename('a/b'), 'b');
expect(builder.basename('a/b/c'), 'c');
expect(builder.basename('a/b.c'), 'b.c');
- expect(builder.basename('a/'), '');
+ expect(builder.basename('a/'), 'a');
expect(builder.basename('a/.'), '.');
expect(builder.basename(r'a\b/c'), 'c');
expect(builder.basename('/a'), 'a');
- // TODO(nweiz): this should actually return '/'
- expect(builder.basename('/'), '');
- expect(builder.basename('a/b/'), '');
+ expect(builder.basename('/'), '/');
+ expect(builder.basename('a/b/'), 'b');
expect(builder.basename(r'a/b\c'), r'b\c');
- expect(builder.basename('a//'), '');
+ expect(builder.basename('a//'), 'a');
+ expect(builder.basename('a/b//'), 'b');
+ expect(builder.basename('a//b'), 'b');
});
test('basenameWithoutExtension', () {
@@ -81,11 +86,16 @@
expect(builder.basenameWithoutExtension('a/b'), 'b');
expect(builder.basenameWithoutExtension('a/b/c'), 'c');
expect(builder.basenameWithoutExtension('a/b.c'), 'b');
- expect(builder.basenameWithoutExtension('a/'), '');
+ expect(builder.basenameWithoutExtension('a/'), 'a');
expect(builder.basenameWithoutExtension('a/.'), '.');
expect(builder.basenameWithoutExtension(r'a/b\c'), r'b\c');
expect(builder.basenameWithoutExtension('a/.bashrc'), '.bashrc');
expect(builder.basenameWithoutExtension('a/b/c.d.e'), 'c.d');
+ expect(builder.basenameWithoutExtension('a//'), 'a');
+ expect(builder.basenameWithoutExtension('a/b//'), 'b');
+ expect(builder.basenameWithoutExtension('a//b'), 'b');
+ expect(builder.basenameWithoutExtension('a/b.c/'), 'b');
+ expect(builder.basenameWithoutExtension('a/b.c//'), 'b');
});
test('isAbsolute', () {
@@ -358,5 +368,7 @@
expect(builder.withoutExtension(r'a.b\c'), r'a');
expect(builder.withoutExtension(r'a/b\c'), r'a/b\c');
expect(builder.withoutExtension(r'a/b\c.d'), r'a/b\c');
+ expect(builder.withoutExtension('a/b.c/'), 'a/b/');
+ expect(builder.withoutExtension('a/b.c//'), 'a/b//');
});
}
diff --git a/test/path_windows_test.dart b/test/path_windows_test.dart
index a94d126..744b7ab 100644
--- a/test/path_windows_test.dart
+++ b/test/path_windows_test.dart
@@ -54,15 +54,19 @@
expect(builder.dirname(r'a\b'), 'a');
expect(builder.dirname(r'a\b\c'), r'a\b');
expect(builder.dirname(r'a\b.c'), 'a');
- expect(builder.dirname(r'a\'), 'a');
- expect(builder.dirname('a/'), 'a');
+ expect(builder.dirname(r'a\'), '.');
+ expect(builder.dirname('a/'), '.');
expect(builder.dirname(r'a\.'), 'a');
expect(builder.dirname(r'a\b/c'), r'a\b');
expect(builder.dirname(r'C:\a'), r'C:\');
- expect(builder.dirname('C:\\'), r'C:\');
- expect(builder.dirname(r'a\b\'), r'a\b');
+ expect(builder.dirname(r'C:\\\a'), r'C:\');
+ expect(builder.dirname(r'C:\'), r'C:\');
+ expect(builder.dirname(r'C:\\\'), r'C:\');
+ expect(builder.dirname(r'a\b\'), r'a');
expect(builder.dirname(r'a/b\c'), 'a/b');
- expect(builder.dirname(r'a\\'), r'a\');
+ expect(builder.dirname(r'a\\'), r'.');
+ expect(builder.dirname(r'a\b\\'), 'a');
+ expect(builder.dirname(r'a\\b'), 'a');
});
test('basename', () {
@@ -71,16 +75,17 @@
expect(builder.basename(r'a\b'), 'b');
expect(builder.basename(r'a\b\c'), 'c');
expect(builder.basename(r'a\b.c'), 'b.c');
- expect(builder.basename(r'a\'), '');
- expect(builder.basename(r'a/'), '');
+ expect(builder.basename(r'a\'), 'a');
+ expect(builder.basename(r'a/'), 'a');
expect(builder.basename(r'a\.'), '.');
expect(builder.basename(r'a\b/c'), r'c');
expect(builder.basename(r'C:\a'), 'a');
- // TODO(nweiz): this should actually return 'C:\'
- expect(builder.basename(r'C:\'), '');
- expect(builder.basename(r'a\b\'), '');
+ expect(builder.basename(r'C:\'), r'C:\');
+ expect(builder.basename(r'a\b\'), 'b');
expect(builder.basename(r'a/b\c'), 'c');
- expect(builder.basename(r'a\\'), '');
+ expect(builder.basename(r'a\\'), 'a');
+ expect(builder.basename(r'a\b\\'), 'b');
+ expect(builder.basename(r'a\\b'), 'b');
});
test('basenameWithoutExtension', () {
@@ -89,11 +94,16 @@
expect(builder.basenameWithoutExtension(r'a\b'), 'b');
expect(builder.basenameWithoutExtension(r'a\b\c'), 'c');
expect(builder.basenameWithoutExtension(r'a\b.c'), 'b');
- expect(builder.basenameWithoutExtension(r'a\'), '');
+ expect(builder.basenameWithoutExtension(r'a\'), 'a');
expect(builder.basenameWithoutExtension(r'a\.'), '.');
expect(builder.basenameWithoutExtension(r'a\b/c'), r'c');
expect(builder.basenameWithoutExtension(r'a\.bashrc'), '.bashrc');
expect(builder.basenameWithoutExtension(r'a\b\c.d.e'), 'c.d');
+ expect(builder.basenameWithoutExtension(r'a\\'), 'a');
+ expect(builder.basenameWithoutExtension(r'a\b\\'), 'b');
+ expect(builder.basenameWithoutExtension(r'a\\b'), 'b');
+ expect(builder.basenameWithoutExtension(r'a\b.c\'), 'b');
+ expect(builder.basenameWithoutExtension(r'a\b.c\\'), 'b');
});
test('isAbsolute', () {
@@ -392,5 +402,6 @@
expect(builder.withoutExtension(r'a\b/c'), r'a\b/c');
expect(builder.withoutExtension(r'a\b/c.d'), r'a\b/c');
expect(builder.withoutExtension(r'a.b/c'), r'a.b/c');
+ expect(builder.withoutExtension(r'a\b.c\'), r'a\b\');
});
}