/* * Copyright 2023-2024 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 * * https://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 xyz.zhouxy.plusone.commons.util; import java.util.Arrays; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * 封装一些常用的正则操作,并可以缓存 {@link Pattern} 实例以复用(最多缓存大概 256 个)。 * * @author ZhouXY * */ public final class RegexTools { private static final int DEFAULT_CACHE_INITIAL_CAPACITY = 64; private static final int MAX_CACHE_SIZE = 256; private static final Map PATTERN_CACHE = new ConcurrentHashMap<>(DEFAULT_CACHE_INITIAL_CAPACITY); /** * 获取 {@link Pattern} 实例。 * * @param pattern 正则表达式 * @param cachePattern 是否缓存 {@link Pattern} 实例 * @return {@link Pattern} 实例 */ public static Pattern getPattern(final String pattern, final boolean cachePattern) { AssertTools.checkNotNull(pattern); return cachePattern ? cacheAndGetPatternInternal(pattern) : getPatternInternal(pattern); } /** * 获取 {@link Pattern} 实例,不缓存。 * * @param pattern 正则表达式 * @return {@link Pattern} 实例 */ public static Pattern getPattern(final String pattern) { AssertTools.checkNotNull(pattern); return getPatternInternal(pattern); } /** * 将各个正则表达式转为 {@link Pattern} 实例。 * * @param patterns 正则表达式 * @param cachePattern 是否缓存 {@link Pattern} 实例 * @return {@link Pattern} 实例数组 */ public static Pattern[] getPatterns(final String[] patterns, final boolean cachePattern) { AssertTools.checkNotNull(patterns); AssertTools.checkArgument(allNotNull(patterns)); return cachePattern ? cacheAndGetPatternsInternal(patterns) : getPatternsInternal(patterns); } /** * 将各个正则表达式转为 {@link Pattern} 实例,不缓存。 * * @param patterns 正则表达式 * @return {@link Pattern} 实例数组 */ public static Pattern[] getPatterns(final String[] patterns) { AssertTools.checkNotNull(patterns); AssertTools.checkArgument(allNotNull(patterns)); return getPatternsInternal(patterns); } /** * 判断 {@code input} 是否匹配 {@code pattern}。 * * @param input 输入 * @param pattern 正则 * @return 判断结果 */ public static boolean matches(@Nullable final CharSequence input, final Pattern pattern) { AssertTools.checkNotNull(pattern); return matchesInternal(input, pattern); } /** * 判断 {@code input} 是否匹配 {@code patterns} 中的一个。 * * @param input 输入 * @param patterns 正则 * @return 判断结果 */ public static boolean matchesOne(@Nullable final CharSequence input, final Pattern[] patterns) { AssertTools.checkNotNull(patterns); AssertTools.checkArgument(allNotNull(patterns)); return matchesOneInternal(input, patterns); } /** * 判断 {@code input} 是否匹配全部正则。 * * @param input 输入 * @param patterns 正则 * @return 判断结果 */ public static boolean matchesAll(@Nullable final CharSequence input, final Pattern[] patterns) { AssertTools.checkNotNull(patterns); AssertTools.checkArgument(allNotNull(patterns)); return matchesAllInternal(input, patterns); } /** * 判断 {@code input} 是否匹配 {@code pattern}。 * * @param input 输入 * @param pattern 正则表达式 * @param cachePattern 是否缓存 {@link Pattern} 实例 * @return 判断结果 */ public static boolean matches(@Nullable final CharSequence input, final String pattern, final boolean cachePattern) { AssertTools.checkNotNull(pattern); Pattern p = cachePattern ? cacheAndGetPatternInternal(pattern) : getPatternInternal(pattern); return matchesInternal(input, p); } /** * 判断 {@code input} 是否匹配 {@code pattern}。不缓存 {@link Pattern} 实例。 * * @param input 输入 * @param pattern 正则表达式 * @return 判断结果 */ public static boolean matches(@Nullable final CharSequence input, final String pattern) { AssertTools.checkNotNull(pattern); return matchesInternal(input, getPatternInternal(pattern)); } /** * 判断 {@code input} 是否匹配 {@code patterns} 中的一个。 * * @param input 输入 * @param patterns 正则表达式 * @param cachePattern 是否缓存 {@link Pattern} 实例 * @return 判断结果 */ public static boolean matchesOne(@Nullable final CharSequence input, final String[] patterns, final boolean cachePattern) { AssertTools.checkNotNull(patterns); AssertTools.checkArgument(allNotNull(patterns)); final Pattern[] patternSet = cachePattern ? cacheAndGetPatternsInternal(patterns) : getPatternsInternal(patterns); return matchesOneInternal(input, patternSet); } /** * 判断 {@code input} 是否匹配 {@code patterns} 中的一个。不缓存 {@link Pattern} 实例。 * * @param input 输入 * @param patterns 正则表达式 * @return 判断结果 */ public static boolean matchesOne(@Nullable final CharSequence input, final String[] patterns) { AssertTools.checkNotNull(patterns); AssertTools.checkArgument(allNotNull(patterns)); final Pattern[] patternSet = getPatternsInternal(patterns); return matchesOneInternal(input, patternSet); } /** * 判断 {@code input} 是否匹配全部正则。 * * @param input 输入 * @param patterns 正则表达式 * @param cachePattern 是否缓存 {@link Pattern} 实例 * @return 判断结果 */ public static boolean matchesAll(@Nullable final CharSequence input, final String[] patterns, final boolean cachePattern) { AssertTools.checkNotNull(patterns); AssertTools.checkArgument(allNotNull(patterns)); final Pattern[] patternSet = cachePattern ? cacheAndGetPatternsInternal(patterns) : getPatternsInternal(patterns); return matchesAllInternal(input, patternSet); } /** * 判断 {@code input} 是否匹配全部正则。不缓存 {@link Pattern} 实例。 * * @param input 输入 * @param patterns 正则表达式 * @return 判断结果 */ public static boolean matchesAll(@Nullable final CharSequence input, final String[] patterns) { AssertTools.checkNotNull(patterns); AssertTools.checkArgument(allNotNull(patterns)); final Pattern[] patternSet = getPatternsInternal(patterns); return matchesAllInternal(input, patternSet); } /** * 生成 Matcher。 * * @param input 输入 * @param pattern 正则 * @return 结果 */ public static Matcher getMatcher(final CharSequence input, final Pattern pattern) { AssertTools.checkNotNull(input); AssertTools.checkNotNull(pattern); return pattern.matcher(input); } /** * 生成 Matcher。 * * @param input 输入 * @param pattern 正则表达式 * @param cachePattern 是否缓存 {@link Pattern} 实例 * @return 结果 */ public static Matcher getMatcher(final CharSequence input, final String pattern, boolean cachePattern) { AssertTools.checkNotNull(input); AssertTools.checkNotNull(pattern); final Pattern p = cachePattern ? cacheAndGetPatternInternal(pattern) : getPatternInternal(pattern); return p.matcher(input); } /** * 生成 Matcher。不缓存 {@link Pattern} 实例。 * * @param input 输入 * @param pattern 正则表达式 * @return 结果 */ public static Matcher getMatcher(final CharSequence input, final String pattern) { AssertTools.checkNotNull(input); AssertTools.checkNotNull(pattern); return getPatternInternal(pattern).matcher(input); } // ========== internal methods ========== /** * 获取 {@link Pattern} 实例。 * * @param pattern 正则表达式 * @return {@link Pattern} 实例 */ @Nonnull private static Pattern cacheAndGetPatternInternal(@Nonnull final String pattern) { if (PATTERN_CACHE.size() < MAX_CACHE_SIZE) { return PATTERN_CACHE.computeIfAbsent(pattern, Pattern::compile); } Pattern result = PATTERN_CACHE.get(pattern); if (result != null) { return result; } return Pattern.compile(pattern); } /** * 获取 {@link Pattern} 实例,不缓存。 * * @param pattern 正则表达式 * @return {@link Pattern} 实例 */ @Nonnull private static Pattern getPatternInternal(@Nonnull final String pattern) { Pattern result = PATTERN_CACHE.get(pattern); if (result == null) { result = Pattern.compile(pattern); } return result; } /** * 将各个正则表达式转为 {@link Pattern} 实例。 * * @param patterns 正则表达式 * @return {@link Pattern} 实例数组 */ @Nonnull private static Pattern[] cacheAndGetPatternsInternal(@Nonnull final String[] patterns) { return Arrays.stream(patterns) .map(RegexTools::cacheAndGetPatternInternal) .toArray(Pattern[]::new); } /** * 将各个正则表达式转为 {@link Pattern} 实例。 * * @param patterns 正则表达式 * @return {@link Pattern} 实例数组 */ @Nonnull private static Pattern[] getPatternsInternal(@Nonnull final String[] patterns) { return Arrays.stream(patterns) .map(RegexTools::getPatternInternal) .toArray(Pattern[]::new); } /** * 判断 {@code input} 是否匹配 {@code pattern}。 * * @param input 输入 * @param pattern 正则 * @return 判断结果 */ private static boolean matchesInternal(@Nullable final CharSequence input, @Nonnull final Pattern pattern) { return input != null && pattern.matcher(input).matches(); } private static boolean matchesOneInternal(@Nullable final CharSequence input, @Nonnull final Pattern[] patterns) { return input != null && Arrays.stream(patterns) .anyMatch(pattern -> pattern.matcher(input).matches()); } private static boolean matchesAllInternal(@Nullable final CharSequence input, @Nonnull final Pattern[] patterns) { return input != null && Arrays.stream(patterns) .allMatch(pattern -> pattern.matcher(input).matches()); } private static boolean allNotNull(T[] array) { return Arrays.stream(array).allMatch(Objects::nonNull); } private RegexTools() { // 不允许实例化 throw new IllegalStateException("Utility class"); } }