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
26package sun.nio.fs;
27
28import java.util.regex.PatternSyntaxException;
29
30public class Globs {
31    private Globs() { }
32
33    private static final String regexMetaChars = ".^$+{[]|()";
34    private static final String globMetaChars = "\\*?[{";
35
36    private static boolean isRegexMeta(char c) {
37        return regexMetaChars.indexOf(c) != -1;
38    }
39
40    private static boolean isGlobMeta(char c) {
41        return globMetaChars.indexOf(c) != -1;
42    }
43    private static char EOL = 0;  //TBD
44
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     */
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
210    static String toUnixRegexPattern(String globPattern) {
211        return toRegexPattern(globPattern, false);
212    }
213
214    static String toWindowsRegexPattern(String globPattern) {
215        return toRegexPattern(globPattern, true);
216    }
217}
218