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 'style.dart'; |
| 8 |
| 9 // TODO(rnystrom): Make this public? |
| 10 class ParsedPath { |
| 11 /// The [Style] that was used to parse this path. |
| 12 Style 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, Style 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 firstSeparator = style.separatorPattern.matchAsPrefix(path); |
| 56 if (firstSeparator != null) { |
| 57 separators.add(firstSeparator[0]); |
| 58 path = path.substring(firstSeparator[0].length); |
| 59 } else { |
| 60 separators.add(''); |
| 61 } |
| 62 |
| 63 var start = 0; |
| 64 for (var match in style.separatorPattern.allMatches(path)) { |
| 65 parts.add(path.substring(start, match.start)); |
| 66 separators.add(match[0]); |
| 67 start = match.end; |
| 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._(this.style, this.root, this.isRootRelative, this.parts, |
| 80 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 = []; |
| 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.generate( |
| 134 newParts.length, (_) => style.separator, growable: true); |
| 135 newSeparators.insert(0, |
| 136 isAbsolute && newParts.length > 0 && |
| 137 root.contains(style.needsSeparatorPattern) ? |
| 138 style.separator : ''); |
| 139 |
| 140 parts = newParts; |
| 141 separators = newSeparators; |
| 142 |
| 143 // Normalize the Windows root if needed. |
| 144 if (root != null && style == Style.windows) { |
| 145 root = root.replaceAll('/', '\\'); |
| 146 } |
| 147 removeTrailingSeparators(); |
| 148 } |
| 149 |
| 150 String toString() { |
| 151 var builder = new StringBuffer(); |
| 152 if (root != null) builder.write(root); |
| 153 for (var i = 0; i < parts.length; i++) { |
| 154 builder.write(separators[i]); |
| 155 builder.write(parts[i]); |
| 156 } |
| 157 builder.write(separators.last); |
| 158 |
| 159 return builder.toString(); |
| 160 } |
| 161 |
| 162 /// Splits the last non-empty part of the path into a `[basename, extension`] |
| 163 /// pair. |
| 164 /// |
| 165 /// Returns a two-element list. The first is the name of the file without any |
| 166 /// extension. The second is the extension or "" if it has none. |
| 167 List<String> _splitExtension() { |
| 168 var file = parts.lastWhere((p) => p != '', orElse: () => null); |
| 169 |
| 170 if (file == null) return ['', '']; |
| 171 if (file == '..') return ['..', '']; |
| 172 |
| 173 var lastDot = file.lastIndexOf('.'); |
| 174 |
| 175 // If there is no dot, or it's the first character, like '.bashrc', it |
| 176 // doesn't count. |
| 177 if (lastDot <= 0) return [file, '']; |
| 178 |
| 179 return [file.substring(0, lastDot), file.substring(lastDot)]; |
| 180 } |
| 181 |
| 182 ParsedPath clone() => new ParsedPath._( |
| 183 style, root, isRootRelative, |
| 184 new List.from(parts), new List.from(separators)); |
| 185 } |
| 186 |
OLD | NEW |