--- old/src/java.base/share/classes/java/lang/ClassLoader.java 2018-09-10 14:12:17.443490752 -0400 +++ new/src/java.base/share/classes/java/lang/ClassLoader.java 2018-09-10 14:12:16.967490752 -0400 @@ -68,6 +68,7 @@ import jdk.internal.ref.CleanerFactory; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; +import jdk.internal.util.PathParser; import sun.reflect.misc.ReflectUtil; import sun.security.util.SecurityConstants; @@ -2555,53 +2556,8 @@ private static String[] initializePath(String propName) { String ldPath = System.getProperty(propName, ""); - int ldLen = ldPath.length(); - char ps = File.pathSeparatorChar; - int psCount = 0; - - if (ClassLoaderHelper.allowsQuotedPathElements && - ldPath.indexOf('\"') >= 0) { - // First, remove quotes put around quoted parts of paths. - // Second, use a quotation mark as a new path separator. - // This will preserve any quoted old path separators. - char[] buf = new char[ldLen]; - int bufLen = 0; - for (int i = 0; i < ldLen; ++i) { - char ch = ldPath.charAt(i); - if (ch == '\"') { - while (++i < ldLen && - (ch = ldPath.charAt(i)) != '\"') { - buf[bufLen++] = ch; - } - } else { - if (ch == ps) { - psCount++; - ch = '\"'; - } - buf[bufLen++] = ch; - } - } - ldPath = new String(buf, 0, bufLen); - ldLen = bufLen; - ps = '\"'; - } else { - for (int i = ldPath.indexOf(ps); i >= 0; - i = ldPath.indexOf(ps, i + 1)) { - psCount++; - } - } - String[] paths = new String[psCount + 1]; - int pathStart = 0; - for (int j = 0; j < psCount; ++j) { - int pathEnd = ldPath.indexOf(ps, pathStart); - paths[j] = (pathStart < pathEnd) ? - ldPath.substring(pathStart, pathEnd) : "."; - pathStart = pathEnd + 1; - } - paths[psCount] = (pathStart < ldLen) ? - ldPath.substring(pathStart, ldLen) : "."; - return paths; + return PathParser.parsePath(ldPath, "."); } // Invoked in the java.lang.Runtime class to implement load and loadLibrary. --- old/src/java.base/share/classes/java/nio/file/Paths.java 2018-09-10 14:12:18.839490752 -0400 +++ new/src/java.base/share/classes/java/nio/file/Paths.java 2018-09-10 14:12:18.375490752 -0400 @@ -25,13 +25,22 @@ package java.nio.file; -import java.nio.file.spi.FileSystemProvider; +import jdk.internal.util.PathParser; + +import java.io.File; import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; /** * This class consists exclusively of static methods that return a {@link Path} * by converting a path string or {@link URI}. * + *
Unless otherwise noted, passing a {@code null} argument to a method + * in this class will cause a {@link NullPointerException} to be thrown. + * * @apiNote * It is recommended to obtain a {@code Path} via the {@code Path.of} methods * instead of via the {@code get} methods defined in this class as this class @@ -96,4 +105,55 @@ public static Path get(URI uri) { return Path.of(uri); } + + /** + * Returns a list of path strings parsed from a string with empty paths removed. + * The {@link File#pathSeparator} is used to split the string and + * empty strings are removed. A list is created of the remaining strings. + *
+ * The {@code pathSeparator} character can be included in a path
+ * on operating systems that support quoting segments of the string.
+ *
+ * @implNote On Windows, quoting of path segments is supported.
+ * Each {@link File#pathSeparator} between pairs of quotation marks (@code 'U+0022')
+ * is considered an ordinary character and the quotes are omitted from the path.
+ * An unmatched double-quote is matched by the end of the string.
+ *
+ * @param path a {@code non-null} string containing paths separated by
+ * {@link File#pathSeparator}.
+ * @return a {@code non-null} immutable list of strings for each non-empty path
+ */
+ public static List
+ * The {@code pathSeparator} character can be included in a path
+ * on operating systems that support quoting segments of the string.
+ *
+ * @implNote On Windows, quoting of path segments is supported.
+ * Each {@link File#pathSeparator} between pairs of quotation marks (@code 'U+0022')
+ * is considered an ordinary character and the quotes are omitted from the path.
+ * An unmatched double-quote is matched by the end of the string.
+ *
+ * @param path a {@code non-null} string containing paths separated by
+ * {@link File#pathSeparator}.
+ * @return a {@code non-null} immutable list of Paths for each non-empty path
+ *
+ * @throws InvalidPathException
+ * if each path string cannot be converted to a {@code Path}
+ */
+ public static List
+ * The string is split using {@link File#pathSeparator}.
+ * The {@code pathSeparator} character can be included in a path
+ * on operating systems that support quoting segments of the string.
+ *
+ * @param path a string containing paths separated by the {@link File#pathSeparator}
+ * @param emptyPath a path to replace an empty path or {@code null} to omit empty paths
+ * @return an non-null array of strings for each path
+ * @implNote On Windows, quoting of path segments is supported. Each {@link File#pathSeparator}
+ * between pairs of quotation marks (@code 'U+0022') is considered an ordinary character
+ * and the quotes are omitted from the path.
+ * An unmatched quote is matched by the end of the string.
+ *
+ * @see java.nio.file.Paths#pathToStrings(String)
+ * @see java.nio.file.Paths#pathToPaths(String)
+ */
+ public static String[] parsePath(String path, String emptyPath) {
+ char ps = File.pathSeparatorChar;
+ int psCount = 0;
+ int emptyCount = 0;
+ if (allowsQuotedPathElements &&
+ path.indexOf('\"') >= 0) {
+ // First, remove quotes put around quoted parts of paths.
+ // Second, use a quotation mark as a new path separator.
+ // This will preserve any quoted old path separators.
+ int ldLen = path.length();
+ char[] buf = new char[ldLen];
+ int bufLen = 0;
+ int i, j;
+ for (i = 0, j = 0; i < ldLen; ++i) {
+ char ch = path.charAt(i);
+ if (ch == '\"') {
+ while (++i < ldLen &&
+ (ch = path.charAt(i)) != '\"') {
+ buf[bufLen++] = ch;
+ }
+ } else {
+ if (ch == ps) {
+ psCount++;
+ emptyCount += (j < bufLen) ? 0 : 1; // count empty elements
+ ch = '\"';
+ j = bufLen + 1; // start of next element
+ }
+ buf[bufLen++] = ch;
+ }
+ }
+ emptyCount += (j < bufLen) ? 0 : 1;
+ path = new String(buf, 0, bufLen);
+ ldLen = bufLen;
+ ps = '\"';
+ } else {
+ int i, j;
+ for (i = path.indexOf(ps), j = 0; i >= 0;
+ j = i + 1, i = path.indexOf(ps, j)) {
+ psCount++;
+ emptyCount += (j < i) ? 0 : 1; // count empty elements
+ }
+ emptyCount += (j < path.length()) ? 0 : 1;
+ }
+
+ if (emptyPath == null) {
+ // Do not save space to replace empty elements
+ psCount -= emptyCount;
+ }
+
+ String[] paths = new String[psCount + 1];
+ int next = 0;
+ int startPath = 0;
+ int endPath;
+ for (endPath = path.indexOf(ps); endPath >= 0;
+ startPath = endPath + 1, endPath = path.indexOf(ps, startPath)) {
+ if (endPath > startPath) {
+ paths[next++] = path.substring(startPath, endPath);
+ } else if (emptyPath != null) {
+ paths[next++] = emptyPath;
+ }
+ }
+ if (path.length() > startPath) {
+ paths[next] = path.substring(startPath);
+ } else if (emptyPath != null) {
+ paths[next] = emptyPath;
+ }
+ return paths;
+ }
+}
--- /dev/null 2018-09-07 16:39:11.960000000 -0400
+++ new/test/jdk/java/nio/file/Path/ParsePathTest.java 2018-09-10 14:12:44.911490752 -0400
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.System;
+import java.io.File;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import java.nio.file.Paths;
+
+import jdk.internal.util.PathParser;
+import org.testng.Assert;
+import org.testng.IMethodInstance;
+import org.testng.IMethodInterceptor;
+import org.testng.TestListenerAdapter;
+import org.testng.TestNG;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/*
+ * @test
+ * @bug 4463345 4244670 8030781
+ * @summary Tests of parsing paths via public and internal APIs
+ * @modules java.base/jdk.internal.util:+open
+ * @run testng/othervm ParsePathTest
+ */
+
+@Test
+public class ParsePathTest {
+
+ static final String SP = File.pathSeparator;
+
+ @DataProvider(name = "pathData")
+ static Object[][] pathData() {
+ if (System.getProperty("os.name").contains("Windows")) {
+ return new Object[][]{
+ // Common path lists
+ {"", List.of(".")},
+ {SP, List.of(".", ".")},
+ {"a" + SP, List.of("a", ".")},
+ {SP + "b", List.of(".", "b")},
+ {"a" + SP + SP + "b", List.of("a", ".", "b")},
+
+ // on Windows parts of paths may be quoted
+ {"\"\"", List.of(".")},
+ {"\"\"" + SP, List.of(".", ".")},
+ {SP + "\"\"", List.of(".", ".")},
+ {"a" + SP + "\"b\"" + SP, List.of("a", "b", ".")},
+ {SP + "\"a\"" + SP + SP + "b", List.of(".", "a", ".", "b")},
+ {"\"a\"" + SP + "\"b\"", List.of("a", "b")},
+ {"\"/a/\"b" + SP + "c", List.of("/a/b", "c")},
+ {"\"/a;b\"" + SP + "c", List.of("/a;b", "c")},
+ {"\"/a" + SP + "b\"" + SP + "c", List.of("/a" + SP + "b", "c")},
+ {"/\"a\"\";\"\"b\"" + SP + "\"c\"", List.of("/a;b", "c")},
+
+ // Unmatched trailing quotes
+ {"\"c\"" + SP + "/\"a\"\";\"\"b", List.of("c", "/a;b")},
+ {"\"c\"" + SP + "/\"a\"\";b", List.of("c", "/a;b")},
+
+ };
+ } else {
+ return new Object[][]{
+ {"", List.of(".")},
+ {SP, List.of(".", ".")},
+ {"a" + SP, List.of("a", ".")},
+ {SP + "b", List.of(".", "b")},
+ {"a" + SP + SP + "b", List.of("a", ".", "b")},
+ };
+ }
+ }
+
+ /**
+ * Test public API that produces lists of Path.
+ */
+ @Test(dataProvider = "pathData")
+ static void checkPathToPaths(String path, List