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 (part2 == null && isAbsolute(part1) && !isRootRelative(part1)) { |
| 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 |