Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1388)

Side by Side Diff: pkg/pathos/lib/path.dart

Issue 16580005: Support a URL style for pathos. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 /// A comprehensive, cross-platform path manipulation library. 5 /// A comprehensive, cross-platform path manipulation library.
6 /// 6 ///
7 /// ## Installing ## 7 /// ## Installing ##
8 /// 8 ///
9 /// Use [pub][] to install this package. Add the following to your 9 /// Use [pub][] to install this package. Add the following to your
10 /// `pubspec.yaml` file. 10 /// `pubspec.yaml` file.
(...skipping 13 matching lines...) Expand all
24 import 'dart:io' as io; 24 import 'dart:io' as io;
25 25
26 /// An internal builder for the current OS so we can provide a straight 26 /// An internal builder for the current OS so we can provide a straight
27 /// functional interface and not require users to create one. 27 /// functional interface and not require users to create one.
28 final _builder = new Builder(); 28 final _builder = new Builder();
29 29
30 /** 30 /**
31 * Inserts [length] elements in front of the [list] and fills them with the 31 * Inserts [length] elements in front of the [list] and fills them with the
32 * [fillValue]. 32 * [fillValue].
33 */ 33 */
34 void _growListFront(List list, int length, fillValue) { 34 void _growListFront(List list, int length, fillValue) =>
35 list.length += length; 35 list.insertAll(0, new List.filled(length, fillValue));
36 list.setRange(length, list.length, list);
37 for (var i = 0; i < length; i++) {
38 list[i] = fillValue;
39 }
40 }
41 36
42 /// Gets the path to the current working directory. 37 /// Gets the path to the current working directory.
43 String get current => io.Directory.current.path; 38 String get current => io.Directory.current.path;
44 39
45 /// Gets the path separator for the current platform. On Mac and Linux, this 40 /// Gets the path separator for the current platform. On Mac and Linux, this
46 /// is `/`. On Windows, it's `\`. 41 /// is `/`. On Windows, it's `\`.
47 String get separator => _builder.separator; 42 String get separator => _builder.separator;
48 43
49 /// Converts [path] to an absolute path by resolving it relative to the current 44 /// Converts [path] to an absolute path by resolving it relative to the current
50 /// working directory. If [path] is already an absolute path, just returns it. 45 /// working directory. If [path] is already an absolute path, just returns it.
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
105 /// // Unix 100 /// // Unix
106 /// path.rootPrefix('path/to/foo'); // -> '' 101 /// path.rootPrefix('path/to/foo'); // -> ''
107 /// path.rootPrefix('/path/to/foo'); // -> '/' 102 /// path.rootPrefix('/path/to/foo'); // -> '/'
108 /// 103 ///
109 /// // Windows 104 /// // Windows
110 /// path.rootPrefix(r'path\to\foo'); // -> '' 105 /// path.rootPrefix(r'path\to\foo'); // -> ''
111 /// path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' 106 /// path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\'
112 String rootPrefix(String path) => _builder.rootPrefix(path); 107 String rootPrefix(String path) => _builder.rootPrefix(path);
113 108
114 /// Returns `true` if [path] is an absolute path and `false` if it is a 109 /// Returns `true` if [path] is an absolute path and `false` if it is a
115 /// relative path. On POSIX systems, absolute paths start with a `/` (forward 110 /// relative path.
116 /// slash). On Windows, an absolute path starts with `\\`, or a drive letter 111 ///
117 /// followed by `:/` or `:\`. 112 /// On POSIX systems, absolute paths start with a `/` (forward slash). On
113 /// Windows, an absolute path starts with `\\`, or a drive letter followed by
114 /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and
115 /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`.
116 ///
117 /// URLs that start with `/` are known as "root-relative", since they're
118 /// relative to the root of the current URL. Since root-relative paths are still
119 /// absolute in every other sense, [isAbsolute] will return true for them. They
120 /// can be detected using [isRootRelative].
118 bool isAbsolute(String path) => _builder.isAbsolute(path); 121 bool isAbsolute(String path) => _builder.isAbsolute(path);
119 122
120 /// Returns `true` if [path] is a relative path and `false` if it is absolute. 123 /// Returns `true` if [path] is a relative path and `false` if it is absolute.
121 /// On POSIX systems, absolute paths start with a `/` (forward slash). On 124 /// On POSIX systems, absolute paths start with a `/` (forward slash). On
122 /// Windows, an absolute path starts with `\\`, or a drive letter followed by 125 /// Windows, an absolute path starts with `\\`, or a drive letter followed by
123 /// `:/` or `:\`. 126 /// `:/` or `:\`.
124 bool isRelative(String path) => _builder.isRelative(path); 127 bool isRelative(String path) => _builder.isRelative(path);
125 128
129 /// Returns `true` if [path] is a root-relative path and `false` if it's not.
130 ///
131 /// URLs that start with `/` are known as "root-relative", since they're
132 /// relative to the root of the current URL. Since root-relative paths are still
133 /// absolute in every other sense, [isAbsolute] will return true for them. They
134 /// can be detected using [isRootRelative].
135 ///
136 /// No POSIX and Windows paths are root-relative.
137 bool isRootRelative(String path) => _builder.isRootRelative(path);
138
126 /// Joins the given path parts into a single path using the current platform's 139 /// Joins the given path parts into a single path using the current platform's
127 /// [separator]. Example: 140 /// [separator]. Example:
128 /// 141 ///
129 /// path.join('path', 'to', 'foo'); // -> 'path/to/foo' 142 /// path.join('path', 'to', 'foo'); // -> 'path/to/foo'
130 /// 143 ///
131 /// If any part ends in a path separator, then a redundant separator will not 144 /// If any part ends in a path separator, then a redundant separator will not
132 /// be added: 145 /// be added:
133 /// 146 ///
134 /// path.join('path/', 'to', 'foo'); // -> 'path/to/foo 147 /// path.join('path/', 'to', 'foo'); // -> 'path/to/foo
135 /// 148 ///
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after
333 /// 346 ///
334 /// // Windows 347 /// // Windows
335 /// builder.rootPrefix(r'path\to\foo'); // -> '' 348 /// builder.rootPrefix(r'path\to\foo'); // -> ''
336 /// builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' 349 /// builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\'
337 String rootPrefix(String path) { 350 String rootPrefix(String path) {
338 var root = _parse(path).root; 351 var root = _parse(path).root;
339 return root == null ? '' : root; 352 return root == null ? '' : root;
340 } 353 }
341 354
342 /// Returns `true` if [path] is an absolute path and `false` if it is a 355 /// Returns `true` if [path] is an absolute path and `false` if it is a
343 /// relative path. On POSIX systems, absolute paths start with a `/` (forward 356 /// relative path.
344 /// slash). On Windows, an absolute path starts with `\\`, or a drive letter 357 ///
345 /// followed by `:/` or `:\`. 358 /// On POSIX systems, absolute paths start with a `/` (forward slash). On
359 /// Windows, an absolute path starts with `\\`, or a drive letter followed by
360 /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and
361 /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`.
362 ///
363 /// URLs that start with `/` are known as "root-relative", since they're
364 /// relative to the root of the current URL. Since root-relative paths are sti ll
Bob Nystrom 2013/06/06 22:50:40 Long lines.
nweiz 2013/06/07 01:04:30 Done.
365 /// absolute in every other sense, [isAbsolute] will return true for them. The y
366 /// can be detected using [isRootRelative].
346 bool isAbsolute(String path) => _parse(path).isAbsolute; 367 bool isAbsolute(String path) => _parse(path).isAbsolute;
347 368
348 /// Returns `true` if [path] is a relative path and `false` if it is absolute. 369 /// Returns `true` if [path] is a relative path and `false` if it is absolute.
349 /// On POSIX systems, absolute paths start with a `/` (forward slash). On 370 /// On POSIX systems, absolute paths start with a `/` (forward slash). On
350 /// Windows, an absolute path starts with `\\`, or a drive letter followed by 371 /// Windows, an absolute path starts with `\\`, or a drive letter followed by
351 /// `:/` or `:\`. 372 /// `:/` or `:\`.
352 bool isRelative(String path) => !isAbsolute(path); 373 bool isRelative(String path) => !isAbsolute(path);
353 374
375 /// Returns `true` if [path] is a root-relative path and `false` if it's not.
376 ///
377 /// URLs that start with `/` are known as "root-relative", since they're
378 /// relative to the root of the current URL. Since root-relative paths are sti ll
Bob Nystrom 2013/06/06 22:50:40 Ditto.
nweiz 2013/06/07 01:04:30 Done.
379 /// absolute in every other sense, [isAbsolute] will return true for them. The y
380 /// can be detected using [isRootRelative].
381 ///
382 /// No POSIX and Windows paths are root-relative.
383 bool isRootRelative(String path) => _parse(path).isRootRelative;
384
354 /// Joins the given path parts into a single path. Example: 385 /// Joins the given path parts into a single path. Example:
355 /// 386 ///
356 /// builder.join('path', 'to', 'foo'); // -> 'path/to/foo' 387 /// builder.join('path', 'to', 'foo'); // -> 'path/to/foo'
357 /// 388 ///
358 /// If any part ends in a path separator, then a redundant separator will not 389 /// If any part ends in a path separator, then a redundant separator will not
359 /// be added: 390 /// be added:
360 /// 391 ///
361 /// builder.join('path/', 'to', 'foo'); // -> 'path/to/foo 392 /// builder.join('path/', 'to', 'foo'); // -> 'path/to/foo
362 /// 393 ///
363 /// If a part is an absolute path, then anything before that will be ignored: 394 /// If a part is an absolute path, then anything before that will be ignored:
(...skipping 19 matching lines...) Expand all
383 /// If a part is an absolute path, then anything before that will be ignored: 414 /// If a part is an absolute path, then anything before that will be ignored:
384 /// 415 ///
385 /// builder.joinAll(['path', '/to', 'foo']); // -> '/to/foo' 416 /// builder.joinAll(['path', '/to', 'foo']); // -> '/to/foo'
386 /// 417 ///
387 /// For a fixed number of parts, [join] is usually terser. 418 /// For a fixed number of parts, [join] is usually terser.
388 String joinAll(Iterable<String> parts) { 419 String joinAll(Iterable<String> parts) {
389 var buffer = new StringBuffer(); 420 var buffer = new StringBuffer();
390 var needsSeparator = false; 421 var needsSeparator = false;
391 422
392 for (var part in parts) { 423 for (var part in parts) {
393 if (this.isAbsolute(part)) { 424 if (this.isRootRelative(part) &&
425 this.isAbsolute(buffer.toString()) &&
426 !this.isRootRelative(buffer.toString())) {
427 var oldRoot = this.rootPrefix(buffer.toString());
Bob Nystrom 2013/06/06 22:50:40 A couple of things here: 1. Calling toString() on
nweiz 2013/06/07 01:04:30 Done.
428 buffer = new StringBuffer();
Bob Nystrom 2013/06/06 22:50:40 buffer.clear() seems cleaner to me. Below too?
nweiz 2013/06/07 01:04:30 Done.
429 buffer.write(oldRoot);
430 buffer.write(part);
431 } else if (this.isAbsolute(part)) {
394 // An absolute path discards everything before it. 432 // An absolute path discards everything before it.
395 buffer = new StringBuffer(); 433 buffer = new StringBuffer();
396 buffer.write(part); 434 buffer.write(part);
397 } else { 435 } else {
398 if (part.length > 0 && part[0].contains(style.separatorPattern)) { 436 if (part.length > 0 && part[0].contains(style.separatorPattern)) {
399 // The part starts with a separator, so we don't need to add one. 437 // The part starts with a separator, so we don't need to add one.
400 } else if (needsSeparator) { 438 } else if (needsSeparator) {
401 buffer.write(separator); 439 buffer.write(separator);
402 } 440 }
403 441
404 buffer.write(part); 442 buffer.write(part);
405 } 443 }
406 444
407 // Unless this part ends with a separator, we'll need to add one before 445 // Unless this part ends with a separator, we'll need to add one before
408 // the next part. 446 // the next part.
409 needsSeparator = part.length > 0 && 447 needsSeparator = part.contains(style.needsSeparatorPattern);
410 !part[part.length - 1].contains(style.separatorPattern);
411 } 448 }
412 449
413 return buffer.toString(); 450 return buffer.toString();
414 } 451 }
415 452
416 // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. 453 // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
417 /// Splits [path] into its components using the current platform's 454 /// Splits [path] into its components using the current platform's
418 /// [separator]. Example: 455 /// [separator]. Example:
419 /// 456 ///
420 /// builder.split('path/to/foo'); // -> ['path', 'to', 'foo'] 457 /// builder.split('path/to/foo'); // -> ['path', 'to', 'foo']
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
491 528
492 from = from == null ? root : this.join(root, from); 529 from = from == null ? root : this.join(root, from);
493 530
494 // We can't determine the path from a relative path to an absolute path. 531 // We can't determine the path from a relative path to an absolute path.
495 if (this.isRelative(from) && this.isAbsolute(path)) { 532 if (this.isRelative(from) && this.isAbsolute(path)) {
496 return this.normalize(path); 533 return this.normalize(path);
497 } 534 }
498 535
499 // If the given path is relative, resolve it relative to the root of the 536 // If the given path is relative, resolve it relative to the root of the
500 // builder. 537 // builder.
501 if (this.isRelative(path)) path = this.resolve(path); 538 if (this.isRelative(path) || this.isRootRelative(path)) {
539 path = this.resolve(path);
540 }
502 541
503 // If the path is still relative and `from` is absolute, we're unable to 542 // If the path is still relative and `from` is absolute, we're unable to
504 // find a path from `from` to `path`. 543 // find a path from `from` to `path`.
505 if (this.isRelative(path) && this.isAbsolute(from)) { 544 if (this.isRelative(path) && this.isAbsolute(from)) {
506 throw new ArgumentError('Unable to find a path to "$path" from "$from".'); 545 throw new ArgumentError('Unable to find a path to "$path" from "$from".');
507 } 546 }
508 547
509 var fromParsed = _parse(from)..normalize(); 548 var fromParsed = _parse(from)..normalize();
510 var pathParsed = _parse(path)..normalize(); 549 var pathParsed = _parse(path)..normalize();
511 550
512 // If the root prefixes don't match (for example, different drive letters 551 // If the root prefixes don't match (for example, different drive letters
513 // on Windows), then there is no relative path, so just return the absolute 552 // on Windows), then there is no relative path, so just return the absolute
514 // one. In Windows, drive letters are case-insenstive and we allow 553 // one. In Windows, drive letters are case-insenstive and we allow
515 // calculation of relative paths, even if a path has not been normalized. 554 // calculation of relative paths, even if a path has not been normalized.
516 if (fromParsed.root != pathParsed.root && 555 if (fromParsed.root != pathParsed.root &&
517 ((fromParsed.root == null || pathParsed.root == null) || 556 ((fromParsed.root == null || pathParsed.root == null) ||
518 fromParsed.root.toLowerCase().replaceAll('/', '\\') != 557 fromParsed.root.toLowerCase().replaceAll('/', '\\') !=
519 pathParsed.root.toLowerCase().replaceAll('/', '\\'))) { 558 pathParsed.root.toLowerCase().replaceAll('/', '\\'))) {
520 return pathParsed.toString(); 559 return pathParsed.toString();
521 } 560 }
522 561
523 // Strip off their common prefix. 562 // Strip off their common prefix.
524 while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 && 563 while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 &&
525 fromParsed.parts[0] == pathParsed.parts[0]) { 564 fromParsed.parts[0] == pathParsed.parts[0]) {
526 fromParsed.parts.removeAt(0); 565 fromParsed.parts.removeAt(0);
527 fromParsed.separators.removeAt(0); 566 fromParsed.separators.removeAt(1);
528 pathParsed.parts.removeAt(0); 567 pathParsed.parts.removeAt(0);
529 pathParsed.separators.removeAt(0); 568 pathParsed.separators.removeAt(1);
530 } 569 }
531 570
532 // If there are any directories left in the root path, we need to walk up 571 // If there are any directories left in the root path, we need to walk up
533 // out of them. 572 // out of them.
534 _growListFront(pathParsed.parts, fromParsed.parts.length, '..'); 573 _growListFront(pathParsed.parts, fromParsed.parts.length, '..');
535 _growListFront( 574 pathParsed.separators[0] = '';
536 pathParsed.separators, fromParsed.parts.length, style.separator); 575 pathParsed.separators.insertAll(1,
576 new List.filled(fromParsed.parts.length, style.separator));
537 577
538 // Corner case: the paths completely collapsed. 578 // Corner case: the paths completely collapsed.
539 if (pathParsed.parts.length == 0) return '.'; 579 if (pathParsed.parts.length == 0) return '.';
540 580
541 // Make it relative. 581 // Make it relative.
542 pathParsed.root = ''; 582 pathParsed.root = '';
543 pathParsed.removeTrailingSeparators(); 583 pathParsed.removeTrailingSeparators();
544 584
545 return pathParsed.toString(); 585 return pathParsed.toString();
546 } 586 }
(...skipping 12 matching lines...) Expand all
559 } 599 }
560 600
561 return parsed.toString(); 601 return parsed.toString();
562 } 602 }
563 603
564 _ParsedPath _parse(String path) { 604 _ParsedPath _parse(String path) {
565 var before = path; 605 var before = path;
566 606
567 // Remove the root prefix, if any. 607 // Remove the root prefix, if any.
568 var root = style.getRoot(path); 608 var root = style.getRoot(path);
609 var isRootRelative = style.getRelativeRoot(path) != null;
569 if (root != null) path = path.substring(root.length); 610 if (root != null) path = path.substring(root.length);
570 611
571 // Split the parts on path separators. 612 // Split the parts on path separators.
572 var parts = []; 613 var parts = [];
573 var separators = []; 614 var separators = [];
615
616 var firstSeparator = style.separatorPattern.firstMatch(path);
617 if (firstSeparator != null && firstSeparator.start == 0) {
618 separators.add(firstSeparator[0]);
619 path = path.substring(firstSeparator[0].length);
620 } else {
621 separators.add('');
622 }
623
574 var start = 0; 624 var start = 0;
575 for (var match in style.separatorPattern.allMatches(path)) { 625 for (var match in style.separatorPattern.allMatches(path)) {
576 parts.add(path.substring(start, match.start)); 626 parts.add(path.substring(start, match.start));
577 separators.add(match[0]); 627 separators.add(match[0]);
578 start = match.end; 628 start = match.end;
579 } 629 }
580 630
581 // Add the final part, if any. 631 // Add the final part, if any.
582 if (start < path.length) { 632 if (start < path.length) {
583 parts.add(path.substring(start)); 633 parts.add(path.substring(start));
584 separators.add(''); 634 separators.add('');
585 } 635 }
586 636
587 return new _ParsedPath(style, root, parts, separators); 637 return new _ParsedPath(style, root, isRootRelative, parts, separators);
588 } 638 }
589 } 639 }
590 640
591 /// An enum type describing a "flavor" of path. 641 /// An enum type describing a "flavor" of path.
592 class Style { 642 class Style {
593 /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths 643 /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths
594 /// start with "/". Used by UNIX, Linux, Mac OS X, and others. 644 /// start with "/". Used by UNIX, Linux, Mac OS X, and others.
595 static final posix = new Style._('posix', '/', '/', '/'); 645 static final posix = new Style._('posix', '/', '/', r'[^/]$', '/');
596 646
597 /// Windows paths use "\" (backslash) as separators. Absolute paths start with 647 /// Windows paths use "\" (backslash) as separators. Absolute paths start with
598 /// a drive letter followed by a colon (example, "C:") or two backslashes 648 /// a drive letter followed by a colon (example, "C:") or two backslashes
599 /// ("\\") for UNC paths. 649 /// ("\\") for UNC paths.
600 // TODO(rnystrom): The UNC root prefix should include the drive name too, not 650 // TODO(rnystrom): The UNC root prefix should include the drive name too, not
601 // just the "\\". 651 // just the "\\".
602 static final windows = new Style._('windows', '\\', r'[/\\]', 652 static final windows = new Style._('windows', '\\', r'[/\\]', r'[^/\\]$',
603 r'\\\\|[a-zA-Z]:[/\\]'); 653 r'\\\\|[a-zA-Z]:[/\\]');
604 654
655 /// URLs aren't filesystem paths, but they're supported by Pathos to make it
656 /// easier to manipulate URL paths in the browser.
657 ///
658 /// URLs use "/" (forward slash) as separators. Absolute paths either start
659 /// with a protocol and optional hostname (e.g. `http://dartlang.org`,
660 /// `file://`) or with "/".
661 static final url = new Style._('url', '/', '/',
662 r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$",
663 r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*", r"/");
664
605 Style._(this.name, this.separator, String separatorPattern, 665 Style._(this.name, this.separator, String separatorPattern,
606 String rootPattern) 666 String needsSeparatorPattern, String rootPattern,
667 [String relativeRootPattern])
607 : separatorPattern = new RegExp(separatorPattern), 668 : separatorPattern = new RegExp(separatorPattern),
608 _rootPattern = new RegExp('^$rootPattern'); 669 needsSeparatorPattern = new RegExp(needsSeparatorPattern),
670 _rootPattern = new RegExp('^$rootPattern'),
671 _relativeRootPattern = relativeRootPattern == null ? null :
672 new RegExp('^$relativeRootPattern');
609 673
610 /// The name of this path style. Will be "posix" or "windows". 674 /// The name of this path style. Will be "posix" or "windows".
611 final String name; 675 final String name;
612 676
613 /// The path separator for this style. On POSIX, this is `/`. On Windows, 677 /// The path separator for this style. On POSIX, this is `/`. On Windows,
614 /// it's `\`. 678 /// it's `\`.
615 final String separator; 679 final String separator;
616 680
617 /// The [Pattern] that can be used to match a separator for a path in this 681 /// The [Pattern] that can be used to match a separator for a path in this
618 /// style. Windows allows both "/" and "\" as path separators even though 682 /// style. Windows allows both "/" and "\" as path separators even though
619 /// "\" is the canonical one. 683 /// "\" is the canonical one.
620 final Pattern separatorPattern; 684 final Pattern separatorPattern;
621 685
686 /// The [Pattern] that matches path components that need a separator after
687 /// them.
688 ///
689 /// Some styles ([url] in particlar) have moderately complex rules about when
690 /// a separator is needed, especially for root path components.
Bob Nystrom 2013/06/06 22:50:40 Can you explain what these rules are somewhere?
nweiz 2013/06/07 01:04:30 Done.
691 final Pattern needsSeparatorPattern;
692
622 // TODO(nweiz): make this a Pattern when issue 7080 is fixed. 693 // TODO(nweiz): make this a Pattern when issue 7080 is fixed.
623 /// The [RegExp] that can be used to match the root prefix of an absolute 694 /// The [RegExp] that can be used to match the root prefix of an absolute
624 /// path in this style. 695 /// path in this style.
625 final RegExp _rootPattern; 696 final RegExp _rootPattern;
626 697
698 /// The [RegExp] that can be used to match the root prefix of a root-relative
699 /// path in this style.
700 ///
701 /// This can be null to indicate that this style doesn't support root-relative
702 /// paths.
703 final RegExp _relativeRootPattern;
704
627 /// Gets the root prefix of [path] if path is absolute. If [path] is relative, 705 /// Gets the root prefix of [path] if path is absolute. If [path] is relative,
628 /// returns `null`. 706 /// returns `null`.
629 String getRoot(String path) { 707 String getRoot(String path) {
630 var match = _rootPattern.firstMatch(path); 708 var match = _rootPattern.firstMatch(path);
709 if (match == null) return getRelativeRoot(path);
710 return match[0];
Bob Nystrom 2013/06/06 22:50:40 Seems weird to refer back to match here after dele
nweiz 2013/06/07 01:04:30 Done.
711 }
712
713 /// Gets the root prefix of [path] if it's root-relative.
714 ///
715 /// If [path] is relative or absolute and not root-relative, returns `null`.
716 String getRelativeRoot(String path) {
717 if (_relativeRootPattern == null) return null;
718 var match = _relativeRootPattern.firstMatch(path);
631 if (match == null) return null; 719 if (match == null) return null;
632 return match[0]; 720 return match[0];
633 } 721 }
634 722
635 String toString() => name; 723 String toString() => name;
636 } 724 }
637 725
638 // TODO(rnystrom): Make this public? 726 // TODO(rnystrom): Make this public?
639 class _ParsedPath { 727 class _ParsedPath {
640 /// The [Style] that was used to parse this path. 728 /// The [Style] that was used to parse this path.
641 Style style; 729 Style style;
642 730
643 /// The absolute root portion of the path, or `null` if the path is relative. 731 /// The absolute root portion of the path, or `null` if the path is relative.
644 /// On POSIX systems, this will be `null` or "/". On Windows, it can be 732 /// On POSIX systems, this will be `null` or "/". On Windows, it can be
645 /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive 733 /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive
646 /// letters. 734 /// letters.
647 String root; 735 String root;
648 736
737 /// Whether this path is root-relative.
738 ///
739 /// See [Builder.isRootRelative].
740 bool isRootRelative;
741
649 /// The path-separated parts of the path. All but the last will be 742 /// The path-separated parts of the path. All but the last will be
650 /// directories. 743 /// directories.
651 List<String> parts; 744 List<String> parts;
652 745
653 /// The path separators following each part. The last one will be an empty 746 /// The path separators preceeding each part.
Bob Nystrom 2013/06/06 22:50:40 "preceeding" -> "preceding".
nweiz 2013/06/07 01:04:30 Done.
654 /// string unless the path ends with a trailing separator. 747 ///
748 /// The first one will be an empty string unless the root requires a separator
749 /// between it and the path. The last one will be an empty string unless the
750 /// path ends with a trailing separator.
655 List<String> separators; 751 List<String> separators;
656 752
657 /// The file extension of the last part, or "" if it doesn't have one. 753 /// The file extension of the last part, or "" if it doesn't have one.
658 String get extension => _splitExtension()[1]; 754 String get extension => _splitExtension()[1];
659 755
660 /// `true` if this is an absolute path. 756 /// `true` if this is an absolute path.
661 bool get isAbsolute => root != null; 757 bool get isAbsolute => root != null;
662 758
663 _ParsedPath(this.style, this.root, this.parts, this.separators); 759 _ParsedPath(this.style, this.root, this.isRootRelative, this.parts,
760 this.separators);
664 761
665 String get basename { 762 String get basename {
666 var copy = this.clone(); 763 var copy = this.clone();
667 copy.removeTrailingSeparators(); 764 copy.removeTrailingSeparators();
668 if (copy.parts.isEmpty) return root == null ? '' : root; 765 if (copy.parts.isEmpty) return root == null ? '' : root;
669 return copy.parts.last; 766 return copy.parts.last;
670 } 767 }
671 768
672 String get basenameWithoutExtension { 769 String get basenameWithoutExtension {
673 var copy = this.clone(); 770 var copy = this.clone();
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
708 if (!isAbsolute) { 805 if (!isAbsolute) {
709 _growListFront(newParts, leadingDoubles, '..'); 806 _growListFront(newParts, leadingDoubles, '..');
710 } 807 }
711 808
712 // If we collapsed down to nothing, do ".". 809 // If we collapsed down to nothing, do ".".
713 if (newParts.length == 0 && !isAbsolute) { 810 if (newParts.length == 0 && !isAbsolute) {
714 newParts.add('.'); 811 newParts.add('.');
715 } 812 }
716 813
717 // Canonicalize separators. 814 // Canonicalize separators.
718 var newSeparators = []; 815 var newSeparators = new List.generate(
719 _growListFront(newSeparators, newParts.length, style.separator); 816 newParts.length, (_) => style.separator, growable: true);
817 newSeparators.insert(0,
818 isAbsolute && newParts.length > 0 &&
819 root.contains(style.needsSeparatorPattern) ?
820 style.separator : '');
720 821
721 parts = newParts; 822 parts = newParts;
722 separators = newSeparators; 823 separators = newSeparators;
723 824
724 // Normalize the Windows root if needed. 825 // Normalize the Windows root if needed.
725 if (root != null && style == Style.windows) { 826 if (root != null && style == Style.windows) {
726 root = root.replaceAll('/', '\\'); 827 root = root.replaceAll('/', '\\');
727 } 828 }
728 removeTrailingSeparators(); 829 removeTrailingSeparators();
729 } 830 }
730 831
731 String toString() { 832 String toString() {
732 var builder = new StringBuffer(); 833 var builder = new StringBuffer();
733 if (root != null) builder.write(root); 834 if (root != null) builder.write(root);
734 for (var i = 0; i < parts.length; i++) { 835 for (var i = 0; i < parts.length; i++) {
836 builder.write(separators[i]);
735 builder.write(parts[i]); 837 builder.write(parts[i]);
736 builder.write(separators[i]);
737 } 838 }
839 builder.write(separators.last);
738 840
739 return builder.toString(); 841 return builder.toString();
740 } 842 }
741 843
742 /// Splits the last part of the path into a two-element list. The first is 844 /// Splits the last part of the path into a two-element list. The first is
743 /// the name of the file without any extension. The second is the extension 845 /// the name of the file without any extension. The second is the extension
744 /// or "" if it has none. 846 /// or "" if it has none.
745 List<String> _splitExtension() { 847 List<String> _splitExtension() {
746 if (parts.isEmpty) return ['', '']; 848 if (parts.isEmpty) return ['', ''];
747 849
748 var file = parts.last; 850 var file = parts.last;
749 if (file == '..') return ['..', '']; 851 if (file == '..') return ['..', ''];
750 852
751 var lastDot = file.lastIndexOf('.'); 853 var lastDot = file.lastIndexOf('.');
752 854
753 // If there is no dot, or it's the first character, like '.bashrc', it 855 // If there is no dot, or it's the first character, like '.bashrc', it
754 // doesn't count. 856 // doesn't count.
755 if (lastDot <= 0) return [file, '']; 857 if (lastDot <= 0) return [file, ''];
756 858
757 return [file.substring(0, lastDot), file.substring(lastDot)]; 859 return [file.substring(0, lastDot), file.substring(lastDot)];
758 } 860 }
759 861
760 _ParsedPath clone() => new _ParsedPath( 862 _ParsedPath clone() => new _ParsedPath(
761 style, root, new List.from(parts), new List.from(separators)); 863 style, root, isRootRelative,
864 new List.from(parts), new List.from(separators));
762 } 865 }
OLDNEW
« no previous file with comments | « no previous file | pkg/pathos/test/pathos_url_test.dart » ('j') | pkg/pathos/test/pathos_url_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698