gradle-master/subprojects/files/src/main/java/org/gradle/api/internal/file/pattern/PatternStepFactory.java
/*
 * Copyright 2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gradle.api.internal.file.pattern;

public class PatternStepFactory {
    private static final AnyWildcardPatternStep ANY_WILDCARD_PATTERN_STEP = new AnyWildcardPatternStep();

    public static PatternStep getStep(String source, boolean caseSensitive) {
        if (source.length() == 0) {
            return new FixedPatternStep(source, caseSensitive);
        }

        // Here, we try to avoid using the reg exp backed pattern step, as it is expensive in terms of performance and heap usage.
        // There are several special cases we handle here:
        // 1. '*'
        // 2. '*' <literal>
        // 3. <literal> '*'
        // 4. <literal> '*' <literal>
        // 5. <literal>
        // Everything else uses a reg exp.

        // Not empty: may match any case above

        char ch = source.charAt(0);
        int endPrefixWildcard = 0;
        if (ch == '*') {
            endPrefixWildcard = 1;
            while (endPrefixWildcard < source.length() && source.charAt(endPrefixWildcard) == '*') {
                endPrefixWildcard++;
            }
        }

        if (endPrefixWildcard == source.length()) {
            // Only * characters: matches #1 above
            return ANY_WILDCARD_PATTERN_STEP;
        }

        // Zero or more * characters followed by at least one !*

        int endLiteral = endPrefixWildcard;
        for(; endLiteral < source.length(); endLiteral++) {
            ch = source.charAt(endLiteral);
            if (ch == '?') {
                // No matches - fall back to regexp
                return new RegExpPatternStep(source, caseSensitive);
            }
            if (ch == '*') {
                break;
            }
        }
        if (endLiteral == source.length()) {
            if (endPrefixWildcard == 0) {
                // No wildcards: matches #5 above
                return new FixedPatternStep(source, caseSensitive);
            }
            // One or more '*' followed by one or more non-wildcard: matches #2 above
            return new HasSuffixPatternStep(source.substring(endPrefixWildcard), caseSensitive);
        }

        // Zero or more * characters followed by literal followed by at least one *

        if (endPrefixWildcard > 0) {
            // No matches - fall back to regexp
            return new RegExpPatternStep(source, caseSensitive);
        }

        // literal followed by at least one *

        int endSuffixWildcard = endLiteral;
        for (; endSuffixWildcard < source.length(); endSuffixWildcard++) {
            ch = source.charAt(endSuffixWildcard);
            if (ch != '*') {
                break;
            }
        }

        if (endSuffixWildcard == source.length()) {
            // Literal followed by *: matches #3 above
            return new HasPrefixPatternStep(source.substring(0, endLiteral), caseSensitive);
        }

        for (int i = endSuffixWildcard; i < source.length(); i++) {
            ch = source.charAt(i);
            if (ch == '?' || ch == '*') {
                // No matches - fall back to regexp
                return new RegExpPatternStep(source, caseSensitive);
            }
        }

        // literal followed by * followed by literal: matches #4 above
        return new HasPrefixAndSuffixPatternStep(source.substring(0, endLiteral), source.substring(endSuffixWildcard), caseSensitive);
    }
}
