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 var before = path; |
| 45 |
| 46 // Remove the root prefix, if any. |
| 47 var root = style.getRoot(path); |
| 48 var isRootRelative = style.getRelativeRoot(path) != null; |
| 49 if (root != null) path = path.substring(root.length); |
| 50 |
| 51 // Split the parts on path separators. |
| 52 var parts = []; |
| 53 var separators = []; |
| 54 |
| 55 var start = 0; |
| 56 |
| 57 if (path.isNotEmpty && style.isSeparator(path.codeUnitAt(0))) { |
| 58 separators.add(path[0]); |
| 59 start = 1; |
| 60 } else { |
| 61 separators.add(''); |
| 62 } |
| 63 |
| 64 for (var i = start; i < path.length; i++) { |
| 65 if (style.isSeparator(path.codeUnitAt(i))) { |
| 66 parts.add(path.substring(start, i)); |
| 67 separators.add(path[i]); |
| 68 start = i + 1; |
| 69 } |
| 70 } |
| 71 |
| 72 // Add the final part, if any. |
| 73 if (start < path.length) { |
| 74 parts.add(path.substring(start)); |
| 75 separators.add(''); |
| 76 } |
| 77 |
| 78 return new ParsedPath._(style, root, isRootRelative, parts, separators); |
| 79 } |
| 80 |
| 81 ParsedPath._(this.style, this.root, this.isRootRelative, this.parts, |
| 82 this.separators); |
| 83 |
| 84 String get basename { |
| 85 var copy = this.clone(); |
| 86 copy.removeTrailingSeparators(); |
| 87 if (copy.parts.isEmpty) return root == null ? '' : root; |
| 88 return copy.parts.last; |
| 89 } |
| 90 |
| 91 String get basenameWithoutExtension => _splitExtension()[0]; |
| 92 |
| 93 bool get hasTrailingSeparator => |
| 94 !parts.isEmpty && (parts.last == '' || separators.last != ''); |
| 95 |
| 96 void removeTrailingSeparators() { |
| 97 while (!parts.isEmpty && parts.last == '') { |
| 98 parts.removeLast(); |
| 99 separators.removeLast(); |
| 100 } |
| 101 if (separators.length > 0) separators[separators.length - 1] = ''; |
| 102 } |
| 103 |
| 104 void normalize() { |
| 105 // Handle '.', '..', and empty parts. |
| 106 var leadingDoubles = 0; |
| 107 var newParts = []; |
| 108 for (var part in parts) { |
| 109 if (part == '.' || part == '') { |
| 110 // Do nothing. Ignore it. |
| 111 } else if (part == '..') { |
| 112 // Pop the last part off. |
| 113 if (newParts.length > 0) { |
| 114 newParts.removeLast(); |
| 115 } else { |
| 116 // Backed out past the beginning, so preserve the "..". |
| 117 leadingDoubles++; |
| 118 } |
| 119 } else { |
| 120 newParts.add(part); |
| 121 } |
| 122 } |
| 123 |
| 124 // A relative path can back out from the start directory. |
| 125 if (!isAbsolute) { |
| 126 newParts.insertAll(0, new List.filled(leadingDoubles, '..')); |
| 127 } |
| 128 |
| 129 // If we collapsed down to nothing, do ".". |
| 130 if (newParts.length == 0 && !isAbsolute) { |
| 131 newParts.add('.'); |
| 132 } |
| 133 |
| 134 // Canonicalize separators. |
| 135 var newSeparators = new List.generate( |
| 136 newParts.length, (_) => style.separator, growable: true); |
| 137 newSeparators.insert(0, |
| 138 isAbsolute && newParts.length > 0 && style.needsSeparator(root) ? |
| 139 style.separator : ''); |
| 140 |
| 141 parts = newParts; |
| 142 separators = newSeparators; |
| 143 |
| 144 // Normalize the Windows root if needed. |
| 145 if (root != null && style == Style.windows) { |
| 146 root = root.replaceAll('/', '\\'); |
| 147 } |
| 148 removeTrailingSeparators(); |
| 149 } |
| 150 |
| 151 String toString() { |
| 152 var builder = new StringBuffer(); |
| 153 if (root != null) builder.write(root); |
| 154 for (var i = 0; i < parts.length; i++) { |
| 155 builder.write(separators[i]); |
| 156 builder.write(parts[i]); |
| 157 } |
| 158 builder.write(separators.last); |
| 159 |
| 160 return builder.toString(); |
| 161 } |
| 162 |
| 163 /// Splits the last non-empty part of the path into a `[basename, extension`] |
| 164 /// pair. |
| 165 /// |
| 166 /// Returns a two-element list. The first is the name of the file without any |
| 167 /// extension. The second is the extension or "" if it has none. |
| 168 List<String> _splitExtension() { |
| 169 var file = parts.lastWhere((p) => p != '', orElse: () => null); |
| 170 |
| 171 if (file == null) return ['', '']; |
| 172 if (file == '..') return ['..', '']; |
| 173 |
| 174 var lastDot = file.lastIndexOf('.'); |
| 175 |
| 176 // If there is no dot, or it's the first character, like '.bashrc', it |
| 177 // doesn't count. |
| 178 if (lastDot <= 0) return [file, '']; |
| 179 |
| 180 return [file.substring(0, lastDot), file.substring(lastDot)]; |
| 181 } |
| 182 |
| 183 ParsedPath clone() => new ParsedPath._( |
| 184 style, root, isRootRelative, |
| 185 new List.from(parts), new List.from(separators)); |
| 186 } |
| 187 |
OLD | NEW |