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