1/* 2 * Copyright (c) 2014, 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 26package jdk.internal.jrtfs; 27 28import java.util.regex.PatternSyntaxException; 29 30/** 31 * @implNote This class needs to maintain JDK 8 source compatibility. 32 * 33 * It is used internally in the JDK to implement jimage/jrtfs access, 34 * but also compiled and delivered as part of the jrtfs.jar to support access 35 * to the jimage file provided by the shipped JDK by tools running on JDK 8. 36 */ 37final class JrtUtils { 38 private JrtUtils() {} 39 40 private static final String regexMetaChars = ".^$+{[]|()"; 41 private static final String globMetaChars = "\\*?[{"; 42 private static boolean isRegexMeta(char c) { 43 return regexMetaChars.indexOf(c) != -1; 44 } 45 private static boolean isGlobMeta(char c) { 46 return globMetaChars.indexOf(c) != -1; 47 } 48 private static final char EOL = 0; 49 private static char next(String glob, int i) { 50 if (i < glob.length()) { 51 return glob.charAt(i); 52 } 53 return EOL; 54 } 55 56 /* 57 * Creates a regex pattern from the given glob expression. 58 * 59 * @throws PatternSyntaxException 60 */ 61 public static String toRegexPattern(String globPattern) { 62 boolean inGroup = false; 63 StringBuilder regex = new StringBuilder("^"); 64 65 int i = 0; 66 while (i < globPattern.length()) { 67 char c = globPattern.charAt(i++); 68 switch (c) { 69 case '\\': 70 // escape special characters 71 if (i == globPattern.length()) { 72 throw new PatternSyntaxException("No character to escape", 73 globPattern, i - 1); 74 } 75 char next = globPattern.charAt(i++); 76 if (isGlobMeta(next) || isRegexMeta(next)) { 77 regex.append('\\'); 78 } 79 regex.append(next); 80 break; 81 case '/': 82 regex.append(c); 83 break; 84 case '[': 85 // don't match name separator in class 86 regex.append("[[^/]&&["); 87 if (next(globPattern, i) == '^') { 88 // escape the regex negation char if it appears 89 regex.append("\\^"); 90 i++; 91 } else { 92 // negation 93 if (next(globPattern, i) == '!') { 94 regex.append('^'); 95 i++; 96 } 97 // hyphen allowed at start 98 if (next(globPattern, i) == '-') { 99 regex.append('-'); 100 i++; 101 } 102 } 103 boolean hasRangeStart = false; 104 char last = 0; 105 while (i < globPattern.length()) { 106 c = globPattern.charAt(i++); 107 if (c == ']') { 108 break; 109 } 110 if (c == '/') { 111 throw new PatternSyntaxException("Explicit 'name separator' in class", 112 globPattern, i - 1); 113 } 114 // TBD: how to specify ']' in a class? 115 if (c == '\\' || c == '[' || 116 c == '&' && next(globPattern, i) == '&') { 117 // escape '\', '[' or "&&" for regex class 118 regex.append('\\'); 119 } 120 regex.append(c); 121 122 if (c == '-') { 123 if (!hasRangeStart) { 124 throw new PatternSyntaxException("Invalid range", 125 globPattern, i - 1); 126 } 127 if ((c = next(globPattern, i++)) == EOL || c == ']') { 128 break; 129 } 130 if (c < last) { 131 throw new PatternSyntaxException("Invalid range", 132 globPattern, i - 3); 133 } 134 regex.append(c); 135 hasRangeStart = false; 136 } else { 137 hasRangeStart = true; 138 last = c; 139 } 140 } 141 if (c != ']') { 142 throw new PatternSyntaxException("Missing ']", globPattern, i - 1); 143 } 144 regex.append("]]"); 145 break; 146 case '{': 147 if (inGroup) { 148 throw new PatternSyntaxException("Cannot nest groups", 149 globPattern, i - 1); 150 } 151 regex.append("(?:(?:"); 152 inGroup = true; 153 break; 154 case '}': 155 if (inGroup) { 156 regex.append("))"); 157 inGroup = false; 158 } else { 159 regex.append('}'); 160 } 161 break; 162 case ',': 163 if (inGroup) { 164 regex.append(")|(?:"); 165 } else { 166 regex.append(','); 167 } 168 break; 169 case '*': 170 if (next(globPattern, i) == '*') { 171 // crosses directory boundaries 172 regex.append(".*"); 173 i++; 174 } else { 175 // within directory boundary 176 regex.append("[^/]*"); 177 } 178 break; 179 case '?': 180 regex.append("[^/]"); 181 break; 182 default: 183 if (isRegexMeta(c)) { 184 regex.append('\\'); 185 } 186 regex.append(c); 187 } 188 } 189 if (inGroup) { 190 throw new PatternSyntaxException("Missing '}", globPattern, i - 1); 191 } 192 return regex.append('$').toString(); 193 } 194} 195