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