private boolean recursiveMatch()

in jbatch/src/main/java/org/apache/batchee/container/jsl/GlobPatternMatcherImpl.java [49:149]


    private boolean recursiveMatch(final String toMatch, final String subpattern) {
        final int firstAsterisk = subpattern.indexOf("*");
        final int secondAsterisk = subpattern.indexOf("*", firstAsterisk + 1);
        final int lastAsterisk = subpattern.lastIndexOf("*");

        //
        //  A) If there are no '*'(s), do a non-wildcard (except for ?) match
        //
        if (firstAsterisk == -1) {
            return matchNoAsterisk(toMatch, subpattern);
        }

        //
        //  B) Match off any beginning or end
        //

        // Match any beginning BEFORE the first asterisk
        if (firstAsterisk > 0) {
            final String beginPattern = subpattern.substring(0, firstAsterisk);
            if (toMatch.length() < beginPattern.length()) {
                return false;
            }
            final String beginToMatch = toMatch.substring(0, firstAsterisk);
            if (!matchNoAsterisk(beginToMatch, beginPattern)) {
                return false;
            }
        }

        // This will just be a straight copy if 'firstAsterisk' = 0.
        // Since we're using recursion, try to be a bit performance-sensitive and not do this until necessary.
        String remainingToMatch = toMatch.substring(firstAsterisk);
        //Match any end AFTER the first asterisk
        if (lastAsterisk < subpattern.length() - 1) {
            final String endPattern = subpattern.substring(lastAsterisk + 1);
            if (remainingToMatch.length() < endPattern.length()) {
                return false;
            }
            // Give me a substring the size of 'endPattern' at the end
            final String endToMatch = remainingToMatch.substring(remainingToMatch.length() - endPattern.length(), remainingToMatch.length());
            if (!matchNoAsterisk(endToMatch, endPattern)) {
                return false;
            }
            // Already matched against char at index: 'remainingToMatch.length() - endPattern.length()', so truncate immediately before.
            remainingToMatch = remainingToMatch.substring(0, remainingToMatch.length() - endPattern.length());
        }

        // C) Now I either have:
        //  1)   *
        //  2)   *-xxxxx-*  (All non-'*' chars in the middle
        //  3)   *-xy-*-x-* (At least one '*' in the middle)
        if (firstAsterisk == lastAsterisk) {
            return true;
        } else if (secondAsterisk == lastAsterisk) {
            final String middlePattern = subpattern.substring(firstAsterisk + 1, lastAsterisk);
            if (remainingToMatch.length() < middlePattern.length()) {
                return false;
            } else {
                for (int i = 0; i <= remainingToMatch.length() - middlePattern.length(); i++) {
                    final String matchCandidate = remainingToMatch.substring(i, i + middlePattern.length());
                    if (matchNoAsterisk(matchCandidate, middlePattern)) {
                        return true;
                    }
                }
                return false;
            }
        } else {
            //
            // Now I have to match against sub-sub-pattern *xxx*xy*.  The strategy here is to use recursion.

            // 1. Isolate 'xxx' and store as 'patternBetweenAsterisk1and2'
            final String patternBetweenAsterisk1and2 = subpattern.substring(firstAsterisk + 1, secondAsterisk);

            // 2. Find every place in the string matching 'xxx' and store the remainder as a candidate
            final List<String> subMatchCandidates = new ArrayList<String>();
            for (int i = 0; i <= remainingToMatch.length() - patternBetweenAsterisk1and2.length(); i++) {
                final String matchCandidate = remainingToMatch.substring(i, i + patternBetweenAsterisk1and2.length());
                if (matchNoAsterisk(matchCandidate, patternBetweenAsterisk1and2)) {
                    // Grab the part of the string after the match.
                    String subMatchCandidate = remainingToMatch.substring(i + patternBetweenAsterisk1and2.length());
                    subMatchCandidates.add(subMatchCandidate);
                }
            }

            if (subMatchCandidates.size() == 0) {
                return false;
            }

            // 2. Calculate the new pattern to match against.
            // For "*xxx*xy*" this would be "*xy*".
            // For "*xxx*xy*z*" this would be "*xy*z*"
            final String nestedPattern = subpattern.substring(secondAsterisk, lastAsterisk + 1);  // We want to include the last asterisk.

            // 3. try matching each one
            for (final String candidate : subMatchCandidates) {
                if (recursiveMatch(candidate, nestedPattern)) {
                    return true;
                }
            }
            return false;
        }
    }