1 /* 2 * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.internal.util; 27 28 import sun.security.action.GetPropertyAction; 29 30 import java.io.File; 31 32 /** 33 * Parse path strings and return a List of paths and path strings. 34 * Implementation relocated and improved from ClassLoader. 35 */ 36 public class PathParser { 37 38 private static final boolean allowsQuotedPathElements = initQuotesAllowed(); 39 40 private static boolean initQuotesAllowed() { 41 return GetPropertyAction.privilegedGetProperty("os.name").contains("Windows"); 42 } 43 44 /** 45 * Returns an array of path strings parsed from a string. 46 * Empty paths, identified by leading, trailing, and adjacent separator characters, 47 * can be omitted or replaced with a non-null provided path. 48 * <p> 49 * The string is split using {@link File#pathSeparator}. 50 * The {@code pathSeparator} character can be included in a path 51 * on operating systems that support quoting segments of the string. 52 * 53 * @param path a string containing paths separated by the {@link File#pathSeparator} 54 * @param emptyPath a path to replace an empty path or {@code null} to omit empty paths 55 * @return an non-null array of strings for each path 56 * @implNote On Windows, quoting of path segments is supported. Each {@link File#pathSeparator} 57 * between pairs of quotation marks (@code 'U+0022') is considered an ordinary character 58 * and the quotes are omitted from the path. 59 * An unmatched quote is matched by the end of the string. 60 * 61 * @see java.nio.file.Paths#pathToStrings(String) 62 * @see java.nio.file.Paths#pathToPaths(String) 63 */ 64 public static String[] parsePath(String path, String emptyPath) { 65 char ps = File.pathSeparatorChar; 66 int psCount = 0; 67 int emptyCount = 0; 68 if (allowsQuotedPathElements && 69 path.indexOf('\"') >= 0) { 70 // First, remove quotes put around quoted parts of paths. 71 // Second, use a quotation mark as a new path separator. 72 // This will preserve any quoted old path separators. 73 int ldLen = path.length(); 74 char[] buf = new char[ldLen]; 75 int bufLen = 0; 76 int i, j; 77 for (i = 0, j = 0; i < ldLen; ++i) { 78 char ch = path.charAt(i); 79 if (ch == '\"') { 80 while (++i < ldLen && 81 (ch = path.charAt(i)) != '\"') { 82 buf[bufLen++] = ch; 83 } 84 } else { 85 if (ch == ps) { 86 psCount++; 87 emptyCount += (j < bufLen) ? 0 : 1; // count empty elements 88 ch = '\"'; 89 j = bufLen + 1; // start of next element 90 } 91 buf[bufLen++] = ch; 92 } 93 } 94 emptyCount += (j < bufLen) ? 0 : 1; 95 path = new String(buf, 0, bufLen); 96 ldLen = bufLen; 97 ps = '\"'; 98 } else { 99 int i, j; 100 for (i = path.indexOf(ps), j = 0; i >= 0; 101 j = i + 1, i = path.indexOf(ps, j)) { 102 psCount++; 103 emptyCount += (j < i) ? 0 : 1; // count empty elements 104 } 105 emptyCount += (j < path.length()) ? 0 : 1; 106 } 107 108 if (emptyPath == null) { 109 // Do not save space to replace empty elements 110 psCount -= emptyCount; 111 } 112 113 String[] paths = new String[psCount + 1]; 114 int next = 0; 115 int startPath = 0; 116 int endPath; 117 for (endPath = path.indexOf(ps); endPath >= 0; 118 startPath = endPath + 1, endPath = path.indexOf(ps, startPath)) { 119 if (endPath > startPath) { 120 paths[next++] = path.substring(startPath, endPath); 121 } else if (emptyPath != null) { 122 paths[next++] = emptyPath; 123 } 124 } 125 if (path.length() > startPath) { 126 paths[next] = path.substring(startPath); 127 } else if (emptyPath != null) { 128 paths[next] = emptyPath; 129 } 130 return paths; 131 } 132 }