OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library path.context; | 5 library path.context; |
6 | 6 |
7 import 'characters.dart' as chars; | |
7 import 'internal_style.dart'; | 8 import 'internal_style.dart'; |
8 import 'style.dart'; | 9 import 'style.dart'; |
9 import 'parsed_path.dart'; | 10 import 'parsed_path.dart'; |
10 import 'path_exception.dart'; | 11 import 'path_exception.dart'; |
11 import '../path.dart' as p; | 12 import '../path.dart' as p; |
12 | 13 |
13 Context createInternal() => new Context._internal(); | 14 Context createInternal() => new Context._internal(); |
14 | 15 |
15 /// An instantiable class for manipulating paths. Unlike the top-level | 16 /// An instantiable class for manipulating paths. Unlike the top-level |
16 /// functions, this lets you explicitly select what platform the paths will use. | 17 /// functions, this lets you explicitly select what platform the paths will use. |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
66 | 67 |
67 /// Creates a new path by appending the given path parts to [current]. | 68 /// Creates a new path by appending the given path parts to [current]. |
68 /// Equivalent to [join()] with [current] as the first argument. Example: | 69 /// Equivalent to [join()] with [current] as the first argument. Example: |
69 /// | 70 /// |
70 /// var context = new Context(current: '/root'); | 71 /// var context = new Context(current: '/root'); |
71 /// context.absolute('path', 'to', 'foo'); // -> '/root/path/to/foo' | 72 /// context.absolute('path', 'to', 'foo'); // -> '/root/path/to/foo' |
72 /// | 73 /// |
73 /// If [current] isn't absolute, this won't return an absolute path. | 74 /// If [current] isn't absolute, this won't return an absolute path. |
74 String absolute(String part1, [String part2, String part3, String part4, | 75 String absolute(String part1, [String part2, String part3, String part4, |
75 String part5, String part6, String part7]) { | 76 String part5, String part6, String part7]) { |
77 _validateArgList( | |
78 "absolute", [part1, part2, part3, part4, part5, part6, part7]); | |
79 | |
80 // If there's a single absolute path, just return it. This is a lot faster | |
81 // for the common case of `p.absolute(path)`. | |
82 if (isAbsolute(part1) && !isRootRelative(part1) && part2 == null) { | |
Bob Nystrom
2015/11/17 23:55:10
I'd move the null check first since that will be t
nweiz
2015/11/18 00:04:13
Done.
| |
83 return part1; | |
84 } | |
85 | |
76 return join(current, part1, part2, part3, part4, part5, part6, part7); | 86 return join(current, part1, part2, part3, part4, part5, part6, part7); |
77 } | 87 } |
78 | 88 |
79 /// Gets the part of [path] after the last separator on the context's | 89 /// Gets the part of [path] after the last separator on the context's |
80 /// platform. | 90 /// platform. |
81 /// | 91 /// |
82 /// context.basename('path/to/foo.dart'); // -> 'foo.dart' | 92 /// context.basename('path/to/foo.dart'); // -> 'foo.dart' |
83 /// context.basename('path/to'); // -> 'to' | 93 /// context.basename('path/to'); // -> 'to' |
84 /// | 94 /// |
85 /// Trailing separators are ignored. | 95 /// Trailing separators are ignored. |
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
288 parsed.parts = parsed.parts.where((part) => !part.isEmpty).toList(); | 298 parsed.parts = parsed.parts.where((part) => !part.isEmpty).toList(); |
289 if (parsed.root != null) parsed.parts.insert(0, parsed.root); | 299 if (parsed.root != null) parsed.parts.insert(0, parsed.root); |
290 return parsed.parts; | 300 return parsed.parts; |
291 } | 301 } |
292 | 302 |
293 /// Normalizes [path], simplifying it by handling `..`, and `.`, and | 303 /// Normalizes [path], simplifying it by handling `..`, and `.`, and |
294 /// removing redundant path separators whenever possible. | 304 /// removing redundant path separators whenever possible. |
295 /// | 305 /// |
296 /// context.normalize('path/./to/..//file.text'); // -> 'path/file.txt' | 306 /// context.normalize('path/./to/..//file.text'); // -> 'path/file.txt' |
297 String normalize(String path) { | 307 String normalize(String path) { |
308 if (!_needsNormalization(path)) return path; | |
309 | |
298 var parsed = _parse(path); | 310 var parsed = _parse(path); |
299 parsed.normalize(); | 311 parsed.normalize(); |
300 return parsed.toString(); | 312 return parsed.toString(); |
301 } | 313 } |
302 | 314 |
315 /// Returns whether [path] needs to be normalized. | |
316 bool _needsNormalization(String path) { | |
317 var start = 0; | |
318 var codeUnits = path.codeUnits; | |
319 var previousPrevious; | |
320 var previous; | |
321 | |
322 // Skip past the root before we start looking for snippets that need | |
323 // normalization. We want to normalize "//", but not when it's part of | |
324 // "http://". | |
325 var root = style.rootLength(path); | |
326 if (root != 0) { | |
327 start = root; | |
328 previous = chars.SLASH; | |
329 | |
330 // On Windows, the root still needs to be normalized if it contains a | |
331 // forward slash. | |
332 if (style == Style.windows) { | |
333 for (var i = 0; i < root; i++) { | |
334 if (codeUnits[i] == chars.SLASH) return true; | |
335 } | |
336 } | |
337 } | |
338 | |
339 for (var i = start; i < codeUnits.length; i++) { | |
340 var codeUnit = codeUnits[i]; | |
341 if (style.isSeparator(codeUnit)) { | |
342 // Forward slashes in Windows paths are normalized to backslashes. | |
343 if (style == Style.windows && codeUnit == chars.SLASH) return true; | |
344 | |
345 // Multiple separators are normalized to single separators. | |
346 if (previous != null && style.isSeparator(previous)) return true; | |
347 | |
348 // Single dots and double dots are normalized to directory traversals. | |
349 // | |
350 // This can return false positives for ".../", but that's unlikely | |
351 // enough that it's probably not going to cause performance issues. | |
352 if (previous == chars.PERIOD && | |
353 (previousPrevious == null || | |
354 previousPrevious == chars.PERIOD || | |
355 style.isSeparator(previousPrevious))) { | |
356 return true; | |
357 } | |
358 } | |
359 | |
360 previousPrevious = previous; | |
361 previous = codeUnit; | |
362 } | |
363 | |
364 // Empty paths are normalized to ".". | |
365 if (previous == null) return true; | |
366 | |
367 // Trailing separators are removed. | |
368 if (style.isSeparator(previous)) return true; | |
369 | |
370 // Single dots and double dots are normalized to directory traversals. | |
371 if (previous == chars.PERIOD && | |
372 (previousPrevious == null || | |
373 previousPrevious == chars.SLASH || | |
374 previousPrevious == chars.PERIOD)) { | |
375 return true; | |
376 } | |
377 | |
378 return false; | |
379 } | |
380 | |
303 /// Attempts to convert [path] to an equivalent relative path relative to | 381 /// Attempts to convert [path] to an equivalent relative path relative to |
304 /// [root]. | 382 /// [root]. |
305 /// | 383 /// |
306 /// var context = new Context(current: '/root/path'); | 384 /// var context = new Context(current: '/root/path'); |
307 /// context.relative('/root/path/a/b.dart'); // -> 'a/b.dart' | 385 /// context.relative('/root/path/a/b.dart'); // -> 'a/b.dart' |
308 /// context.relative('/root/other.dart'); // -> '../other.dart' | 386 /// context.relative('/root/other.dart'); // -> '../other.dart' |
309 /// | 387 /// |
310 /// If the [from] argument is passed, [path] is made relative to that instead. | 388 /// If the [from] argument is passed, [path] is made relative to that instead. |
311 /// | 389 /// |
312 /// context.relative('/root/path/a/b.dart', | 390 /// context.relative('/root/path/a/b.dart', |
(...skipping 252 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
565 var message = new StringBuffer(); | 643 var message = new StringBuffer(); |
566 message.write("$method("); | 644 message.write("$method("); |
567 message.write(args | 645 message.write(args |
568 .take(numArgs) | 646 .take(numArgs) |
569 .map((arg) => arg == null ? "null" : '"$arg"') | 647 .map((arg) => arg == null ? "null" : '"$arg"') |
570 .join(", ")); | 648 .join(", ")); |
571 message.write("): part ${i - 1} was null, but part $i was not."); | 649 message.write("): part ${i - 1} was null, but part $i was not."); |
572 throw new ArgumentError(message.toString()); | 650 throw new ArgumentError(message.toString()); |
573 } | 651 } |
574 } | 652 } |
OLD | NEW |