Dart DocumentationpathBuilder

Builder class

An instantiable class for manipulating paths. Unlike the top-level functions, this lets you explicitly select what platform the paths will use.

class Builder {
 /// Creates a new path builder for the given style and root directory.
 ///
 /// If [style] is omitted, it uses the host operating system's path style. If
 /// only [root] is omitted, it defaults ".". If *both* [style] and [root] are
 /// omitted, [root] defaults to the current working directory.
 ///
 /// On the browser, the path style is [Style.url]. In Dartium, [root] defaults
 /// to the current URL. When using dart2js, it currently defaults to `.` due
 /// to technical constraints.
 factory Builder({Style style, String root}) {
   if (root == null) {
     if (style == null) {
       root = current;
     } else {
       root = ".";
     }
   }

   if (style == null) style = Style.platform;

   return new Builder._(style, root);
 }

 Builder._(this.style, this.root);

 /// The style of path that this builder works with.
 final Style style;

 /// The root directory that relative paths will be relative to.
 final String root;

 /// Gets the path separator for the builder's [style]. On Mac and Linux,
 /// this is `/`. On Windows, it's `\`.
 String get separator => style.separator;

 /// Gets the part of [path] after the last separator on the builder's
 /// platform.
 ///
 ///     builder.basename('path/to/foo.dart'); // -> 'foo.dart'
 ///     builder.basename('path/to');          // -> 'to'
 ///
 /// Trailing separators are ignored.
 ///
 ///     builder.basename('path/to/'); // -> 'to'
 String basename(String path) => _parse(path).basename;

 /// Gets the part of [path] after the last separator on the builder's
 /// platform, and without any trailing file extension.
 ///
 ///     builder.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo'
 ///
 /// Trailing separators are ignored.
 ///
 ///     builder.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo'
 String basenameWithoutExtension(String path) =>
   _parse(path).basenameWithoutExtension;

 /// Gets the part of [path] before the last separator.
 ///
 ///     builder.dirname('path/to/foo.dart'); // -> 'path/to'
 ///     builder.dirname('path/to');          // -> 'path'
 ///
 /// Trailing separators are ignored.
 ///
 ///     builder.dirname('path/to/'); // -> 'path'
 String dirname(String path) {
   var parsed = _parse(path);
   parsed.removeTrailingSeparators();
   if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root;
   if (parsed.parts.length == 1) {
     return parsed.root == null ? '.' : parsed.root;
   }
   parsed.parts.removeLast();
   parsed.separators.removeLast();
   parsed.removeTrailingSeparators();
   return parsed.toString();
 }

 /// Gets the file extension of [path]: the portion of [basename] from the last
 /// `.` to the end (including the `.` itself).
 ///
 ///     builder.extension('path/to/foo.dart'); // -> '.dart'
 ///     builder.extension('path/to/foo'); // -> ''
 ///     builder.extension('path.to/foo'); // -> ''
 ///     builder.extension('path/to/foo.dart.js'); // -> '.js'
 ///
 /// If the file name starts with a `.`, then it is not considered an
 /// extension:
 ///
 ///     builder.extension('~/.bashrc');    // -> ''
 ///     builder.extension('~/.notes.txt'); // -> '.txt'
 String extension(String path) => _parse(path).extension;

 // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
 /// Returns the root of [path], if it's absolute, or an empty string if it's
 /// relative.
 ///
 ///     // Unix
 ///     builder.rootPrefix('path/to/foo'); // -> ''
 ///     builder.rootPrefix('/path/to/foo'); // -> '/'
 ///
 ///     // Windows
 ///     builder.rootPrefix(r'path\to\foo'); // -> ''
 ///     builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\'
 ///
 ///     // URL
 ///     builder.rootPrefix('path/to/foo'); // -> ''
 ///     builder.rootPrefix('http://dartlang.org/path/to/foo');
 ///       // -> 'http://dartlang.org'
 String rootPrefix(String path) {
   var root = _parse(path).root;
   return root == null ? '' : root;
 }

 /// Returns `true` if [path] is an absolute path and `false` if it is a
 /// relative path.
 ///
 /// On POSIX systems, absolute paths start with a `/` (forward slash). On
 /// Windows, an absolute path starts with `\\`, or a drive letter followed by
 /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and
 /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`.
 ///
 /// URLs that start with `/` are known as "root-relative", since they're
 /// relative to the root of the current URL. Since root-relative paths are
 /// still absolute in every other sense, [isAbsolute] will return true for
 /// them. They can be detected using [isRootRelative].
 bool isAbsolute(String path) => _parse(path).isAbsolute;

 /// Returns `true` if [path] is a relative path and `false` if it is absolute.
 /// On POSIX systems, absolute paths start with a `/` (forward slash). On
 /// Windows, an absolute path starts with `\\`, or a drive letter followed by
 /// `:/` or `:\`.
 bool isRelative(String path) => !this.isAbsolute(path);

 /// Returns `true` if [path] is a root-relative path and `false` if it's not.
 ///
 /// URLs that start with `/` are known as "root-relative", since they're
 /// relative to the root of the current URL. Since root-relative paths are
 /// still absolute in every other sense, [isAbsolute] will return true for
 /// them. They can be detected using [isRootRelative].
 ///
 /// No POSIX and Windows paths are root-relative.
 bool isRootRelative(String path) => _parse(path).isRootRelative;

 /// Joins the given path parts into a single path. Example:
 ///
 ///     builder.join('path', 'to', 'foo'); // -> 'path/to/foo'
 ///
 /// If any part ends in a path separator, then a redundant separator will not
 /// be added:
 ///
 ///     builder.join('path/', 'to', 'foo'); // -> 'path/to/foo
 ///
 /// If a part is an absolute path, then anything before that will be ignored:
 ///
 ///     builder.join('path', '/to', 'foo'); // -> '/to/foo'
 ///
 String join(String part1, [String part2, String part3, String part4,
             String part5, String part6, String part7, String part8]) {
   var parts = [part1, part2, part3, part4, part5, part6, part7, part8];
   _validateArgList("join", parts);
   return joinAll(parts.where((part) => part != null));
 }

 /// Joins the given path parts into a single path. Example:
 ///
 ///     builder.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo'
 ///
 /// If any part ends in a path separator, then a redundant separator will not
 /// be added:
 ///
 ///     builder.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo
 ///
 /// If a part is an absolute path, then anything before that will be ignored:
 ///
 ///     builder.joinAll(['path', '/to', 'foo']); // -> '/to/foo'
 ///
 /// For a fixed number of parts, [join] is usually terser.
 String joinAll(Iterable<String> parts) {
   var buffer = new StringBuffer();
   var needsSeparator = false;
   var isAbsoluteAndNotRootRelative = false;

   for (var part in parts.where((part) => part != '')) {
     if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) {
       // If the new part is root-relative, it preserves the previous root but
       // replaces the path after it.
       var oldRoot = this.rootPrefix(buffer.toString());
       buffer.clear();
       buffer.write(oldRoot);
       buffer.write(part);
     } else if (this.isAbsolute(part)) {
       isAbsoluteAndNotRootRelative = !this.isRootRelative(part);
       // An absolute path discards everything before it.
       buffer.clear();
       buffer.write(part);
     } else {
       if (part.length > 0 && part[0].contains(style.separatorPattern)) {
         // The part starts with a separator, so we don't need to add one.
       } else if (needsSeparator) {
         buffer.write(separator);
       }

       buffer.write(part);
     }

     // Unless this part ends with a separator, we'll need to add one before
     // the next part.
     needsSeparator = part.contains(style.needsSeparatorPattern);
   }

   return buffer.toString();
 }

 // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
 /// Splits [path] into its components using the current platform's
 /// [separator]. Example:
 ///
 ///     builder.split('path/to/foo'); // -> ['path', 'to', 'foo']
 ///
 /// The path will *not* be normalized before splitting.
 ///
 ///     builder.split('path/../foo'); // -> ['path', '..', 'foo']
 ///
 /// If [path] is absolute, the root directory will be the first element in the
 /// array. Example:
 ///
 ///     // Unix
 ///     builder.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo']
 ///
 ///     // Windows
 ///     builder.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo']
 List<String> split(String path) {
   var parsed = _parse(path);
   // Filter out empty parts that exist due to multiple separators in a row.
   parsed.parts = parsed.parts.where((part) => !part.isEmpty)
                              .toList();
   if (parsed.root != null) parsed.parts.insert(0, parsed.root);
   return parsed.parts;
 }

 /// Normalizes [path], simplifying it by handling `..`, and `.`, and
 /// removing redundant path separators whenever possible.
 ///
 ///     builder.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
 String normalize(String path) {
   var parsed = _parse(path);
   parsed.normalize();
   return parsed.toString();
 }

 /// Creates a new path by appending the given path parts to the [root].
 /// Equivalent to [join()] with [root] as the first argument. Example:
 ///
 ///     var builder = new Builder(root: 'root');
 ///     builder.resolve('path', 'to', 'foo'); // -> 'root/path/to/foo'
 String resolve(String part1, [String part2, String part3, String part4,
             String part5, String part6, String part7]) {
   return join(root, part1, part2, part3, part4, part5, part6, part7);
 }

 /// Attempts to convert [path] to an equivalent relative path relative to
 /// [root].
 ///
 ///     var builder = new Builder(root: '/root/path');
 ///     builder.relative('/root/path/a/b.dart'); // -> 'a/b.dart'
 ///     builder.relative('/root/other.dart'); // -> '../other.dart'
 ///
 /// If the [from] argument is passed, [path] is made relative to that instead.
 ///
 ///     builder.relative('/root/path/a/b.dart',
 ///         from: '/root/path'); // -> 'a/b.dart'
 ///     builder.relative('/root/other.dart',
 ///         from: '/root/path'); // -> '../other.dart'
 ///
 /// If [path] and/or [from] are relative paths, they are assumed to be
 /// relative to [root].
 ///
 /// Since there is no relative path from one drive letter to another on
 /// Windows, this will return an absolute path in that case.
 ///
 ///     builder.relative(r'D:\other', from: r'C:\other'); // -> 'D:\other'
 ///
 /// This will also return an absolute path if an absolute [path] is passed to
 /// a builder with a relative [root].
 ///
 ///     var builder = new Builder(r'some/relative/path');
 ///     builder.relative(r'/absolute/path'); // -> '/absolute/path'
 String relative(String path, {String from}) {
   from = from == null ? root : this.join(root, from);

   // We can't determine the path from a relative path to an absolute path.
   if (this.isRelative(from) && this.isAbsolute(path)) {
     return this.normalize(path);
   }

   // If the given path is relative, resolve it relative to the root of the
   // builder.
   if (this.isRelative(path) || this.isRootRelative(path)) {
     path = this.resolve(path);
   }

   // If the path is still relative and `from` is absolute, we're unable to
   // find a path from `from` to `path`.
   if (this.isRelative(path) && this.isAbsolute(from)) {
     throw new ArgumentError('Unable to find a path to "$path" from "$from".');
   }

   var fromParsed = _parse(from)..normalize();
   var pathParsed = _parse(path)..normalize();

   if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '.') {
     return pathParsed.toString();
   }

   // If the root prefixes don't match (for example, different drive letters
   // on Windows), then there is no relative path, so just return the absolute
   // one. In Windows, drive letters are case-insenstive and we allow
   // calculation of relative paths, even if a path has not been normalized.
   if (fromParsed.root != pathParsed.root &&
       ((fromParsed.root ==  null || pathParsed.root == null) ||
         fromParsed.root.toLowerCase().replaceAll('/', '\\') !=
         pathParsed.root.toLowerCase().replaceAll('/', '\\'))) {
     return pathParsed.toString();
   }

   // Strip off their common prefix.
   while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 &&
          fromParsed.parts[0] == pathParsed.parts[0]) {
     fromParsed.parts.removeAt(0);
     fromParsed.separators.removeAt(1);
     pathParsed.parts.removeAt(0);
     pathParsed.separators.removeAt(1);
   }

   // If there are any directories left in the from path, we need to walk up
   // out of them. If a directory left in the from path is '..', it cannot
   // be cancelled by adding a '..'.
   if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') {
     throw new ArgumentError('Unable to find a path to "$path" from "$from".');
   }
   _growListFront(pathParsed.parts, fromParsed.parts.length, '..');
   pathParsed.separators[0] = '';
   pathParsed.separators.insertAll(1,
       new List.filled(fromParsed.parts.length, style.separator));

   // Corner case: the paths completely collapsed.
   if (pathParsed.parts.length == 0) return '.';

   // Corner case: path was '.' and some '..' directories were added in front.
   // Don't add a final '/.' in that case.
   if (pathParsed.parts.length > 1 && pathParsed.parts.last == '.') {
     pathParsed.parts.removeLast();
     pathParsed.separators..removeLast()..removeLast()..add('');
   }

   // Make it relative.
   pathParsed.root = '';
   pathParsed.removeTrailingSeparators();

   return pathParsed.toString();
 }

 /// Removes a trailing extension from the last part of [path].
 ///
 ///     builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
 String withoutExtension(String path) {
   var parsed = _parse(path);

   for (var i = parsed.parts.length - 1; i >= 0; i--) {
     if (!parsed.parts[i].isEmpty) {
       parsed.parts[i] = parsed.basenameWithoutExtension;
       break;
     }
   }

   return parsed.toString();
 }

 /// Returns the path represented by [uri].
 ///
 /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL
 /// style, this will just convert [uri] to a string.
 ///
 ///     // POSIX
 ///     builder.fromUri(Uri.parse('file:///path/to/foo'))
 ///       // -> '/path/to/foo'
 ///
 ///     // Windows
 ///     builder.fromUri(Uri.parse('file:///C:/path/to/foo'))
 ///       // -> r'C:\path\to\foo'
 ///
 ///     // URL
 ///     builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo'))
 ///       // -> 'http://dartlang.org/path/to/foo'
 String fromUri(Uri uri) => style.pathFromUri(uri);

 /// Returns the URI that represents [path].
 ///
 /// For POSIX and Windows styles, this will return a `file:` URI. For the URL
 /// style, this will just convert [path] to a [Uri].
 ///
 ///     // POSIX
 ///     builder.toUri('/path/to/foo')
 ///       // -> Uri.parse('file:///path/to/foo')
 ///
 ///     // Windows
 ///     builder.toUri(r'C:\path\to\foo')
 ///       // -> Uri.parse('file:///C:/path/to/foo')
 ///
 ///     // URL
 ///     builder.toUri('http://dartlang.org/path/to/foo')
 ///       // -> Uri.parse('http://dartlang.org/path/to/foo')
 Uri toUri(String path) {
   if (isRelative(path)) {
     return style.relativePathToUri(path);
   } else {
     return style.absolutePathToUri(join(root, path));
   }
 }

 _ParsedPath _parse(String path) {
   var before = path;

   // Remove the root prefix, if any.
   var root = style.getRoot(path);
   var isRootRelative = style.getRelativeRoot(path) != null;
   if (root != null) path = path.substring(root.length);

   // Split the parts on path separators.
   var parts = [];
   var separators = [];

   var firstSeparator = style.separatorPattern.matchAsPrefix(path);
   if (firstSeparator != null) {
     separators.add(firstSeparator[0]);
     path = path.substring(firstSeparator[0].length);
   } else {
     separators.add('');
   }

   var start = 0;
   for (var match in style.separatorPattern.allMatches(path)) {
     parts.add(path.substring(start, match.start));
     separators.add(match[0]);
     start = match.end;
   }

   // Add the final part, if any.
   if (start < path.length) {
     parts.add(path.substring(start));
     separators.add('');
   }

   return new _ParsedPath(style, root, isRootRelative, parts, separators);
 }
}

Constructors

factory Builder({Style style, String root}) #

Creates a new path builder for the given style and root directory.

If style is omitted, it uses the host operating system's path style. If only root is omitted, it defaults ".". If both style and root are omitted, root defaults to the current working directory.

On the browser, the path style is Style.url. In Dartium, root defaults to the current URL. When using dart2js, it currently defaults to . due to technical constraints.

factory Builder({Style style, String root}) {
 if (root == null) {
   if (style == null) {
     root = current;
   } else {
     root = ".";
   }
 }

 if (style == null) style = Style.platform;

 return new Builder._(style, root);
}

Properties

final String root #

The root directory that relative paths will be relative to.

final String root

final String separator #

Gets the path separator for the builder's style. On Mac and Linux, this is /. On Windows, it's \.

String get separator => style.separator;

final Style style #

The style of path that this builder works with.

final Style style

Methods

String basename(String path) #

Gets the part of path after the last separator on the builder's platform.

builder.basename('path/to/foo.dart'); // -> 'foo.dart'
builder.basename('path/to');          // -> 'to'

Trailing separators are ignored.

builder.basename('path/to/'); // -> 'to'
String basename(String path) => _parse(path).basename;

String basenameWithoutExtension(String path) #

Gets the part of path after the last separator on the builder's platform, and without any trailing file extension.

builder.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo'

Trailing separators are ignored.

builder.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo'
String basenameWithoutExtension(String path) =>
 _parse(path).basenameWithoutExtension;

String dirname(String path) #

Gets the part of path before the last separator.

builder.dirname('path/to/foo.dart'); // -> 'path/to'
builder.dirname('path/to');          // -> 'path'

Trailing separators are ignored.

builder.dirname('path/to/'); // -> 'path'
String dirname(String path) {
 var parsed = _parse(path);
 parsed.removeTrailingSeparators();
 if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root;
 if (parsed.parts.length == 1) {
   return parsed.root == null ? '.' : parsed.root;
 }
 parsed.parts.removeLast();
 parsed.separators.removeLast();
 parsed.removeTrailingSeparators();
 return parsed.toString();
}

String extension(String path) #

Gets the file extension of path: the portion of basename from the last . to the end (including the . itself).

builder.extension('path/to/foo.dart'); // -> '.dart'
builder.extension('path/to/foo'); // -> ''
builder.extension('path.to/foo'); // -> ''
builder.extension('path/to/foo.dart.js'); // -> '.js'

If the file name starts with a ., then it is not considered an extension:

builder.extension('~/.bashrc');    // -> ''
builder.extension('~/.notes.txt'); // -> '.txt'
String extension(String path) => _parse(path).extension;

String fromUri(Uri uri) #

Returns the path represented by uri.

For POSIX and Windows styles, uri must be a file: URI. For the URL style, this will just convert uri to a string.

// POSIX
builder.fromUri(Uri.parse('file:///path/to/foo'))
  // -> '/path/to/foo'

// Windows
builder.fromUri(Uri.parse('file:///C:/path/to/foo'))
  // -> r'C:\path\to\foo'

// URL
builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo'))
  // -> 'http://dartlang.org/path/to/foo'
String fromUri(Uri uri) => style.pathFromUri(uri);

bool isAbsolute(String path) #

Returns true if path is an absolute path and false if it is a relative path.

On POSIX systems, absolute paths start with a / (forward slash). On Windows, an absolute path starts with \\, or a drive letter followed by :/ or :\. For URLs, absolute paths either start with a protocol and optional hostname (e.g. http://dartlang.org, file://) or with a /.

URLs that start with / are known as "root-relative", since they're relative to the root of the current URL. Since root-relative paths are still absolute in every other sense, isAbsolute will return true for them. They can be detected using isRootRelative.

bool isAbsolute(String path) => _parse(path).isAbsolute;

bool isRelative(String path) #

Returns true if path is a relative path and false if it is absolute. On POSIX systems, absolute paths start with a / (forward slash). On Windows, an absolute path starts with \\, or a drive letter followed by :/ or :\.

bool isRelative(String path) => !this.isAbsolute(path);

bool isRootRelative(String path) #

Returns true if path is a root-relative path and false if it's not.

URLs that start with / are known as "root-relative", since they're relative to the root of the current URL. Since root-relative paths are still absolute in every other sense, isAbsolute will return true for them. They can be detected using isRootRelative.

No POSIX and Windows paths are root-relative.

bool isRootRelative(String path) => _parse(path).isRootRelative;

String join(String part1, [String part2, String part3, String part4, String part5, String part6, String part7, String part8]) #

Joins the given path parts into a single path. Example:

builder.join('path', 'to', 'foo'); // -> 'path/to/foo'

If any part ends in a path separator, then a redundant separator will not be added:

builder.join('path/', 'to', 'foo'); // -> 'path/to/foo

If a part is an absolute path, then anything before that will be ignored:

builder.join('path', '/to', 'foo'); // -> '/to/foo'
String join(String part1, [String part2, String part3, String part4,
           String part5, String part6, String part7, String part8]) {
 var parts = [part1, part2, part3, part4, part5, part6, part7, part8];
 _validateArgList("join", parts);
 return joinAll(parts.where((part) => part != null));
}

String joinAll(Iterable<String> parts) #

Joins the given path parts into a single path. Example:

builder.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo'

If any part ends in a path separator, then a redundant separator will not be added:

builder.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo

If a part is an absolute path, then anything before that will be ignored:

builder.joinAll(['path', '/to', 'foo']); // -> '/to/foo'

For a fixed number of parts, join is usually terser.

String joinAll(Iterable<String> parts) {
 var buffer = new StringBuffer();
 var needsSeparator = false;
 var isAbsoluteAndNotRootRelative = false;

 for (var part in parts.where((part) => part != '')) {
   if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) {
     // If the new part is root-relative, it preserves the previous root but
     // replaces the path after it.
     var oldRoot = this.rootPrefix(buffer.toString());
     buffer.clear();
     buffer.write(oldRoot);
     buffer.write(part);
   } else if (this.isAbsolute(part)) {
     isAbsoluteAndNotRootRelative = !this.isRootRelative(part);
     // An absolute path discards everything before it.
     buffer.clear();
     buffer.write(part);
   } else {
     if (part.length > 0 && part[0].contains(style.separatorPattern)) {
       // The part starts with a separator, so we don't need to add one.
     } else if (needsSeparator) {
       buffer.write(separator);
     }

     buffer.write(part);
   }

   // Unless this part ends with a separator, we'll need to add one before
   // the next part.
   needsSeparator = part.contains(style.needsSeparatorPattern);
 }

 return buffer.toString();
}

String normalize(String path) #

Normalizes path, simplifying it by handling .., and ., and removing redundant path separators whenever possible.

builder.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
String normalize(String path) {
 var parsed = _parse(path);
 parsed.normalize();
 return parsed.toString();
}

String relative(String path, {String from}) #

Attempts to convert path to an equivalent relative path relative to root.

var builder = new Builder(root: '/root/path');
builder.relative('/root/path/a/b.dart'); // -> 'a/b.dart'
builder.relative('/root/other.dart'); // -> '../other.dart'

If the from argument is passed, path is made relative to that instead.

builder.relative('/root/path/a/b.dart',
    from: '/root/path'); // -> 'a/b.dart'
builder.relative('/root/other.dart',
    from: '/root/path'); // -> '../other.dart'

If path and/or from are relative paths, they are assumed to be relative to root.

Since there is no relative path from one drive letter to another on Windows, this will return an absolute path in that case.

builder.relative(r'D:\other', from: r'C:\other'); // -> 'D:\other'

This will also return an absolute path if an absolute path is passed to a builder with a relative root.

var builder = new Builder(r'some/relative/path');
builder.relative(r'/absolute/path'); // -> '/absolute/path'
String relative(String path, {String from}) {
 from = from == null ? root : this.join(root, from);

 // We can't determine the path from a relative path to an absolute path.
 if (this.isRelative(from) && this.isAbsolute(path)) {
   return this.normalize(path);
 }

 // If the given path is relative, resolve it relative to the root of the
 // builder.
 if (this.isRelative(path) || this.isRootRelative(path)) {
   path = this.resolve(path);
 }

 // If the path is still relative and `from` is absolute, we're unable to
 // find a path from `from` to `path`.
 if (this.isRelative(path) && this.isAbsolute(from)) {
   throw new ArgumentError('Unable to find a path to "$path" from "$from".');
 }

 var fromParsed = _parse(from)..normalize();
 var pathParsed = _parse(path)..normalize();

 if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '.') {
   return pathParsed.toString();
 }

 // If the root prefixes don't match (for example, different drive letters
 // on Windows), then there is no relative path, so just return the absolute
 // one. In Windows, drive letters are case-insenstive and we allow
 // calculation of relative paths, even if a path has not been normalized.
 if (fromParsed.root != pathParsed.root &&
     ((fromParsed.root ==  null || pathParsed.root == null) ||
       fromParsed.root.toLowerCase().replaceAll('/', '\\') !=
       pathParsed.root.toLowerCase().replaceAll('/', '\\'))) {
   return pathParsed.toString();
 }

 // Strip off their common prefix.
 while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 &&
        fromParsed.parts[0] == pathParsed.parts[0]) {
   fromParsed.parts.removeAt(0);
   fromParsed.separators.removeAt(1);
   pathParsed.parts.removeAt(0);
   pathParsed.separators.removeAt(1);
 }

 // If there are any directories left in the from path, we need to walk up
 // out of them. If a directory left in the from path is '..', it cannot
 // be cancelled by adding a '..'.
 if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') {
   throw new ArgumentError('Unable to find a path to "$path" from "$from".');
 }
 _growListFront(pathParsed.parts, fromParsed.parts.length, '..');
 pathParsed.separators[0] = '';
 pathParsed.separators.insertAll(1,
     new List.filled(fromParsed.parts.length, style.separator));

 // Corner case: the paths completely collapsed.
 if (pathParsed.parts.length == 0) return '.';

 // Corner case: path was '.' and some '..' directories were added in front.
 // Don't add a final '/.' in that case.
 if (pathParsed.parts.length > 1 && pathParsed.parts.last == '.') {
   pathParsed.parts.removeLast();
   pathParsed.separators..removeLast()..removeLast()..add('');
 }

 // Make it relative.
 pathParsed.root = '';
 pathParsed.removeTrailingSeparators();

 return pathParsed.toString();
}

String resolve(String part1, [String part2, String part3, String part4, String part5, String part6, String part7]) #

Creates a new path by appending the given path parts to the root. Equivalent to join() with root as the first argument. Example:

var builder = new Builder(root: 'root');
builder.resolve('path', 'to', 'foo'); // -> 'root/path/to/foo'
String resolve(String part1, [String part2, String part3, String part4,
           String part5, String part6, String part7]) {
 return join(root, part1, part2, part3, part4, part5, part6, part7);
}

String rootPrefix(String path) #

Returns the root of path, if it's absolute, or an empty string if it's relative.

// Unix
builder.rootPrefix('path/to/foo'); // -> ''
builder.rootPrefix('/path/to/foo'); // -> '/'

// Windows
builder.rootPrefix(r'path\to\foo'); // -> ''
builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\'

// URL
builder.rootPrefix('path/to/foo'); // -> ''
builder.rootPrefix('http://dartlang.org/path/to/foo');
  // -> 'http://dartlang.org'
String rootPrefix(String path) {
 var root = _parse(path).root;
 return root == null ? '' : root;
}

List<String> split(String path) #

Splits path into its components using the current platform's separator. Example:

builder.split('path/to/foo'); // -> ['path', 'to', 'foo']

The path will not be normalized before splitting.

builder.split('path/../foo'); // -> ['path', '..', 'foo']

If path is absolute, the root directory will be the first element in the array. Example:

// Unix
builder.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo']

// Windows
builder.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo']
List<String> split(String path) {
 var parsed = _parse(path);
 // Filter out empty parts that exist due to multiple separators in a row.
 parsed.parts = parsed.parts.where((part) => !part.isEmpty)
                            .toList();
 if (parsed.root != null) parsed.parts.insert(0, parsed.root);
 return parsed.parts;
}

Uri toUri(String path) #

Returns the URI that represents path.

For POSIX and Windows styles, this will return a file: URI. For the URL style, this will just convert path to a Uri.

// POSIX
builder.toUri('/path/to/foo')
  // -> Uri.parse('file:///path/to/foo')

// Windows
builder.toUri(r'C:\path\to\foo')
  // -> Uri.parse('file:///C:/path/to/foo')

// URL
builder.toUri('http://dartlang.org/path/to/foo')
  // -> Uri.parse('http://dartlang.org/path/to/foo')
Uri toUri(String path) {
 if (isRelative(path)) {
   return style.relativePathToUri(path);
 } else {
   return style.absolutePathToUri(join(root, path));
 }
}

String withoutExtension(String path) #

Removes a trailing extension from the last part of path.

builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
String withoutExtension(String path) {
 var parsed = _parse(path);

 for (var i = parsed.parts.length - 1; i >= 0; i--) {
   if (!parsed.parts[i].isEmpty) {
     parsed.parts[i] = parsed.basenameWithoutExtension;
     break;
   }
 }

 return parsed.toString();
}