1919import org .jspecify .annotations .Nullable ;
2020
2121import java .io .ByteArrayOutputStream ;
22- import java .io .File ;
2322import java .io .IOException ;
2423import java .io .InputStream ;
2524import java .nio .charset .Charset ;
2625import java .nio .charset .StandardCharsets ;
26+ import java .nio .file .Path ;
2727import java .util .Arrays ;
2828import java .util .function .Predicate ;
2929import java .util .regex .Pattern ;
@@ -36,7 +36,6 @@ private StringUtils() {
3636
3737 public static @ Nullable String trimIndentPreserveCRLF (@ Nullable String text ) {
3838 if (text == null ) {
39- //noinspection DataFlowIssue
4039 return null ;
4140 }
4241 return trimIndent ((text .endsWith ("\r \n " ) ? text .substring (0 , text .length () - 2 ) : text )
@@ -227,7 +226,7 @@ public static String capitalize(String value) {
227226 return value ;
228227 }
229228 return Character .toUpperCase (value .charAt (0 )) +
230- value .substring (1 );
229+ value .substring (1 );
231230 }
232231
233232 public static String uncapitalize (String value ) {
@@ -372,58 +371,48 @@ public static String repeat(String s, int count) {
372371 return new String (multiple );
373372 }
374373
375- public static boolean matchesGlob (@ Nullable String value , @ Nullable String globPattern ) {
376- if ("*" .equals (globPattern )) {
374+ /**
375+ * Checks if a given string matches a specified glob pattern. A glob pattern may include
376+ * special characters such as '*' to represent any sequence of characters and '?' to
377+ * represent any single character.
378+ * <p>
379+ * For file path matching, use {@link org.openrewrite.PathUtils#matchesGlob(Path, String)},
380+ * which properly interprets '*' and '**' wildcards for file paths.
381+ *
382+ * @param str the input string to match against the pattern, can be null
383+ * @param pattern the glob pattern to evaluate, can be null
384+ * @return true if the input string matches the glob pattern, false otherwise
385+ * @see org.openrewrite.PathUtils#matchesGlob(Path, String)
386+ */
387+ public static boolean matchesGlob (@ Nullable String str , @ Nullable String pattern ) {
388+ if ("*" .equals (pattern )) {
377389 return true ;
378- }
379- if (globPattern == null ) {
390+ } else if (pattern == null ) {
380391 return false ;
381392 }
382- if (value == null ) {
383- value = "" ;
384- }
385393
386- return matchesGlob (
387- globPattern .replace (wrongFileSeparatorChar , File .separatorChar ),
388- value .replace (wrongFileSeparatorChar , File .separatorChar ),
389- false
390- );
391- }
394+ if (str == null ) {
395+ str = "" ;
396+ }
392397
393- private static final char wrongFileSeparatorChar = File .separatorChar == '/' ? '\\' : '/' ;
398+ if (str .isEmpty () && !pattern .isEmpty ()) {
399+ return allStars (pattern , 0 , pattern .length () - 1 );
400+ } else if (pattern .isEmpty ()) {
401+ return str .isEmpty ();
402+ }
394403
395- private static boolean matchesGlob (String pattern , String str , boolean caseSensitive ) {
396404 int patIdxStart = 0 ;
397405 int patIdxEnd = pattern .length () - 1 ;
398406 int strIdxStart = 0 ;
399407 int strIdxEnd = str .length () - 1 ;
400408
401- if (!pattern .contains ("*" )) {
402- // No '*'s, so we make a shortcut
403- if (patIdxEnd != strIdxEnd ) {
404- return false ; // Pattern and string do not have the same size
405- }
406- for (int i = 0 ; i <= patIdxEnd ; i ++) {
407- char ch = pattern .charAt (i );
408- if (ch != '?' && different (caseSensitive , ch , str .charAt (i ))) {
409- return false ; // Character mismatch
410- }
411- }
412- return true ; // String matches against pattern
413- }
414-
415- if (patIdxEnd == 0 ) {
416- return true ; // Pattern contains only '*', which matches anything
417- }
418-
419409 // Process characters before first star
420410 while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd ) {
421411 char ch = pattern .charAt (patIdxStart );
422412 if (ch == '*' ) {
423413 break ;
424414 }
425- if (ch != '?' &&
426- different (caseSensitive , ch , str .charAt (strIdxStart ))) {
415+ if (ch != '?' && different (ch , str .charAt (strIdxStart ))) {
427416 return false ; // Character mismatch
428417 }
429418 patIdxStart ++;
@@ -444,7 +433,7 @@ private static boolean matchesGlob(String pattern, String str, boolean caseSensi
444433 if (ch == '*' ) {
445434 break ;
446435 }
447- if (ch != '?' && different (caseSensitive , ch , str .charAt (strIdxEnd ))) {
436+ if (ch != '?' && different (ch , str .charAt (strIdxEnd ))) {
448437 return false ; // Character mismatch
449438 }
450439 patIdxEnd --;
@@ -456,7 +445,7 @@ private static boolean matchesGlob(String pattern, String str, boolean caseSensi
456445 return allStars (pattern , patIdxStart , patIdxEnd );
457446 }
458447
459- // process pattern between stars. padIdxStart and patIdxEnd point
448+ // Process pattern between stars. patIdxStart and patIdxEnd point
460449 // always to a '*'.
461450 while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd ) {
462451 int patIdxTmp = -1 ;
@@ -475,18 +464,9 @@ private static boolean matchesGlob(String pattern, String str, boolean caseSensi
475464 // strIdxStart & strIdxEnd
476465 int patLength = (patIdxTmp - patIdxStart - 1 );
477466 int strLength = (strIdxEnd - strIdxStart + 1 );
478- int foundIdx = -1 ;
479- strLoop :
480- for (int i = 0 ; i <= strLength - patLength ; i ++) {
481- for (int j = 0 ; j < patLength ; j ++) {
482- char ch = pattern .charAt (patIdxStart + j + 1 );
483- if (ch != '?' && different (caseSensitive , ch , str .charAt (strIdxStart + i + j ))) {
484- continue strLoop ;
485- }
486- }
487- foundIdx = strIdxStart + i ;
488- break ;
489- }
467+
468+ int foundIdx = findPatternInString (pattern , patIdxStart + 1 , patLength ,
469+ str , strIdxStart , strLength );
490470
491471 if (foundIdx == -1 ) {
492472 return false ;
@@ -500,6 +480,21 @@ private static boolean matchesGlob(String pattern, String str, boolean caseSensi
500480 return allStars (pattern , patIdxStart , patIdxEnd );
501481 }
502482
483+ private static int findPatternInString (String pattern , int patStart , int patLength ,
484+ String str , int strStart , int strLength ) {
485+ strLoop :
486+ for (int i = 0 ; i <= strLength - patLength ; i ++) {
487+ for (int j = 0 ; j < patLength ; j ++) {
488+ char ch = pattern .charAt (patStart + j );
489+ if (ch != '?' && different (ch , str .charAt (strStart + i + j ))) {
490+ continue strLoop ;
491+ }
492+ }
493+ return strStart + i ;
494+ }
495+ return -1 ;
496+ }
497+
503498 private static boolean allStars (String chars , int start , int end ) {
504499 for (int i = start ; i <= end ; ++i ) {
505500 if (chars .charAt (i ) != '*' ) {
@@ -509,10 +504,8 @@ private static boolean allStars(String chars, int start, int end) {
509504 return true ;
510505 }
511506
512- private static boolean different (boolean caseSensitive , char ch , char other ) {
513- return caseSensitive ?
514- ch != other :
515- Character .toUpperCase (ch ) != Character .toUpperCase (other );
507+ private static boolean different (char ch , char other ) {
508+ return Character .toUpperCase (ch ) != Character .toUpperCase (other );
516509 }
517510
518511 public static String indent (String text ) {
0 commit comments