Chromium Code Reviews| 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 |