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 |