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
44
45
46
47
48
49
50
51
52
53
54
55 package org.codehaus.plexus.util;
56
57 import java.io.File;
58 import java.util.ArrayList;
59 import java.util.List;
60 import java.util.StringTokenizer;
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 public final class SelectorUtils {
76
77 public static final String PATTERN_HANDLER_PREFIX = "[";
78
79 public static final String PATTERN_HANDLER_SUFFIX = "]";
80
81 public static final String REGEX_HANDLER_PREFIX = "%regex" + PATTERN_HANDLER_PREFIX;
82
83 public static final String ANT_HANDLER_PREFIX = "%ant" + PATTERN_HANDLER_PREFIX;
84
85 private static SelectorUtils instance = new SelectorUtils();
86
87
88
89
90 private SelectorUtils() {}
91
92
93
94
95 public static SelectorUtils getInstance() {
96 return instance;
97 }
98
99
100
101
102
103
104
105
106
107
108
109 public static boolean matchPatternStart(String pattern, String str) {
110 return matchPatternStart(pattern, str, true);
111 }
112
113
114
115
116
117
118
119
120
121
122
123
124 public static boolean matchPatternStart(String pattern, String str, boolean isCaseSensitive) {
125 if (isRegexPrefixedPattern(pattern)) {
126
127
128 return true;
129 } else {
130 if (isAntPrefixedPattern(pattern)) {
131 pattern = pattern.substring(
132 ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length());
133 }
134
135 String altStr = str.replace('\\', '/');
136
137 return matchAntPathPatternStart(pattern, str, File.separator, isCaseSensitive)
138 || matchAntPathPatternStart(pattern, altStr, "/", isCaseSensitive);
139 }
140 }
141
142 static boolean isAntPrefixedPattern(String pattern) {
143 return pattern.length() > (ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
144 && pattern.startsWith(ANT_HANDLER_PREFIX)
145 && pattern.endsWith(PATTERN_HANDLER_SUFFIX);
146 }
147
148 @SuppressWarnings("SimplifiableIfStatement")
149 static boolean matchAntPathPatternStart(
150 MatchPattern pattern, String str, String separator, boolean isCaseSensitive) {
151 if (separatorPatternStartSlashMismatch(pattern, str, separator)) {
152 return false;
153 }
154
155 return matchAntPathPatternStart(pattern.getTokenizedPathString(), str, separator, isCaseSensitive);
156 }
157
158 static boolean matchAntPathPatternStart(String pattern, String str, String separator, boolean isCaseSensitive) {
159
160
161
162
163 if (separatorPatternStartSlashMismatch(pattern, str, separator)) {
164 return false;
165 }
166
167 String[] patDirs = tokenizePathToString(pattern, separator);
168 return matchAntPathPatternStart(patDirs, str, separator, isCaseSensitive);
169 }
170
171
172
173
174
175 private static boolean separatorPatternStartSlashMismatch(String pattern, String str, String separator) {
176 return str.startsWith(separator) != pattern.startsWith(separator);
177 }
178
179 private static boolean separatorPatternStartSlashMismatch(MatchPattern matchPattern, String str, String separator) {
180 return str.startsWith(separator) != matchPattern.startsWith(separator);
181 }
182
183 static boolean matchAntPathPatternStart(String[] patDirs, String str, String separator, boolean isCaseSensitive) {
184 String[] strDirs = tokenizePathToString(str, separator);
185
186 int patIdxStart = 0;
187 int patIdxEnd = patDirs.length - 1;
188 int strIdxStart = 0;
189 int strIdxEnd = strDirs.length - 1;
190
191
192 while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
193 String patDir = patDirs[patIdxStart];
194 if (patDir.equals("**")) {
195 break;
196 }
197 if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
198 return false;
199 }
200 patIdxStart++;
201 strIdxStart++;
202 }
203
204 return strIdxStart > strIdxEnd || patIdxStart <= patIdxEnd;
205 }
206
207
208
209
210
211
212
213
214 public static boolean matchPath(String pattern, String str) {
215 return matchPath(pattern, str, true);
216 }
217
218
219
220
221
222
223
224
225
226 public static boolean matchPath(String pattern, String str, boolean isCaseSensitive) {
227 return matchPath(pattern, str, File.separator, isCaseSensitive);
228 }
229
230 public static boolean matchPath(String pattern, String str, String separator, boolean isCaseSensitive) {
231 if (isRegexPrefixedPattern(pattern)) {
232 String localPattern = pattern.substring(
233 REGEX_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length());
234
235 return str.matches(localPattern);
236 } else {
237 String localPattern = isAntPrefixedPattern(pattern)
238 ? pattern.substring(ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length())
239 : pattern;
240 final String osRelatedPath = toOSRelatedPath(str, separator);
241 final String osRelatedPattern = toOSRelatedPath(localPattern, separator);
242 return matchAntPathPattern(osRelatedPattern, osRelatedPath, separator, isCaseSensitive);
243 }
244 }
245
246 private static String toOSRelatedPath(String pattern, String separator) {
247 if ("/".equals(separator)) {
248 return pattern.replace("\\", separator);
249 }
250 if ("\\".equals(separator)) {
251 return pattern.replace("/", separator);
252 }
253 return pattern;
254 }
255
256 static boolean isRegexPrefixedPattern(String pattern) {
257 return pattern.length() > (REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1)
258 && pattern.startsWith(REGEX_HANDLER_PREFIX)
259 && pattern.endsWith(PATTERN_HANDLER_SUFFIX);
260 }
261
262 static boolean matchAntPathPattern(
263 MatchPattern matchPattern, String str, String separator, boolean isCaseSensitive) {
264 if (separatorPatternStartSlashMismatch(matchPattern, str, separator)) {
265 return false;
266 }
267 String[] patDirs = matchPattern.getTokenizedPathString();
268 String[] strDirs = tokenizePathToString(str, separator);
269 return matchAntPathPattern(patDirs, strDirs, isCaseSensitive);
270 }
271
272 static boolean matchAntPathPattern(String pattern, String str, String separator, boolean isCaseSensitive) {
273 if (separatorPatternStartSlashMismatch(pattern, str, separator)) {
274 return false;
275 }
276 String[] patDirs = tokenizePathToString(pattern, separator);
277 String[] strDirs = tokenizePathToString(str, separator);
278 return matchAntPathPattern(patDirs, strDirs, isCaseSensitive);
279 }
280
281 static boolean matchAntPathPattern(String[] patDirs, String[] strDirs, boolean isCaseSensitive) {
282 int patIdxStart = 0;
283 int patIdxEnd = patDirs.length - 1;
284 int strIdxStart = 0;
285 int strIdxEnd = strDirs.length - 1;
286
287
288 while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
289 String patDir = patDirs[patIdxStart];
290 if (patDir.equals("**")) {
291 break;
292 }
293 if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
294 return false;
295 }
296 patIdxStart++;
297 strIdxStart++;
298 }
299 if (strIdxStart > strIdxEnd) {
300
301 for (int i = patIdxStart; i <= patIdxEnd; i++) {
302 if (!patDirs[i].equals("**")) {
303 return false;
304 }
305 }
306 return true;
307 } else {
308 if (patIdxStart > patIdxEnd) {
309
310 return false;
311 }
312 }
313
314
315 while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
316 String patDir = patDirs[patIdxEnd];
317 if (patDir.equals("**")) {
318 break;
319 }
320 if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) {
321 return false;
322 }
323 patIdxEnd--;
324 strIdxEnd--;
325 }
326 if (strIdxStart > strIdxEnd) {
327
328 for (int i = patIdxStart; i <= patIdxEnd; i++) {
329 if (!patDirs[i].equals("**")) {
330 return false;
331 }
332 }
333 return true;
334 }
335
336 while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
337 int patIdxTmp = -1;
338 for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
339 if (patDirs[i].equals("**")) {
340 patIdxTmp = i;
341 break;
342 }
343 }
344 if (patIdxTmp == patIdxStart + 1) {
345
346 patIdxStart++;
347 continue;
348 }
349
350
351 int patLength = (patIdxTmp - patIdxStart - 1);
352 int strLength = (strIdxEnd - strIdxStart + 1);
353 int foundIdx = -1;
354 strLoop:
355 for (int i = 0; i <= strLength - patLength; i++) {
356 for (int j = 0; j < patLength; j++) {
357 String subPat = patDirs[patIdxStart + j + 1];
358 String subStr = strDirs[strIdxStart + i + j];
359 if (!match(subPat, subStr, isCaseSensitive)) {
360 continue strLoop;
361 }
362 }
363
364 foundIdx = strIdxStart + i;
365 break;
366 }
367
368 if (foundIdx == -1) {
369 return false;
370 }
371
372 patIdxStart = patIdxTmp;
373 strIdxStart = foundIdx + patLength;
374 }
375
376 for (int i = patIdxStart; i <= patIdxEnd; i++) {
377 if (!patDirs[i].equals("**")) {
378 return false;
379 }
380 }
381
382 return true;
383 }
384
385 static boolean matchAntPathPattern(char[][] patDirs, char[][] strDirs, boolean isCaseSensitive) {
386 int patIdxStart = 0;
387 int patIdxEnd = patDirs.length - 1;
388 int strIdxStart = 0;
389 int strIdxEnd = strDirs.length - 1;
390
391
392 while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
393 char[] patDir = patDirs[patIdxStart];
394 if (isDoubleStar(patDir)) {
395 break;
396 }
397 if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
398 return false;
399 }
400 patIdxStart++;
401 strIdxStart++;
402 }
403 if (strIdxStart > strIdxEnd) {
404
405 for (int i = patIdxStart; i <= patIdxEnd; i++) {
406 if (!isDoubleStar(patDirs[i])) {
407 return false;
408 }
409 }
410 return true;
411 } else {
412 if (patIdxStart > patIdxEnd) {
413
414 return false;
415 }
416 }
417
418
419 while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
420 char[] patDir = patDirs[patIdxEnd];
421 if (isDoubleStar(patDir)) {
422 break;
423 }
424 if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) {
425 return false;
426 }
427 patIdxEnd--;
428 strIdxEnd--;
429 }
430 if (strIdxStart > strIdxEnd) {
431
432 for (int i = patIdxStart; i <= patIdxEnd; i++) {
433 if (!isDoubleStar(patDirs[i])) {
434 return false;
435 }
436 }
437 return true;
438 }
439
440 while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
441 int patIdxTmp = -1;
442 for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
443 if (isDoubleStar(patDirs[i])) {
444 patIdxTmp = i;
445 break;
446 }
447 }
448 if (patIdxTmp == patIdxStart + 1) {
449
450 patIdxStart++;
451 continue;
452 }
453
454
455 int patLength = (patIdxTmp - patIdxStart - 1);
456 int strLength = (strIdxEnd - strIdxStart + 1);
457 int foundIdx = -1;
458 strLoop:
459 for (int i = 0; i <= strLength - patLength; i++) {
460 for (int j = 0; j < patLength; j++) {
461 char[] subPat = patDirs[patIdxStart + j + 1];
462 char[] subStr = strDirs[strIdxStart + i + j];
463 if (!match(subPat, subStr, isCaseSensitive)) {
464 continue strLoop;
465 }
466 }
467
468 foundIdx = strIdxStart + i;
469 break;
470 }
471
472 if (foundIdx == -1) {
473 return false;
474 }
475
476 patIdxStart = patIdxTmp;
477 strIdxStart = foundIdx + patLength;
478 }
479
480 for (int i = patIdxStart; i <= patIdxEnd; i++) {
481 if (!isDoubleStar(patDirs[i])) {
482 return false;
483 }
484 }
485
486 return true;
487 }
488
489 private static boolean isDoubleStar(char[] patDir) {
490 return patDir != null && patDir.length == 2 && patDir[0] == '*' && patDir[1] == '*';
491 }
492
493
494
495
496
497
498
499
500
501
502 public static boolean match(String pattern, String str) {
503 return match(pattern, str, true);
504 }
505
506
507
508
509
510
511
512
513
514
515
516 public static boolean match(String pattern, String str, boolean isCaseSensitive) {
517 char[] patArr = pattern.toCharArray();
518 char[] strArr = str.toCharArray();
519 return match(patArr, strArr, isCaseSensitive);
520 }
521
522 public static boolean match(char[] patArr, char[] strArr, boolean isCaseSensitive) {
523 int patIdxStart = 0;
524 int patIdxEnd = patArr.length - 1;
525 int strIdxStart = 0;
526 int strIdxEnd = strArr.length - 1;
527 char ch;
528
529 boolean containsStar = false;
530 for (char aPatArr : patArr) {
531 if (aPatArr == '*') {
532 containsStar = true;
533 break;
534 }
535 }
536
537 if (!containsStar) {
538
539 if (patIdxEnd != strIdxEnd) {
540 return false;
541 }
542 for (int i = 0; i <= patIdxEnd; i++) {
543 ch = patArr[i];
544 if (ch != '?' && !equals(ch, strArr[i], isCaseSensitive)) {
545 return false;
546 }
547 }
548 return true;
549 }
550
551 if (patIdxEnd == 0) {
552 return true;
553 }
554
555
556 while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
557 if (ch != '?' && !equals(ch, strArr[strIdxStart], isCaseSensitive)) {
558 return false;
559 }
560 patIdxStart++;
561 strIdxStart++;
562 }
563 if (strIdxStart > strIdxEnd) {
564
565
566 for (int i = patIdxStart; i <= patIdxEnd; i++) {
567 if (patArr[i] != '*') {
568 return false;
569 }
570 }
571 return true;
572 }
573
574
575 while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
576 if (ch != '?' && !equals(ch, strArr[strIdxEnd], isCaseSensitive)) {
577 return false;
578 }
579 patIdxEnd--;
580 strIdxEnd--;
581 }
582 if (strIdxStart > strIdxEnd) {
583
584
585 for (int i = patIdxStart; i <= patIdxEnd; i++) {
586 if (patArr[i] != '*') {
587 return false;
588 }
589 }
590 return true;
591 }
592
593
594
595 while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
596 int patIdxTmp = -1;
597 for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
598 if (patArr[i] == '*') {
599 patIdxTmp = i;
600 break;
601 }
602 }
603 if (patIdxTmp == patIdxStart + 1) {
604
605 patIdxStart++;
606 continue;
607 }
608
609
610 int patLength = (patIdxTmp - patIdxStart - 1);
611 int strLength = (strIdxEnd - strIdxStart + 1);
612 int foundIdx = -1;
613 strLoop:
614 for (int i = 0; i <= strLength - patLength; i++) {
615 for (int j = 0; j < patLength; j++) {
616 ch = patArr[patIdxStart + j + 1];
617 if (ch != '?' && !equals(ch, strArr[strIdxStart + i + j], isCaseSensitive)) {
618 continue strLoop;
619 }
620 }
621
622 foundIdx = strIdxStart + i;
623 break;
624 }
625
626 if (foundIdx == -1) {
627 return false;
628 }
629
630 patIdxStart = patIdxTmp;
631 strIdxStart = foundIdx + patLength;
632 }
633
634
635
636 for (int i = patIdxStart; i <= patIdxEnd; i++) {
637 if (patArr[i] != '*') {
638 return false;
639 }
640 }
641 return true;
642 }
643
644
645
646
647 private static boolean equals(char c1, char c2, boolean isCaseSensitive) {
648 if (c1 == c2) {
649 return true;
650 }
651 if (!isCaseSensitive) {
652
653 if (Character.toUpperCase(c1) == Character.toUpperCase(c2)
654 || Character.toLowerCase(c1) == Character.toLowerCase(c2)) {
655 return true;
656 }
657 }
658 return false;
659 }
660
661 private static String[] tokenizePathToString(String path, String separator) {
662 List<String> ret = new ArrayList<String>();
663 StringTokenizer st = new StringTokenizer(path, separator);
664 while (st.hasMoreTokens()) {
665 ret.add(st.nextToken());
666 }
667 return ret.toArray(new String[0]);
668 }
669
670
671
672
673
674
675
676
677
678
679
680
681 public static boolean isOutOfDate(File src, File target, int granularity) {
682 if (!src.exists()) {
683 return false;
684 }
685 if (!target.exists()) {
686 return true;
687 }
688 if ((src.lastModified() - granularity) > target.lastModified()) {
689 return true;
690 }
691 return false;
692 }
693
694
695
696
697
698
699
700
701 public static String removeWhitespace(String input) {
702 StringBuilder result = new StringBuilder();
703 if (input != null) {
704 StringTokenizer st = new StringTokenizer(input);
705 while (st.hasMoreTokens()) {
706 result.append(st.nextToken());
707 }
708 }
709 return result.toString();
710 }
711 }