Add path.joinAll.

Review URL: https://codereview.chromium.org//12314047

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@18860 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/lib/path.dart b/lib/path.dart
index 3bd2332..9e540b2 100644
--- a/lib/path.dart
+++ b/lib/path.dart
@@ -112,6 +112,23 @@
             String part5, String part6, String part7, String part8]) =>
   _builder.join(part1, part2, part3, part4, part5, part6, part7, part8);
 
+/// Joins the given path parts into a single path using the current platform's
+/// [separator]. Example:
+///
+///     path.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo'
+///
+/// If any part ends in a path separator, then a redundant separator will not
+/// be added:
+///
+///     path.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo
+///
+/// If a part is an absolute path, then anything before that will be ignored:
+///
+///     path.joinAll(['path', '/to', 'foo']); // -> '/to/foo'
+///
+/// For a fixed number of parts, [join] is usually terser.
+String joinAll(Iterable<String> parts) => _builder.joinAll(parts);
+
 // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed.
 /// Splits [path] into its components using the current platform's [separator].
 ///
@@ -321,15 +338,30 @@
   ///
   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 parts = [part1, part2, part3, part4, part5, part6, part7, part8];
-    _validateArgList("join", parts);
-
     for (var part in parts) {
-      if (part == null) continue;
-
       if (this.isAbsolute(part)) {
         // An absolute path discards everything before it.
         buffer.clear();
diff --git a/test/path_posix_test.dart b/test/path_posix_test.dart
index e1c3942..763035f 100644
--- a/test/path_posix_test.dart
+++ b/test/path_posix_test.dart
@@ -161,6 +161,25 @@
     });
   });
 
+  group('joinAll', () {
+    test('allows more than eight parts', () {
+      expect(builder.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']),
+          'a/b/c/d/e/f/g/h/i');
+    });
+
+    test('does not add separator if a part ends in one', () {
+      expect(builder.joinAll(['a/', 'b', 'c/', 'd']), 'a/b/c/d');
+      expect(builder.joinAll(['a\\', 'b']), r'a\/b');
+    });
+
+    test('ignores parts before an absolute path', () {
+      expect(builder.joinAll(['a', '/', 'b', 'c']), '/b/c');
+      expect(builder.joinAll(['a', '/b', '/c', 'd']), '/c/d');
+      expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'a/c:\b/c/d');
+      expect(builder.joinAll(['a', r'\\b', 'c', 'd']), r'a/\\b/c/d');
+    });
+  });
+
   group('split', () {
     test('simple cases', () {
       expect(builder.split(''), []);
diff --git a/test/path_windows_test.dart b/test/path_windows_test.dart
index 12e370b..8164224 100644
--- a/test/path_windows_test.dart
+++ b/test/path_windows_test.dart
@@ -180,6 +180,26 @@
     });
   });
 
+  group('joinAll', () {
+    test('allows more than eight parts', () {
+      expect(builder.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']),
+          r'a\b\c\d\e\f\g\h\i');
+    });
+
+    test('does not add separator if a part ends or begins in one', () {
+      expect(builder.joinAll([r'a\', 'b', r'c\', 'd']), r'a\b\c\d');
+      expect(builder.joinAll(['a/', 'b']), r'a/b');
+      expect(builder.joinAll(['a', '/b']), 'a/b');
+      expect(builder.joinAll(['a', r'\b']), r'a\b');
+    });
+
+    test('ignores parts before an absolute path', () {
+      expect(builder.joinAll(['a', '/b', '/c', 'd']), r'a/b/c\d');
+      expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'c:\b\c\d');
+      expect(builder.joinAll(['a', r'\\b', r'\\c', 'd']), r'\\c\d');
+    });
+  });
+
   group('split', () {
     test('simple cases', () {
       expect(builder.split(''), []);