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