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