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