Index: pkg/analyzer/lib/src/util/absolute_path.dart |
diff --git a/pkg/analyzer/lib/src/util/absolute_path.dart b/pkg/analyzer/lib/src/util/absolute_path.dart |
index f31935d330a1841703d35bd6916b0818c771a3e3..2a90ad767570ae4964ea2981562215cd1269e645 100644 |
--- a/pkg/analyzer/lib/src/util/absolute_path.dart |
+++ b/pkg/analyzer/lib/src/util/absolute_path.dart |
@@ -4,17 +4,21 @@ |
library analyzer.src.util.absolute_path; |
-/// The class for manipulating absolute paths. |
+/// The class for manipulating absolute, normalized paths. |
class AbsolutePathContext { |
- final String separator; |
+ static const int _COLON = 0x3A; |
+ static const int _PERIOD = 0x2e; |
+ static const int _LOWER_A = 0x61; |
+ static const int _LOWER_Z = 0x7A; |
+ static const int _UPPER_A = 0x41; |
+ static const int _UPPER_Z = 0x5A; |
+ final bool _isWindows; |
+ String separator; |
int _separatorChar; |
- AbsolutePathContext(this.separator) { |
- if (separator.length != 1) { |
- throw new ArgumentError.value( |
- separator, 'separator', 'must be exactly one character long'); |
- } |
+ AbsolutePathContext(this._isWindows) { |
+ separator = _isWindows ? r'\' : '/'; |
_separatorChar = separator.codeUnitAt(0); |
} |
@@ -55,6 +59,14 @@ class AbsolutePathContext { |
: path.substring(0, lastIndex); |
} |
+ /// Return `true` if the given [path] is valid. |
+ /// |
+ /// context.isNormalized('/foo/bar'); // -> true |
+ /// context.isNormalized('/foo/bar/../baz'); // -> false |
+ bool isValid(String path) { |
+ return _isAbsolute(path) && _isNormalized(path); |
+ } |
+ |
/// Return `true` if [child] is a path beneath [parent], and `false` |
/// otherwise. Both the [child] and [parent] paths must be absolute paths. |
/// |
@@ -94,6 +106,57 @@ class AbsolutePathContext { |
return null; |
} |
+ /// Return `true` if the given [path] is absolute. |
+ /// |
+ /// _isAbsolute('/foo/bar'); // -> true |
+ /// _isAbsolute('/'); // -> true |
+ /// _isAbsolute('foo/bar'); // -> false |
+ /// _isAbsolute('C:\foo\bar'); // -> true |
+ /// _isAbsolute('C:\'); // -> true |
+ /// _isAbsolute('foo\bar'); // -> false |
+ bool _isAbsolute(String path) { |
+ if (_isWindows) { |
+ return path.length >= 3 && |
+ _isAlphabetic(path.codeUnitAt(0)) && |
+ path.codeUnitAt(1) == _COLON && |
+ path.codeUnitAt(2) == _separatorChar; |
+ } else { |
+ return path.isNotEmpty && path.codeUnitAt(0) == _separatorChar; |
+ } |
+ } |
+ |
+ |
+ /// Return `true` if the given absolute [path] is normalized. |
+ /// |
+ /// _isAbsolute('/foo/bar'); // -> true |
+ /// _isAbsolute('/foo/..bar'); // -> true |
+ /// _isAbsolute('/'); // -> true |
+ /// _isAbsolute('/foo/bar/../baz'); // -> false |
+ /// _isAbsolute('/foo/bar/..'); // -> false |
Brian Wilkerson
2015/12/09 22:58:20
Wrong method name in examples.
|
+ bool _isNormalized(String path) { |
+ int periodCount = 0; |
+ for (int c in path.codeUnits) { |
+ if (c == _PERIOD) { |
+ periodCount++; |
+ continue; |
+ } |
+ if (c == _separatorChar) { |
+ if (periodCount == 1 || periodCount == 2) { |
+ return false; |
+ } |
+ } |
+ periodCount = 0; |
+ } |
+ return periodCount != 1 && periodCount != 2; |
+ } |
+ |
+ /// Returns whether [char] is the code for an ASCII letter (uppercase or |
+ /// lowercase). |
+ static bool _isAlphabetic(int char) { |
+ return char >= _UPPER_A && char <= _UPPER_Z || |
+ char >= _LOWER_A && char <= _LOWER_Z; |
+ } |
+ |
/// Return `true` if [str] starts with the given [prefix]. |
/// |
/// The check is done from the end of [prefix], because absolute paths |