liquibase-core/src/main/java/liquibase/util/file/FilenameUtils.java
public static boolean wildcardMatch(String filename, String wildcardMatcher, IOCase caseSensitivity) {
        if ((filename == null) && (wildcardMatcher == null)) {
            return true;
        }
        if ((filename == null) || (wildcardMatcher == null)) {
            return false;
        }
        if (caseSensitivity == null) {
            caseSensitivity = IOCase.SENSITIVE;
        }
        filename = caseSensitivity.convertCase(filename);
        wildcardMatcher = caseSensitivity.convertCase(wildcardMatcher);
        String[] wcs = splitOnTokens(wildcardMatcher);
        boolean anyChars = false;
        int textIdx = 0;
        int wcsIdx = 0;
        Stack backtrack = new Stack();

        // loop around a backtrack stack, to handle complex * matching
        do {
            if (!backtrack.isEmpty()) {
                int[] array = (int[]) backtrack.pop();
                wcsIdx = array[0];
                textIdx = array[1];
                anyChars = true;
            }

            // loop whilst tokens and text left to process
            label:
            while (wcsIdx < wcs.length) {
        
                switch (wcs[wcsIdx]) {
                    case "?":
                        // ? so move to next text char
                        textIdx++;
                        anyChars = false;
                
                        break;
                    case "*":
                        // set any chars status
                        anyChars = true;
                        if (wcsIdx == (wcs.length - 1)) {
                            textIdx = filename.length();
                        }
                
                        break;
                    default:
                        // matching text token
                        if (anyChars) {
                            // any chars then try to locate text token
                            textIdx = filename.indexOf(wcs[wcsIdx], textIdx);
                            if (textIdx == -1) {
                                // token not found
                                break label;
                            }
                            int repeat = filename.indexOf(wcs[wcsIdx], textIdx + 1);
                            if (repeat >= 0) {
                                backtrack.push(new int[]{wcsIdx, repeat});
                            }
                        } else {
                            // matching from current position
                            if (!filename.startsWith(wcs[wcsIdx], textIdx)) {
                                // couldnt match token
                                break label;
                            }
                        }
                
                        // matched text token, move text index to end of matched token
                        textIdx += wcs[wcsIdx].length();
                        anyChars = false;
                        break;
                }
        
                wcsIdx++;
            }

            // full match
            if ((wcsIdx == wcs.length) && (textIdx == filename.length())) {
                return true;
            }

        } while (!backtrack.isEmpty());

        return false;
    }
public static boolean wildcardMatch(String filename, String wildcardMatcher, IOCase caseSensitivity) {
        if ((filename == null) && (wildcardMatcher == null)) {
            return true;
        }
        if ((filename == null) || (wildcardMatcher == null)) {
            return false;
        }
        if (caseSensitivity == null) {
            caseSensitivity = IOCase.SENSITIVE;
        }
        filename = caseSensitivity.convertCase(filename);
        wildcardMatcher = caseSensitivity.convertCase(wildcardMatcher);
        String[] wcs = splitOnTokens(wildcardMatcher);
        boolean anyChars = false;
        int textIdx = 0;
        int wcsIdx = 0;
        Stack backtrack = new Stack();

        // loop around a backtrack stack, to handle complex * matching
        do {
            if (!backtrack.isEmpty()) {
                int[] array = (int[]) backtrack.pop();
                wcsIdx = array[0];
                textIdx = array[1];
                anyChars = true;
            }

            // loop whilst tokens and text left to process
            label:
            while (wcsIdx < wcs.length) {
        
                switch (wcs[wcsIdx]) {
                    case "?":
                        // ? so move to next text char
                        textIdx++;
                        anyChars = false;
                
                        break;
                    case "*":
                        // set any chars status
                        anyChars = true;
                        if (wcsIdx == (wcs.length - 1)) {
                            textIdx = filename.length();
                        }
                
                        break;
                    default:
                        // matching text token
                        if (anyChars) {
                            // any chars then try to locate text token
                            textIdx = filename.indexOf(wcs[wcsIdx], textIdx);
                            if (textIdx == -1) {
                                // token not found
                                break label;
                            }
                            int repeat = filename.indexOf(wcs[wcsIdx], textIdx + 1);
                            if (repeat >= 0) {
                                backtrack.push(new int[]{wcsIdx, repeat});
                            }
                        } else {
                            // matching from current position
                            if (!filename.startsWith(wcs[wcsIdx], textIdx)) {
                                // couldnt match token
                                break label;
                            }
                        }
                
                        // matched text token, move text index to end of matched token
                        textIdx += wcs[wcsIdx].length();
                        anyChars = false;
                        break;
                }
        
                wcsIdx++;
            }

            // full match
            if ((wcsIdx == wcs.length) && (textIdx == filename.length())) {
                return true;
            }

        } while (!backtrack.isEmpty());

        return false;
    }
Checks a filename to see if it matches the specified wildcard matcher
allowing control over case-sensitivity.
<p>
The wildcard matcher uses the characters '?' and '*' to represent a
single or multiple wildcard characters.

@param filename  the filename to match on
@param wildcardMatcher  the wildcard string to match against
@param caseSensitivity  what case sensitivity rule to use, null means case-sensitive
@return true if the filename matches the wilcard string
@since Commons IO 1.3