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
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
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 while ( ( ch = patArr[patIdxStart] ) != '*' && strIdxStart <= strIdxEnd )
430 {
431 if ( ch != '?' && !equals( ch, strArr[strIdxStart], isCaseSensitive ) )
432 {
433 return false;
434 }
435 patIdxStart++;
436 strIdxStart++;
437 }
438 if ( strIdxStart > strIdxEnd )
439 {
440
441
442 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
443 {
444 if ( patArr[i] != '*' )
445 {
446 return false;
447 }
448 }
449 return true;
450 }
451
452
453 while ( ( ch = patArr[patIdxEnd] ) != '*' && strIdxStart <= strIdxEnd )
454 {
455 if ( ch != '?' && !equals( ch, strArr[strIdxEnd], isCaseSensitive ) )
456 {
457 return false;
458 }
459 patIdxEnd--;
460 strIdxEnd--;
461 }
462 if ( strIdxStart > strIdxEnd )
463 {
464
465
466 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
467 {
468 if ( patArr[i] != '*' )
469 {
470 return false;
471 }
472 }
473 return true;
474 }
475
476
477
478 while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
479 {
480 int patIdxTmp = -1;
481 for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
482 {
483 if ( patArr[i] == '*' )
484 {
485 patIdxTmp = i;
486 break;
487 }
488 }
489 if ( patIdxTmp == patIdxStart + 1 )
490 {
491
492 patIdxStart++;
493 continue;
494 }
495
496
497 int patLength = ( patIdxTmp - patIdxStart - 1 );
498 int strLength = ( strIdxEnd - strIdxStart + 1 );
499 int foundIdx = -1;
500 strLoop:
501 for ( int i = 0; i <= strLength - patLength; i++ )
502 {
503 for ( int j = 0; j < patLength; j++ )
504 {
505 ch = patArr[patIdxStart + j + 1];
506 if ( ch != '?' && !equals( ch, strArr[strIdxStart + i + j], isCaseSensitive ) )
507 {
508 continue strLoop;
509 }
510 }
511
512 foundIdx = strIdxStart + i;
513 break;
514 }
515
516 if ( foundIdx == -1 )
517 {
518 return false;
519 }
520
521 patIdxStart = patIdxTmp;
522 strIdxStart = foundIdx + patLength;
523 }
524
525
526
527 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
528 {
529 if ( patArr[i] != '*' )
530 {
531 return false;
532 }
533 }
534 return true;
535 }
536
537
538
539
540 private static boolean equals( char c1, char c2, boolean isCaseSensitive )
541 {
542 if ( c1 == c2 )
543 {
544 return true;
545 }
546 if ( !isCaseSensitive )
547 {
548
549 if ( Character.toUpperCase( c1 ) == Character.toUpperCase( c2 )
550 || Character.toLowerCase( c1 ) == Character.toLowerCase( c2 ) )
551 {
552 return true;
553 }
554 }
555 return false;
556 }
557
558
559
560
561
562
563
564
565
566 private static List<String> tokenizePath( String path, String separator )
567 {
568 List<String> ret = new ArrayList<String>();
569 StringTokenizer st = new StringTokenizer( path, separator );
570 while ( st.hasMoreTokens() )
571 {
572 ret.add( st.nextToken() );
573 }
574 return ret;
575 }
576
577
578 @SuppressWarnings("SimplifiableIfStatement")
579 static boolean matchAntPathPatternStart( @Nonnull MatchPattern pattern,
580 @Nonnull String str,
581 @Nonnull String separator,
582 boolean isCaseSensitive )
583 {
584 if ( separatorPatternStartSlashMismatch( pattern, str, separator ) )
585 {
586 return false;
587 }
588
589 return matchAntPathPatternStart( pattern.getTokenizedPathString(), str, separator, isCaseSensitive );
590 }
591
592 private static String[] tokenizePathToString( @Nonnull String path, @Nonnull String separator )
593 {
594 List<String> ret = new ArrayList<String>();
595 StringTokenizer st = new StringTokenizer( path, separator );
596 while ( st.hasMoreTokens() )
597 {
598 ret.add( st.nextToken() );
599 }
600 return ret.toArray( new String[ret.size()] );
601 }
602
603 private static boolean matchAntPathPatternStart( @Nonnull String[] patDirs,
604 @Nonnull String str,
605 @Nonnull String separator,
606 boolean isCaseSensitive )
607 {
608 String[] strDirs = tokenizePathToString( str, separator );
609 return matchAntPathPatternStart( patDirs, strDirs, isCaseSensitive );
610 }
611
612 private static boolean matchAntPathPatternStart( @Nonnull String[] patDirs,
613 @Nonnull String[] tokenizedFileName,
614 boolean isCaseSensitive )
615 {
616
617 int patIdxStart = 0;
618 int patIdxEnd = patDirs.length - 1;
619 int strIdxStart = 0;
620 int strIdxEnd = tokenizedFileName.length - 1;
621
622
623 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
624 {
625 String patDir = patDirs[patIdxStart];
626 if ( patDir.equals( "**" ) )
627 {
628 break;
629 }
630 if ( !match( patDir, tokenizedFileName[strIdxStart], isCaseSensitive ) )
631 {
632 return false;
633 }
634 patIdxStart++;
635 strIdxStart++;
636 }
637
638 return strIdxStart > strIdxEnd || patIdxStart <= patIdxEnd;
639 }
640
641 private static boolean separatorPatternStartSlashMismatch( @Nonnull MatchPattern matchPattern, @Nonnull String str,
642 @Nonnull String separator )
643 {
644 return str.startsWith( separator ) != matchPattern.startsWith( separator );
645 }
646
647 private static boolean separatorPatternStartSlashMismatch( String pattern, String str, String separator )
648 {
649 return str.startsWith( separator ) != pattern.startsWith( separator );
650 }
651
652
653 static boolean matchAntPathPattern( String[] patDirs, String[] strDirs, boolean isCaseSensitive )
654 {
655 int patIdxStart = 0;
656 int patIdxEnd = patDirs.length - 1;
657 int strIdxStart = 0;
658 int strIdxEnd = strDirs.length - 1;
659
660
661 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
662 {
663 String patDir = patDirs[patIdxStart];
664 if ( patDir.equals( "**" ) )
665 {
666 break;
667 }
668 if ( !match( patDir, strDirs[strIdxStart], isCaseSensitive ) )
669 {
670 return false;
671 }
672 patIdxStart++;
673 strIdxStart++;
674 }
675 if ( strIdxStart > strIdxEnd )
676 {
677
678 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
679 {
680 if ( !patDirs[i].equals( "**" ) )
681 {
682 return false;
683 }
684 }
685 return true;
686 }
687 else
688 {
689 if ( patIdxStart > patIdxEnd )
690 {
691
692 return false;
693 }
694 }
695
696
697 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
698 {
699 String patDir = patDirs[patIdxEnd];
700 if ( patDir.equals( "**" ) )
701 {
702 break;
703 }
704 if ( !match( patDir, strDirs[strIdxEnd], isCaseSensitive ) )
705 {
706 return false;
707 }
708 patIdxEnd--;
709 strIdxEnd--;
710 }
711 if ( strIdxStart > strIdxEnd )
712 {
713
714 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
715 {
716 if ( !patDirs[i].equals( "**" ) )
717 {
718 return false;
719 }
720 }
721 return true;
722 }
723
724 while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
725 {
726 int patIdxTmp = -1;
727 for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
728 {
729 if ( patDirs[i].equals( "**" ) )
730 {
731 patIdxTmp = i;
732 break;
733 }
734 }
735 if ( patIdxTmp == patIdxStart + 1 )
736 {
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806