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
610 int patIdxStart = 0;
611 int patIdxEnd = patDirs.length - 1;
612 int strIdxStart = 0;
613 int strIdxEnd = strDirs.length - 1;
614
615
616 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
617 {
618 String patDir = patDirs[patIdxStart];
619 if ( patDir.equals( "**" ) )
620 {
621 break;
622 }
623 if ( !match( patDir, strDirs[strIdxStart], isCaseSensitive ) )
624 {
625 return false;
626 }
627 patIdxStart++;
628 strIdxStart++;
629 }
630
631 return strIdxStart > strIdxEnd || patIdxStart <= patIdxEnd;
632 }
633
634 private static boolean separatorPatternStartSlashMismatch( @Nonnull MatchPattern matchPattern, @Nonnull String str,
635 @Nonnull String separator )
636 {
637 return str.startsWith( separator ) != matchPattern.startsWith( separator );
638 }
639
640 private static boolean separatorPatternStartSlashMismatch( String pattern, String str, String separator )
641 {
642 return str.startsWith( separator ) != pattern.startsWith( separator );
643 }
644
645
646 static boolean matchAntPathPattern( String[] patDirs, String[] strDirs, boolean isCaseSensitive )
647 {
648 int patIdxStart = 0;
649 int patIdxEnd = patDirs.length - 1;
650 int strIdxStart = 0;
651 int strIdxEnd = strDirs.length - 1;
652
653
654 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
655 {
656 String patDir = patDirs[patIdxStart];
657 if ( patDir.equals( "**" ) )
658 {
659 break;
660 }
661 if ( !match( patDir, strDirs[strIdxStart], isCaseSensitive ) )
662 {
663 return false;
664 }
665 patIdxStart++;
666 strIdxStart++;
667 }
668 if ( strIdxStart > strIdxEnd )
669 {
670
671 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
672 {
673 if ( !patDirs[i].equals( "**" ) )
674 {
675 return false;
676 }
677 }
678 return true;
679 }
680 else
681 {
682 if ( patIdxStart > patIdxEnd )
683 {
684
685 return false;
686 }
687 }
688
689
690 while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd )
691 {
692 String patDir = patDirs[patIdxEnd];
693 if ( patDir.equals( "**" ) )
694 {
695 break;
696 }
697 if ( !match( patDir, strDirs[strIdxEnd], isCaseSensitive ) )
698 {
699 return false;
700 }
701 patIdxEnd--;
702 strIdxEnd--;
703 }
704 if ( strIdxStart > strIdxEnd )
705 {
706
707 for ( int i = patIdxStart; i <= patIdxEnd; i++ )
708 {
709 if ( !patDirs[i].equals( "**" ) )
710 {
711 return false;
712 }
713 }
714 return true;
715 }
716
717 while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd )
718 {
719 int patIdxTmp = -1;
720 for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ )
721 {
722 if ( patDirs[i].equals( "**" ) )
723 {
724 patIdxTmp = i;
725 break;
726 }
727 }
728 if ( patIdxTmp == patIdxStart + 1 )
729 {
730
731
732
733
734
735
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