1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.eclipse.jgit.ignore.internal;
44
45 import static java.lang.Character.isLetter;
46
47 import java.text.MessageFormat;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.List;
51 import java.util.regex.Pattern;
52 import java.util.regex.PatternSyntaxException;
53
54 import org.eclipse.jgit.errors.InvalidPatternException;
55 import org.eclipse.jgit.ignore.FastIgnoreRule;
56 import org.eclipse.jgit.internal.JGitText;
57
58
59
60
61
62
63
64 public class Strings {
65
66 static char getPathSeparator(Character pathSeparator) {
67 return pathSeparator == null ? FastIgnoreRule.PATH_SEPARATOR
68 : pathSeparator.charValue();
69 }
70
71
72
73
74
75
76
77
78 public static String stripTrailing(String pattern, char c) {
79 while (pattern.length() > 0
80 && pattern.charAt(pattern.length() - 1) == c)
81 pattern = pattern.substring(0, pattern.length() - 1);
82 return pattern;
83 }
84
85 static int count(String s, char c, boolean ignoreFirstLast) {
86 int start = 0;
87 int count = 0;
88 while (true) {
89 start = s.indexOf(c, start);
90 if (start == -1)
91 break;
92 if (!ignoreFirstLast || (start != 0 && start != s.length()))
93 count++;
94 start++;
95 }
96 return count;
97 }
98
99
100
101
102
103
104
105
106
107
108 public static List<String> split(String pattern, char slash) {
109 int count = count(pattern, slash, true);
110 if (count < 1)
111 throw new IllegalStateException(
112 "Pattern must have at least two segments: " + pattern);
113 List<String> segments = new ArrayList<String>(count);
114 int right = 0;
115 while (true) {
116 int left = right;
117 right = pattern.indexOf(slash, right);
118 if (right == -1) {
119 if (left < pattern.length())
120 segments.add(pattern.substring(left));
121 break;
122 }
123 if (right - left > 0)
124 if (left == 1)
125
126 segments.add(pattern.substring(left - 1, right));
127 else if (right == pattern.length() - 1)
128
129 segments.add(pattern.substring(left, right + 1));
130 else
131 segments.add(pattern.substring(left, right));
132 right++;
133 }
134 return segments;
135 }
136
137 static boolean isWildCard(String pattern) {
138 return pattern.indexOf('*') != -1 || isComplexWildcard(pattern);
139 }
140
141 private static boolean isComplexWildcard(String pattern) {
142 int idx1 = pattern.indexOf('[');
143 if (idx1 != -1) {
144 return true;
145 }
146 if (pattern.indexOf('?') != -1) {
147 return true;
148 } else {
149
150
151 int backSlash = pattern.indexOf('\\');
152 if (backSlash >= 0) {
153 int nextIdx = backSlash + 1;
154 if (pattern.length() == nextIdx) {
155 return false;
156 }
157 char nextChar = pattern.charAt(nextIdx);
158 if (escapedByBackslash(nextChar)) {
159 return true;
160 } else {
161 return false;
162 }
163 }
164 }
165 return false;
166 }
167
168 private static boolean escapedByBackslash(char nextChar) {
169 return nextChar == '?' || nextChar == '*' || nextChar == '[';
170 }
171
172 static PatternState checkWildCards(String pattern) {
173 if (isComplexWildcard(pattern))
174 return PatternState.COMPLEX;
175 int startIdx = pattern.indexOf('*');
176 if (startIdx < 0)
177 return PatternState.NONE;
178
179 if (startIdx == pattern.length() - 1)
180 return PatternState.TRAILING_ASTERISK_ONLY;
181 if (pattern.lastIndexOf('*') == 0)
182 return PatternState.LEADING_ASTERISK_ONLY;
183
184 return PatternState.COMPLEX;
185 }
186
187 static enum PatternState {
188 LEADING_ASTERISK_ONLY, TRAILING_ASTERISK_ONLY, COMPLEX, NONE
189 }
190
191 final static List<String> POSIX_CHAR_CLASSES = Arrays.asList(
192 "alnum", "alpha", "blank", "cntrl",
193
194 "digit", "graph", "lower", "print",
195
196 "punct", "space", "upper", "xdigit",
197
198 "word"
199
200
201
202 );
203
204 private static final String DL = "\\p{javaDigit}\\p{javaLetter}";
205
206 final static List<String> JAVA_CHAR_CLASSES = Arrays
207 .asList("\\p{Alnum}", "\\p{javaLetter}", "\\p{Blank}", "\\p{Cntrl}",
208
209 "\\p{javaDigit}", "[\\p{Graph}" + DL + "]", "\\p{Ll}", "[\\p{Print}" + DL + "]",
210
211 "\\p{Punct}", "\\p{Space}", "\\p{Lu}", "\\p{XDigit}",
212
213 "[" + DL + "_]"
214
215 );
216
217
218
219 final static Pattern UNSUPPORTED = Pattern
220 .compile("\\[\\[[.=]\\w+[.=]\\]\\]");
221
222
223
224
225
226
227
228
229
230
231
232
233
234 static Pattern convertGlob(String pattern) throws InvalidPatternException {
235 if (UNSUPPORTED.matcher(pattern).find())
236 throw new InvalidPatternException(
237 "Collating symbols [[.a.]] or equivalence class expressions [[=a=]] are not supported",
238 pattern);
239
240 StringBuilder sb = new StringBuilder(pattern.length());
241
242 int in_brackets = 0;
243 boolean seenEscape = false;
244 boolean ignoreLastBracket = false;
245 boolean in_char_class = false;
246
247 char[] charClass = new char[6];
248
249 for (int i = 0; i < pattern.length(); i++) {
250 final char c = pattern.charAt(i);
251 switch (c) {
252
253 case '*':
254 if (seenEscape || in_brackets > 0)
255 sb.append(c);
256 else
257 sb.append('.').append(c);
258 break;
259
260 case '(':
261 case ')':
262 case '{':
263 case '}':
264 case '+':
265 case '$':
266 case '^':
267 case '|':
268 if (seenEscape || in_brackets > 0)
269 sb.append(c);
270 else
271 sb.append('\\').append(c);
272 break;
273
274 case '.':
275 if (seenEscape)
276 sb.append(c);
277 else
278 sb.append('\\').append('.');
279 break;
280
281 case '?':
282 if (seenEscape || in_brackets > 0)
283 sb.append(c);
284 else
285 sb.append('.');
286 break;
287
288 case ':':
289 if (in_brackets > 0)
290 if (lookBehind(sb) == '['
291 && isLetter(lookAhead(pattern, i)))
292 in_char_class = true;
293 sb.append(':');
294 break;
295
296 case '-':
297 if (in_brackets > 0) {
298 if (lookAhead(pattern, i) == ']')
299 sb.append('\\').append(c);
300 else
301 sb.append(c);
302 } else
303 sb.append('-');
304 break;
305
306 case '\\':
307 if (in_brackets > 0) {
308 char lookAhead = lookAhead(pattern, i);
309 if (lookAhead == ']' || lookAhead == '[')
310 ignoreLastBracket = true;
311 } else {
312
313 char lookAhead = lookAhead(pattern, i);
314 if (lookAhead != '\\' && lookAhead != '['
315 && lookAhead != '?' && lookAhead != '*'
316 && lookAhead != ' ' && lookBehind(sb) != '\\') {
317 break;
318 }
319 }
320 sb.append(c);
321 break;
322
323 case '[':
324 if (in_brackets > 0) {
325 sb.append('\\').append('[');
326 ignoreLastBracket = true;
327 } else {
328 if (!seenEscape) {
329 in_brackets++;
330 ignoreLastBracket = false;
331 }
332 sb.append('[');
333 }
334 break;
335
336 case ']':
337 if (seenEscape) {
338 sb.append(']');
339 ignoreLastBracket = true;
340 break;
341 }
342 if (in_brackets <= 0) {
343 sb.append('\\').append(']');
344 ignoreLastBracket = true;
345 break;
346 }
347 char lookBehind = lookBehind(sb);
348 if ((lookBehind == '[' && !ignoreLastBracket)
349 || lookBehind == '^') {
350 sb.append('\\');
351 sb.append(']');
352 ignoreLastBracket = true;
353 } else {
354 ignoreLastBracket = false;
355 if (!in_char_class) {
356 in_brackets--;
357 sb.append(']');
358 } else {
359 in_char_class = false;
360 String charCl = checkPosixCharClass(charClass);
361
362 if (charCl != null) {
363 sb.setLength(sb.length() - 4);
364 sb.append(charCl);
365 }
366 reset(charClass);
367 }
368 }
369 break;
370
371 case '!':
372 if (in_brackets > 0) {
373 if (lookBehind(sb) == '[')
374 sb.append('^');
375 else
376 sb.append(c);
377 } else
378 sb.append(c);
379 break;
380
381 default:
382 if (in_char_class)
383 setNext(charClass, c);
384 else
385 sb.append(c);
386 break;
387 }
388
389 seenEscape = c == '\\';
390
391 }
392
393 if (in_brackets > 0)
394 throw new InvalidPatternException("Not closed bracket?", pattern);
395 try {
396 return Pattern.compile(sb.toString());
397 } catch (PatternSyntaxException e) {
398 InvalidPatternException patternException = new InvalidPatternException(
399 MessageFormat.format(JGitText.get().invalidIgnoreRule,
400 pattern),
401 pattern);
402 patternException.initCause(e);
403 throw patternException;
404 }
405 }
406
407
408
409
410
411
412 private static char lookBehind(StringBuilder buffer) {
413 return buffer.length() > 0 ? buffer.charAt(buffer.length() - 1) : 0;
414 }
415
416
417
418
419
420
421
422
423 private static char lookAhead(String pattern, int i) {
424 int idx = i + 1;
425 return idx >= pattern.length() ? 0 : pattern.charAt(idx);
426 }
427
428 private static void setNext(char[] buffer, char c) {
429 for (int i = 0; i < buffer.length; i++)
430 if (buffer[i] == 0) {
431 buffer[i] = c;
432 break;
433 }
434 }
435
436 private static void reset(char[] buffer) {
437 for (int i = 0; i < buffer.length; i++)
438 buffer[i] = 0;
439 }
440
441 private static String checkPosixCharClass(char[] buffer) {
442 for (int i = 0; i < POSIX_CHAR_CLASSES.size(); i++) {
443 String clazz = POSIX_CHAR_CLASSES.get(i);
444 boolean match = true;
445 for (int j = 0; j < clazz.length(); j++)
446 if (buffer[j] != clazz.charAt(j)) {
447 match = false;
448 break;
449 }
450 if (match)
451 return JAVA_CHAR_CLASSES.get(i);
452 }
453 return null;
454 }
455
456 static String deleteBackslash(String s) {
457 if (s.indexOf('\\') < 0) {
458 return s;
459 }
460 StringBuilder sb = new StringBuilder(s.length());
461 for (int i = 0; i < s.length(); i++) {
462 char ch = s.charAt(i);
463 if (ch == '\\') {
464 if (i + 1 == s.length()) {
465 continue;
466 }
467 char next = s.charAt(i + 1);
468 if (next == '\\') {
469 sb.append(ch);
470 i++;
471 continue;
472 }
473 if (!escapedByBackslash(next)) {
474 continue;
475 }
476 }
477 sb.append(ch);
478 }
479 return sb.toString();
480 }
481
482 }