OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013, 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 path.parsed_path; |
| 6 |
| 7 import 'internal_style.dart'; |
| 8 import 'style.dart'; |
| 9 |
| 10 class ParsedPath { |
| 11 /// The [InternalStyle] that was used to parse this path. |
| 12 InternalStyle style; |
| 13 |
| 14 /// The absolute root portion of the path, or `null` if the path is relative. |
| 15 /// On POSIX systems, this will be `null` or "/". On Windows, it can be |
| 16 /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive |
| 17 /// letters. |
| 18 String root; |
| 19 |
| 20 /// Whether this path is root-relative. |
| 21 /// |
| 22 /// See [Context.isRootRelative]. |
| 23 bool isRootRelative; |
| 24 |
| 25 /// The path-separated parts of the path. All but the last will be |
| 26 /// directories. |
| 27 List<String> parts; |
| 28 |
| 29 /// The path separators preceding each part. |
| 30 /// |
| 31 /// The first one will be an empty string unless the root requires a separator |
| 32 /// between it and the path. The last one will be an empty string unless the |
| 33 /// path ends with a trailing separator. |
| 34 List<String> separators; |
| 35 |
| 36 /// The file extension of the last non-empty part, or "" if it doesn't have |
| 37 /// one. |
| 38 String get extension => _splitExtension()[1]; |
| 39 |
| 40 /// `true` if this is an absolute path. |
| 41 bool get isAbsolute => root != null; |
| 42 |
| 43 factory ParsedPath.parse(String path, InternalStyle style) { |
| 44 // Remove the root prefix, if any. |
| 45 var root = style.getRoot(path); |
| 46 var isRootRelative = style.isRootRelative(path); |
| 47 if (root != null) path = path.substring(root.length); |
| 48 |
| 49 // Split the parts on path separators. |
| 50 var parts = <String>[]; |
| 51 var separators = <String>[]; |
| 52 |
| 53 var start = 0; |
| 54 |
| 55 if (path.isNotEmpty && style.isSeparator(path.codeUnitAt(0))) { |
| 56 separators.add(path[0]); |
| 57 start = 1; |
| 58 } else { |
| 59 separators.add(''); |
| 60 } |
| 61 |
| 62 for (var i = start; i < path.length; i++) { |
| 63 if (style.isSeparator(path.codeUnitAt(i))) { |
| 64 parts.add(path.substring(start, i)); |
| 65 separators.add(path[i]); |
| 66 start = i + 1; |
| 67 } |
| 68 } |
| 69 |
| 70 // Add the final part, if any. |
| 71 if (start < path.length) { |
| 72 parts.add(path.substring(start)); |
| 73 separators.add(''); |
| 74 } |
| 75 |
| 76 return new ParsedPath._(style, root, isRootRelative, parts, separators); |
| 77 } |
| 78 |
| 79 ParsedPath._( |
| 80 this.style, this.root, this.isRootRelative, this.parts, this.separators); |
| 81 |
| 82 String get basename { |
| 83 var copy = this.clone(); |
| 84 copy.removeTrailingSeparators(); |
| 85 if (copy.parts.isEmpty) return root == null ? '' : root; |
| 86 return copy.parts.last; |
| 87 } |
| 88 |
| 89 String get basenameWithoutExtension => _splitExtension()[0]; |
| 90 |
| 91 bool get hasTrailingSeparator => |
| 92 !parts.isEmpty && (parts.last == '' || separators.last != ''); |
| 93 |
| 94 void removeTrailingSeparators() { |
| 95 while (!parts.isEmpty && parts.last == '') { |
| 96 parts.removeLast(); |
| 97 separators.removeLast(); |
| 98 } |
| 99 if (separators.length > 0) separators[separators.length - 1] = ''; |
| 100 } |
| 101 |
| 102 void normalize() { |
| 103 // Handle '.', '..', and empty parts. |
| 104 var leadingDoubles = 0; |
| 105 var newParts = <String>[]; |
| 106 for (var part in parts) { |
| 107 if (part == '.' || part == '') { |
| 108 // Do nothing. Ignore it. |
| 109 } else if (part == '..') { |
| 110 // Pop the last part off. |
| 111 if (newParts.length > 0) { |
| 112 newParts.removeLast(); |
| 113 } else { |
| 114 // Backed out past the beginning, so preserve the "..". |
| 115 leadingDoubles++; |
| 116 } |
| 117 } else { |
| 118 newParts.add(part); |
| 119 } |
| 120 } |
| 121 |
| 122 // A relative path can back out from the start directory. |
| 123 if (!isAbsolute) { |
| 124 newParts.insertAll(0, new List.filled(leadingDoubles, '..')); |
| 125 } |
| 126 |
| 127 // If we collapsed down to nothing, do ".". |
| 128 if (newParts.length == 0 && !isAbsolute) { |
| 129 newParts.add('.'); |
| 130 } |
| 131 |
| 132 // Canonicalize separators. |
| 133 var newSeparators = new List<String>.generate( |
| 134 newParts.length, (_) => style.separator, growable: true); |
| 135 newSeparators.insert(0, isAbsolute && |
| 136 newParts.length > 0 && |
| 137 style.needsSeparator(root) ? style.separator : ''); |
| 138 |
| 139 parts = newParts; |
| 140 separators = newSeparators; |
| 141 |
| 142 // Normalize the Windows root if needed. |
| 143 if (root != null && style == Style.windows) { |
| 144 root = root.replaceAll('/', '\\'); |
| 145 } |
| 146 removeTrailingSeparators(); |
| 147 } |
| 148 |
| 149 String toString() { |
| 150 var builder = new StringBuffer(); |
| 151 if (root != null) builder.write(root); |
| 152 for (var i = 0; i < parts.length; i++) { |
| 153 builder.write(separators[i]); |
| 154 builder.write(parts[i]); |
| 155 } |
| 156 builder.write(separators.last); |
| 157 |
| 158 return builder.toString(); |
| 159 } |
| 160 |
| 161 /// Splits the last non-empty part of the path into a `[basename, extension`] |
| 162 /// pair. |
| 163 /// |
| 164 /// Returns a two-element list. The first is the name of the file without any |
| 165 /// extension. The second is the extension or "" if it has none. |
| 166 List<String> _splitExtension() { |
| 167 var file = parts.lastWhere((p) => p != '', orElse: () => null); |
| 168 |
| 169 if (file == null) return ['', '']; |
| 170 if (file == '..') return ['..', '']; |
| 171 |
| 172 var lastDot = file.lastIndexOf('.'); |
| 173 |
| 174 // If there is no dot, or it's the first character, like '.bashrc', it |
| 175 // doesn't count. |
| 176 if (lastDot <= 0) return [file, '']; |
| 177 |
| 178 return [file.substring(0, lastDot), file.substring(lastDot)]; |
| 179 } |
| 180 |
| 181 ParsedPath clone() => new ParsedPath._(style, root, isRootRelative, |
| 182 new List.from(parts), new List.from(separators)); |
| 183 } |
OLD | NEW |