package net.sf.expectit.matcher; /* * #%L * ExpectIt * %% * Copyright (C) 2014 Alexey Gavrilov and contributors * %% * 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. * #L% */ import static net.sf.expectit.matcher.SimpleResult.failure; import static net.sf.expectit.matcher.SimpleResult.success; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; import net.sf.expectit.MultiResult; import net.sf.expectit.Result; /** * The matcher factory. * * @author Alexey Gavrilov */ public final class Matchers { /** * Don't expect to create an instance of this class. */ private Matchers() { } /** * Creates a matcher of {@code String} that matches when examined input fully <b>matches</b> * the given regular * expression. * <p/> * This method simply compiles the given pattern to a {@link java.util.regex.Pattern} and * passes it to the * {@link #matches(java.util.regex.Pattern)} method. * * @param pattern the pattern that represents regular expression * @return the match result * @see #regexp(java.util.regex.Pattern) */ public static Matcher<Result> matches(String pattern) { return matches(Pattern.compile(pattern)); } /** * Creates a matcher of {@code Pattern} that matches when examined input fully <b>matches</b> * the given regular * expression. The operation is equivalent to the {@link java.util.regex.Matcher#matches()} * method. * <p/> * The returning {@code Result} implements the {@link java.util.regex.MatchResult} methods to * query the results * of the regular expression match. * <p/> * If the match succeeded, the input buffer is cleared and {@link net.sf.expectit * .Result#getBefore()} returns * an empty string. * * @param pattern the representation of a regular expression * @return the match result */ public static Matcher<Result> matches(Pattern pattern) { return new RegexpMatcher(pattern, false); } /** * Creates a matcher of {@code String} that matches when examined input <b>contains</b> the * given regular * expression. * <p/> * This method simply compiles the given string to a {@link java.util.regex.Pattern} and * passes it to the * {@link #regexp(java.util.regex.Pattern)} method. * * @param pattern the string that represents regular expression * @return the match result * @see #regexp(java.util.regex.Pattern) */ public static Matcher<Result> regexp(String pattern) { return regexp(Pattern.compile(pattern)); } /** * Creates a matcher of {@code Pattern} that matches when examined input <b>contains</b> the * given regular * expression. The operation is equivalent to the {@link java.util.regex.Matcher#find()} method. * <p/> * The returning {@code Result} implements the {@link java.util.regex.MatchResult} methods to * query the results * of the regular expression match. * <p/> * If the match succeeded, the input buffer is updated: the input part from the beginning * until the end * position of the match is removed. * * @param pattern the representation of a regular expression * @return the match result */ public static Matcher<Result> regexp(Pattern pattern) { return new RegexpMatcher(pattern, true); } /** * Creates a matcher of {@code String} that matches when examined input contains the given * substring. * <p/> * The returning {@code Result} has no groups except the one with {@code 0} index which * represents the exact * string match. * <p/> * If the match succeeded, the input buffer is updated: the input part from the beginning * until the end * position of the match is removed. * * @param string the string to search for * @return the match result */ public static Matcher<Result> contains(final String string) { return new Matcher<Result>() { @Override public Result matches(String input, boolean isEof) { int pos = input.indexOf(string); return pos != -1 ? success(input, input.substring(0, pos), string) : failure(input, false); } @Override public String toString() { return generateToString("contains", string); } }; } /** * Creates a matcher that matches if the examined input matches <b>all</b> of the specified * matchers. This method * evaluates all the matchers regardless intermediate results. * <p/> * The match result represents a combination of all match operations. If succeeded, * the match result with the * greatest end position is selected to implement the result {@link Result} instance returned * by this method. * If the result is negative, then the one which fails first is returned. * <p/> * If several matchers the have same end position, then the result from the one with the * smaller argument index is * returned. * * @param matchers the vararg array of the matchers * @return the multi match result */ public static Matcher<MultiResult> allOf(final Matcher<?>... matchers) { checkNotEmpty(matchers); return new MultiMatcher(true, matchers); } /** * Creates a matcher that matches if the examined input matches <b>any</b> of the specified * matchers. * <p/> * The match result represents a combination of any match operations. If succeeded, * the match result with the * greatest end position is selected to implement the result {@link Result} instance returned * by this method. * <p/> * If several matchers have the same end position, then the result from the one with the * smaller argument index is * returned. * * @param matchers the vararg array of the matchers * @return the multi match result */ public static Matcher<MultiResult> anyOf(final Matcher<?>... matchers) { checkNotEmpty(matchers); return new MultiMatcher(false, matchers); } /** * Creates a matcher that matches if input reaches the end of stream. * <p/> * If succeeded, the {@link net.sf.expectit.Result#getBefore()} will return the entire input * buffer, and * the {@link net.sf.expectit.Result#group()} returns an empty string. * * @return the matcher */ public static Matcher<Result> eof() { return new Matcher<Result>() { @Override public Result matches(String input, boolean isEof) { return isEof ? success(input, input, "") : failure(input, false); } @Override public String toString() { return "eof"; } }; } /** * Creates a matcher that matches if the given {@code matcher} matches the {@code number} of * times. * <p/> * The match result represents a combination of all the performed match operations. If * succeeded, the result * with the greatest end position is returned. * <p/> * * @param number the number of times which the given {@code matcher} must match the input * @param matcher the matcher * @return the result */ public static Matcher<MultiResult> times(final int number, final Matcher<?> matcher) { Matcher<?>[] matchers = new Matcher[number]; Arrays.fill(matchers, matcher); return sequence(matchers); } /** * Matches the given matchers one by one. Every successful matches updates the internal * buffer. The consequent * match operation is performed after the previous match has succeeded. * * @param matchers the collection of matchers. * @return the matcher. */ public static Matcher<MultiResult> sequence(final Matcher<?>... matchers) { checkNotEmpty(matchers); return new Matcher<MultiResult>() { @Override public MultiResult matches(String input, boolean isEof) { int matchCount = 0; Result[] results = new Result[matchers.length]; Arrays.fill(results, failure(input, false)); int beginIndex = 0; for (int i = 0; i < matchers.length; i++) { Result result = matchers[i].matches(input.substring(beginIndex), isEof); if (result.isSuccessful()) { beginIndex += result.end(); results[i] = result; if (++matchCount == matchers.length) { String group = result.group(); Result finalResult = new SimpleResult( true, input, input.substring(0, beginIndex - group.length()), group, result.canStopMatching()); return new MultiResultImpl(finalResult, Arrays.asList(results)); } } else { break; } } final List<Result> resultList = Arrays.asList(results); boolean canStopMatching = MultiResultImpl.canStopMatching(resultList); return new MultiResultImpl(failure(input, canStopMatching), resultList); } @Override public String toString() { return String.format("sequence(%s)", MultiMatcher.matchersToString(matchers)); } }; } /** * Creates a matcher that matches when at least one character exists in the input buffer. * <p/> * If the result is successful, the {@link net.sf.expectit.Result#getBefore()} returns an * empty string, the * {@link net.sf.expectit.Result#group()} returns the entire input buffer. * * @return the result */ public static Matcher<Result> anyString() { return new Matcher<Result>() { @Override public Result matches(String input, boolean isEof) { return input.length() > 0 ? success(input, "", input) : failure(input, false); } @Override public String toString() { return "anyString"; } }; } private static void checkNotEmpty(Matcher<?>[] matchers) { if (matchers.length == 0) { throw new IllegalArgumentException("Matchers cannot be empty"); } } static String generateToString(String name, Object parameter) { return String.format("%s('%s')", name, parameter); } /** * Creates a matcher that matches when the given string is equal to the * input buffer contents. * <p/> * If the result is successful, the {@link net.sf.expectit.Result#getBefore()} returns an * empty string, the {@link net.sf.expectit.Result#group()} returns the entire input buffer. * * @param exact the string to match. * @return the result. */ public static Matcher<Result> exact(final String exact) { return new Matcher<Result>() { @Override public Result matches(String input, boolean isEof) { return exact.equals(input) ? success(input, "", input) : failure(input, input.length() > exact.length()); } @Override public String toString() { return generateToString("exact", exact); } }; } /** * Creates a matcher that matches when the input buffer starts with the given string. * <p/> * If the result is successful, the {@link net.sf.expectit.Result#getBefore()} returns an * empty string, the {@link net.sf.expectit.Result#group()} returns the given string. * * @param prefix the prefix string to match. * @return the result. */ public static Matcher<Result> startsWith(final String prefix) { return new Matcher<Result>() { @Override public Result matches(String input, boolean isEof) { return input.startsWith(prefix) ? success(input, "", prefix) : failure(input, input.length() > prefix.length()); } @Override public String toString() { return generateToString("startsWith", prefix); } }; } }