@Override public Matcher mergeNext(Matcher after) { if (after instanceof TrueMatcher) { return this; } Matcher m = (next instanceof TrueMatcher) ? after : SeqMatcher.create(next, after); return new ZeroOrMoreMatcher(repeated, m); }
/** * If the match after a repeated pattern is false, then treat the whole match as false. * For example: {@code ".*$." => "$."}. */ static Matcher zeroOrMoreFalse(Matcher matcher) { if (matcher instanceof ZeroOrMoreMatcher) { ZeroOrMoreMatcher zm = matcher.as(); if (zm.repeated() instanceof FalseMatcher || zm.next() instanceof FalseMatcher) { return zm.next(); } } return matcher; }
/** * If the matcher preceding an OR clause is a repeated any match, move into each branch * of the OR clause. This allows for other optimizations such as conversion to an indexOf * to take effect for each branch. */ static Matcher inlineMatchAnyPrecedingOr(Matcher matcher) { if (matcher instanceof ZeroOrMoreMatcher) { ZeroOrMoreMatcher zm = matcher.as(); if (zm.repeated() instanceof AnyMatcher && zm.next() instanceof OrMatcher) { List<Matcher> matchers = zm.next().<OrMatcher>as().matchers(); List<Matcher> ms = new ArrayList<>(); for (Matcher m : matchers) { ms.add(new ZeroOrMoreMatcher(AnyMatcher.INSTANCE, m)); } return OrMatcher.create(ms); } } return matcher; }
/** * Get the prefix matcher. This is similar to {@link #head(Matcher)} except that it can * reach into character sequences as well as higher level sequences. */ static Matcher getPrefix(Matcher matcher) { if (matcher instanceof SeqMatcher) { List<Matcher> ms = matcher.<SeqMatcher>as().matchers(); return ms.get(0); } else if (matcher instanceof ZeroOrMoreMatcher) { ZeroOrMoreMatcher zm = matcher.as(); return new ZeroOrMoreMatcher(zm.repeated(), TrueMatcher.INSTANCE); } else if (matcher instanceof CharSeqMatcher) { String pattern = matcher.<CharSeqMatcher>as().pattern(); return pattern.isEmpty() ? null : new CharSeqMatcher(pattern.charAt(0)); } else { return matcher; } }
/** * If a start anchor is followed by a repeated any match, then the start anchor can be removed * as it will not change the result ({@code "^.*" => ".*"}). */ static Matcher removeStartFollowedByMatchAny(Matcher matcher) { if (matcher instanceof SeqMatcher) { List<Matcher> matchers = matcher.<SeqMatcher>as().matchers(); if (matchers.size() == 2 && matchers.get(0) instanceof StartMatcher && matchers.get(1) instanceof ZeroOrMoreMatcher) { ZeroOrMoreMatcher zm = matchers.get(1).as(); if (zm.repeated() instanceof AnyMatcher) { return zm; } } } return matcher; }
/** * Get the suffix matcher. This is similar to {@link #tail(Matcher)} except that it intended * to be used with {@link #getPrefix(Matcher)} */ static Matcher getSuffix(Matcher matcher) { if (matcher instanceof SeqMatcher) { List<Matcher> ms = matcher.<SeqMatcher>as().matchers(); return SeqMatcher.create(ms.subList(1, ms.size())); } else if (matcher instanceof ZeroOrMoreMatcher) { ZeroOrMoreMatcher zm = matcher.as(); return zm.next(); } else if (matcher instanceof CharSeqMatcher) { String pattern = matcher.<CharSeqMatcher>as().pattern(); return pattern.length() <= 1 ? TrueMatcher.INSTANCE : new CharSeqMatcher(pattern.substring(1)); } else { return TrueMatcher.INSTANCE; } } }
/** * If the matcher preceding an OR clause is a repeated any match, move into each branch * of the OR clause. This allows for other optimizations such as conversion to an indexOf * to take effect for each branch. */ static Matcher inlineMatchAnyPrecedingOr(Matcher matcher) { if (matcher instanceof ZeroOrMoreMatcher) { ZeroOrMoreMatcher zm = matcher.as(); if (zm.repeated() instanceof AnyMatcher && zm.next() instanceof OrMatcher) { List<Matcher> matchers = zm.next().<OrMatcher>as().matchers(); List<Matcher> ms = new ArrayList<>(); for (Matcher m : matchers) { ms.add(new ZeroOrMoreMatcher(AnyMatcher.INSTANCE, m)); } return OrMatcher.create(ms); } } return matcher; }
/** * Get the prefix matcher. This is similar to {@link #head(Matcher)} except that it can * reach into character sequences as well as higher level sequences. */ static Matcher getPrefix(Matcher matcher) { if (matcher instanceof SeqMatcher) { List<Matcher> ms = matcher.<SeqMatcher>as().matchers(); return ms.get(0); } else if (matcher instanceof ZeroOrMoreMatcher) { ZeroOrMoreMatcher zm = matcher.as(); return new ZeroOrMoreMatcher(zm.repeated(), TrueMatcher.INSTANCE); } else if (matcher instanceof CharSeqMatcher) { String pattern = matcher.<CharSeqMatcher>as().pattern(); return pattern.isEmpty() ? null : new CharSeqMatcher(pattern.charAt(0)); } else { return matcher; } }
/** * If a start anchor is followed by a repeated any match, then the start anchor can be removed * as it will not change the result ({@code "^.*" => ".*"}). */ static Matcher removeStartFollowedByMatchAny(Matcher matcher) { if (matcher instanceof SeqMatcher) { List<Matcher> matchers = matcher.<SeqMatcher>as().matchers(); if (matchers.size() == 2 && matchers.get(0) instanceof StartMatcher && matchers.get(1) instanceof ZeroOrMoreMatcher) { ZeroOrMoreMatcher zm = matchers.get(1).as(); if (zm.repeated() instanceof AnyMatcher) { return zm; } } } return matcher; }
/** * Get the suffix matcher. This is similar to {@link #tail(Matcher)} except that it intended * to be used with {@link #getPrefix(Matcher)} */ static Matcher getSuffix(Matcher matcher) { if (matcher instanceof SeqMatcher) { List<Matcher> ms = matcher.<SeqMatcher>as().matchers(); return SeqMatcher.create(ms.subList(1, ms.size())); } else if (matcher instanceof ZeroOrMoreMatcher) { ZeroOrMoreMatcher zm = matcher.as(); return zm.next(); } else if (matcher instanceof CharSeqMatcher) { String pattern = matcher.<CharSeqMatcher>as().pattern(); return pattern.length() <= 1 ? TrueMatcher.INSTANCE : new CharSeqMatcher(pattern.substring(1)); } else { return TrueMatcher.INSTANCE; } } }
/** * Remove match any pattern at the end, e.g., ({@code "foo.*$" => "foo"}). */ static Matcher removeTrailingMatchAny(Matcher matcher) { if (matcher instanceof ZeroOrMoreMatcher) { ZeroOrMoreMatcher zm = matcher.as(); boolean atEnd = zm.next() instanceof TrueMatcher || zm.next() instanceof EndMatcher; if (atEnd && zm.repeated() instanceof AnyMatcher) { return TrueMatcher.INSTANCE; } } return matcher; }
@Override public Matcher mergeNext(Matcher after) { if (after instanceof TrueMatcher) { return this; } Matcher m = (next instanceof TrueMatcher) ? after : SeqMatcher.create(next, after); return new ZeroOrMoreMatcher(repeated, m); }
/** * Remove match any pattern at the end, e.g., ({@code "foo.*$" => "foo"}). */ static Matcher removeTrailingMatchAny(Matcher matcher) { if (matcher instanceof ZeroOrMoreMatcher) { ZeroOrMoreMatcher zm = matcher.as(); boolean atEnd = zm.next() instanceof TrueMatcher || zm.next() instanceof EndMatcher; if (atEnd && zm.repeated() instanceof AnyMatcher) { return TrueMatcher.INSTANCE; } } return matcher; }
@Override public Matcher rewriteEnd(Function<Matcher, Matcher> f) { return f.apply(new ZeroOrMoreMatcher(repeated, next.rewriteEnd(f))); }
/** * If the match after a repeated pattern is false, then treat the whole match as false. * For example: {@code ".*$." => "$."}. */ static Matcher zeroOrMoreFalse(Matcher matcher) { if (matcher instanceof ZeroOrMoreMatcher) { ZeroOrMoreMatcher zm = matcher.as(); if (zm.repeated() instanceof FalseMatcher || zm.next() instanceof FalseMatcher) { return zm.next(); } } return matcher; }
@Override public Matcher rewriteEnd(Function<Matcher, Matcher> f) { return f.apply(new ZeroOrMoreMatcher(repeated, next.rewriteEnd(f))); }
/** * Zero or more start anchors is the same as not being anchored by the start. */ static Matcher removeRepeatedStart(Matcher matcher) { if (matcher instanceof ZeroOrMoreMatcher) { ZeroOrMoreMatcher zm = matcher.as(); if (zm.repeated() instanceof StartMatcher) { return zm.next(); } } return matcher; }
@Override public Matcher rewrite(Function<Matcher, Matcher> f) { return f.apply(new ZeroOrMoreMatcher(repeated.rewrite(f), next.rewrite(f))); }
/** * Zero or more start anchors is the same as not being anchored by the start. */ static Matcher removeRepeatedStart(Matcher matcher) { if (matcher instanceof ZeroOrMoreMatcher) { ZeroOrMoreMatcher zm = matcher.as(); if (zm.repeated() instanceof StartMatcher) { return zm.next(); } } return matcher; }
@Override public Matcher rewrite(Function<Matcher, Matcher> f) { return f.apply(new ZeroOrMoreMatcher(repeated.rewrite(f), next.rewrite(f))); }