Untitled

mail@pastecode.io avatar
unknown
java
a year ago
3.9 kB
6
Indexable
Never
package pl.ds.util;

import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Stream;

import static java.util.Objects.isNull;

public final class StringUtil {

    private StringUtil() {
        // no instances
    }

    /**
     * <p>Checks if a {@code string} contains a {@code pattern}.</p>
     *
     * <p>A pattern may contain single or multiple wildcard characters {@code *}.
     * Each occurrence of {@code *} in the {@code pattern} means that it can be a match for
     * zero or more characters of the {@code string}.</p>
     *
     * <p>Asterisk (*) is considered as a regular character, if it is preceded by a backslash (\)
     * in a pattern. Backslash (\) is considered as a regular character in all cases other
     * than preceding the asterisk (*).</p>
     *
     * <p>Examples:</p>
     * <pre>
     * StringUtils.contains(null, *) = false
     * StringUtils.contains(*, null) = false
     * StringUtils.contains("", "") = true
     * StringUtils.contains("abc", "") = true
     * StringUtils.contains("abc", "a") = true
     * StringUtils.contains("abc", "a*") = true
     * StringUtils.contains("a*c", "a\*") = true
     * StringUtils.contains("a*c", "abc") = false
     * StringUtils.contains("abc", "A") = false
     * StringUtils.contains("abc", "abcd") = false
     * </pre>
     *
     * @param string  string to check
     * @param pattern pattern to search in a string
     * @return true if the {@code string} contains a {@code pattern}, false otherwise.
     */
    public static boolean contains(String string, String pattern) {
        if (isNull(string) || isNull(pattern) || pattern.length() > string.length()) {
            return false;
        }
        if (pattern.isEmpty()) {
            return true;
        }

        StringCharacterIterator stringIterator = new StringCharacterIterator(string);
        StringCharacterIterator patternIterator = new StringCharacterIterator(pattern);
        Collection<PatternComparator> comparators = new ArrayList<>();
        do {
            char stringChar = stringIterator.current();
            if (stringChar == patternIterator.first()) {
                comparators.add(new PatternComparator(pattern));
            }
            if (comparators.stream().map(comparator -> comparator.pushCharacter(stringChar)).anyMatch(PatternComparator::isSuccess)) {
                return true;
            } else {
                comparators.removeIf(PatternComparator::isFailed);
            }
        } while (stringIterator.next() != CharacterIterator.DONE);


        return false;
    }

    private static class PatternComparator {
        private Status status;

        private enum Status {IN_PROGRESS, FAILED, SUCCESS;}

        private final StringCharacterIterator patternIterator;

        public PatternComparator(String pattern) {
            patternIterator = new StringCharacterIterator(pattern);
            status = Status.IN_PROGRESS;
        }
        private PatternComparator pushCharacter(char character) {
            if (status != Status.IN_PROGRESS) {
                return this;
            }

            if (patternIterator.current() == character) {
                if (endOfPattern()) {
                    status = Status.SUCCESS;
                } else {
                    patternIterator.next();
                    status = Status.IN_PROGRESS;
                }
            } else {
                status = Status.FAILED;
            }
            return this;
        }

        public boolean isSuccess() {
            return status == Status.SUCCESS;
        }

        public boolean isFailed() {
            return status == Status.FAILED;
        }

        private boolean endOfPattern() {
            return patternIterator.getIndex() + 1 == patternIterator.getEndIndex();
        }
    }
}