| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 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. |
| 4 |
| 5 library analyzer.src.util.absolute_path; |
| 6 |
| 7 /// The class for manipulating absolute, normalized paths. |
| 8 class AbsolutePathContext { |
| 9 static const int _COLON = 0x3A; |
| 10 static const int _LOWER_A = 0x61; |
| 11 static const int _LOWER_Z = 0x7A; |
| 12 static const int _UPPER_A = 0x41; |
| 13 static const int _UPPER_Z = 0x5A; |
| 14 |
| 15 final bool _isWindows; |
| 16 String separator; |
| 17 int _separatorChar; |
| 18 String _singlePeriodComponent; |
| 19 String _doublePeriodComponent; |
| 20 String _singlePeriodEnding; |
| 21 String _doublePeriodEnding; |
| 22 |
| 23 AbsolutePathContext(this._isWindows) { |
| 24 separator = _isWindows ? r'\' : '/'; |
| 25 _separatorChar = separator.codeUnitAt(0); |
| 26 _singlePeriodComponent = separator + '.' + separator; |
| 27 _doublePeriodComponent = separator + '..' + separator; |
| 28 _singlePeriodEnding = separator + '.'; |
| 29 _doublePeriodEnding = separator + '..'; |
| 30 } |
| 31 |
| 32 /// Append the given relative [suffix] to the given absolute [parent]. |
| 33 /// |
| 34 /// context.append('/path/to', 'foo'); // -> '/path/to/foo' |
| 35 /// |
| 36 /// The given [suffix] cannot be an absolute path or use `..`. |
| 37 String append(String parent, String suffix) { |
| 38 return '$parent$separator$suffix'; |
| 39 } |
| 40 |
| 41 /// Return the part of the absolute [path] after the last separator on the |
| 42 /// context's platform. |
| 43 /// |
| 44 /// context.basename('/path/to/foo.dart'); // -> 'foo.dart' |
| 45 /// context.basename('/path/to'); // -> 'to' |
| 46 /// context.basename('/path'); // -> 'path' |
| 47 /// context.basename('/'); // -> '' |
| 48 String basename(String path) { |
| 49 int index = path.lastIndexOf(separator); |
| 50 return path.substring(index + 1); |
| 51 } |
| 52 |
| 53 /// Return the part of the absolute [path] before the last separator. |
| 54 /// |
| 55 /// context.dirname('/path/to/foo.dart'); // -> '/path/to' |
| 56 /// context.dirname('/path/to'); // -> '/path' |
| 57 /// context.dirname(r'/path'); // -> '/' |
| 58 /// context.dirname(r'/'); // -> '/' |
| 59 /// context.dirname(r'C:\path'); // -> 'C:\' |
| 60 /// context.dirname(r'C:\'); // -> 'C:\' |
| 61 String dirname(String path) { |
| 62 int firstIndex = path.indexOf(separator); |
| 63 int lastIndex = path.lastIndexOf(separator); |
| 64 return lastIndex == firstIndex |
| 65 ? path.substring(0, firstIndex + 1) |
| 66 : path.substring(0, lastIndex); |
| 67 } |
| 68 |
| 69 /// Return `true` if the given [path] is valid. |
| 70 /// |
| 71 /// context.isNormalized('/foo/bar'); // -> true |
| 72 /// context.isNormalized('/foo/bar/../baz'); // -> false |
| 73 bool isValid(String path) { |
| 74 return _isAbsolute(path) && _isNormalized(path); |
| 75 } |
| 76 |
| 77 /// Return `true` if [child] is a path beneath [parent], and `false` |
| 78 /// otherwise. Both the [child] and [parent] paths must be absolute paths. |
| 79 /// |
| 80 /// context.isWithin('/root/path', '/root/path/a'); // -> true |
| 81 /// context.isWithin('/root/path', '/root/other'); // -> false |
| 82 /// context.isWithin('/root/path', '/root/path'); // -> false |
| 83 bool isWithin(String parent, String child) { |
| 84 int parentLength = parent.length; |
| 85 int childLength = child.length; |
| 86 if (parentLength >= childLength) { |
| 87 return false; |
| 88 } |
| 89 if (child.codeUnitAt(parentLength) != _separatorChar) { |
| 90 return false; |
| 91 } |
| 92 return _startsWithUnsafe(child, parent); |
| 93 } |
| 94 |
| 95 /// Split [path] into its components using [separator]. |
| 96 /// |
| 97 /// context.split('/path/to/foo'); // -> ['', 'path', 'to', 'foo'] |
| 98 List<String> split(String path) { |
| 99 return path.split(separator); |
| 100 } |
| 101 |
| 102 /// If the given [child] is within the given [parent], then return the |
| 103 /// relative path from [parent] to [child]. Otherwise return `null`. Both |
| 104 /// the [child] and [parent] paths must be absolute paths. |
| 105 /// |
| 106 /// context.relative('/root/path', '/root/path/a/b.dart'); // -> 'a/b.dart
' |
| 107 /// context.relative('/root/path', '/root/other.dart'); // -> null |
| 108 String suffix(String parent, String child) { |
| 109 String parentPrefix = parent + separator; |
| 110 if (child.startsWith(parentPrefix)) { |
| 111 return child.substring(parentPrefix.length); |
| 112 } |
| 113 return null; |
| 114 } |
| 115 |
| 116 /// Return `true` if the given [path] is absolute. |
| 117 /// |
| 118 /// _isAbsolute('/foo/bar'); // -> true |
| 119 /// _isAbsolute('/'); // -> true |
| 120 /// _isAbsolute('foo/bar'); // -> false |
| 121 /// _isAbsolute('C:\foo\bar'); // -> true |
| 122 /// _isAbsolute('C:\'); // -> true |
| 123 /// _isAbsolute('foo\bar'); // -> false |
| 124 bool _isAbsolute(String path) { |
| 125 if (_isWindows) { |
| 126 return path.length >= 3 && |
| 127 _isAlphabetic(path.codeUnitAt(0)) && |
| 128 path.codeUnitAt(1) == _COLON && |
| 129 path.codeUnitAt(2) == _separatorChar; |
| 130 } else { |
| 131 return path.isNotEmpty && path.codeUnitAt(0) == _separatorChar; |
| 132 } |
| 133 } |
| 134 |
| 135 /// Return `true` if the given absolute [path] is normalized. |
| 136 /// |
| 137 /// _isNormalized('/foo/bar'); // -> true |
| 138 /// _isNormalized('/foo/..bar'); // -> true |
| 139 /// _isNormalized('/foo/bar..'); // -> true |
| 140 /// _isNormalized('/'); // -> true |
| 141 /// _isNormalized('/foo/bar/../baz'); // -> false |
| 142 /// _isNormalized('/foo/bar/..'); // -> false |
| 143 bool _isNormalized(String path) { |
| 144 return !path.contains(_singlePeriodComponent) && |
| 145 !path.contains(_doublePeriodComponent) && |
| 146 !path.endsWith(_singlePeriodEnding) && |
| 147 !path.endsWith(_doublePeriodEnding); |
| 148 } |
| 149 |
| 150 /// Returns whether [char] is the code for an ASCII letter (uppercase or |
| 151 /// lowercase). |
| 152 static bool _isAlphabetic(int char) { |
| 153 return char >= _UPPER_A && char <= _UPPER_Z || |
| 154 char >= _LOWER_A && char <= _LOWER_Z; |
| 155 } |
| 156 |
| 157 /// Return `true` if [str] starts with the given [prefix]. |
| 158 /// |
| 159 /// The check is done from the end of [prefix], because absolute paths |
| 160 /// usually have the same prefix, e.g. the user's home directory. |
| 161 static bool _startsWithUnsafe(String str, String prefix) { |
| 162 int len = prefix.length; |
| 163 for (int i = len - 1; i >= 0; i--) { |
| 164 if (str.codeUnitAt(i) != prefix.codeUnitAt(i)) { |
| 165 return false; |
| 166 } |
| 167 } |
| 168 return true; |
| 169 } |
| 170 } |
| OLD | NEW |