All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dromara.hutool.cron.pattern.parser.PartParser Maven / Gradle / Ivy

There is a newer version: 6.0.0.M3
Show newest version
/*
 * Copyright (c) 2013-2024 Hutool Team and hutool.cn
 *
 * 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.dromara.hutool.cron.pattern.parser;

import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.date.Month;
import org.dromara.hutool.core.date.Week;
import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.split.SplitUtil;
import org.dromara.hutool.cron.CronException;
import org.dromara.hutool.cron.pattern.Part;
import org.dromara.hutool.cron.pattern.matcher.*;

import java.util.ArrayList;
import java.util.List;

/**
 * 定时任务表达式各个部分的解析器,根据{@link Part}指定不同部分,解析为{@link PartMatcher}
* 每个部分支持: *
    *
  • * :表示匹配这个位置所有的时间
  • *
  • ? :表示匹配这个位置任意的时间(与"*"作用一致)
  • *
  • L :表示匹配这个位置允许的最大值
  • *
  • */2 :表示间隔时间,例如在分上,表示每两分钟,同样*可以使用数字列表代替,逗号分隔
  • *
  • 2-8 :表示连续区间,例如在分上,表示2,3,4,5,6,7,8分
  • *
  • 2,3,5,8 :表示列表
  • *
  • wed :表示周别名
  • *
  • jan :表示月别名
  • *
* * @author looly * @since 5.8.0 */ public class PartParser { private final Part part; /** * 创建解析器 * * @param part 对应解析的部分枚举 * @return 解析器 */ public static PartParser of(final Part part) { return new PartParser(part); } /** * 构造 * @param part 对应解析的部分枚举 */ public PartParser(final Part part) { this.part = part; } /** * 将表达式解析为{@link PartMatcher}
*
    *
  • * 或者 ? 返回{@link AlwaysTrueMatcher}
  • *
  • {@link Part#DAY_OF_MONTH} 返回{@link DayOfMonthMatcher}
  • *
  • {@link Part#YEAR} 返回{@link YearValueMatcher}
  • *
  • 其他 返回{@link BoolArrayMatcher}
  • *
* * @param value 表达式 * @return {@link PartMatcher} */ public PartMatcher parse(final String value) { if (isMatchAllStr(value)) { //兼容Quartz的"?"表达式,不会出现互斥情况,与"*"作用相同 return new AlwaysTrueMatcher(); } final List values = parseArray(value); if (values.isEmpty()) { throw new CronException("Invalid part value: [{}]", value); } switch (this.part) { case DAY_OF_MONTH: return new DayOfMonthMatcher(values); case YEAR: return new YearValueMatcher(values); default: return new BoolArrayMatcher(values); } } /** * 处理数组形式表达式
* 处理的形式包括: *
    *
  • a*
  • *
  • a,b,c,d
  • *
* * @param value 子表达式值 * @return 值列表 */ private List parseArray(final String value) { final List values = new ArrayList<>(); final List parts = SplitUtil.split(value, StrUtil.COMMA); for (final String part : parts) { ListUtil.addAllIfNotContains(values, parseStep(part)); } return values; } /** * 处理间隔形式的表达式
* 处理的形式包括: *
    *
  • a*
  • *
  • a/b*/b
  • *
  • a-b/2
  • *
* * @param value 表达式值 * @return List */ private List parseStep(final String value) { final List parts = SplitUtil.split(value, StrUtil.SLASH); final int size = parts.size(); final List results; if (size == 1) {// 普通形式 results = parseRange(value, -1); } else if (size == 2) {// 间隔形式 // issue#I7SMP7,步进不检查范围 final int step = parseNumber(parts.get(1), false); if (step < 1) { throw new CronException("Non positive divisor for field: [{}]", value); } results = parseRange(parts.get(0), step); } else { throw new CronException("Invalid syntax of field: [{}]", value); } return results; } /** * 处理表达式中范围表达式 处理的形式包括: *
    *
  • *
  • *
  • 2
  • *
  • 3-8
  • *
  • 8-3
  • *
  • 3-3
  • *
* * @param value 范围表达式 * @param step 步进 * @return List */ private List parseRange(final String value, int step) { final List results = new ArrayList<>(); // 全部匹配形式 if (value.length() <= 2) { //根据步进的第一个数字确定起始时间,类似于 12/3则从12(秒、分等)开始 int minValue = part.getMin(); if (!isMatchAllStr(value)) { minValue = Math.max(minValue, parseNumber(value, true)); } else { //在全匹配模式下,如果步进不存在,表示步进为1 if (step < 1) { step = 1; } } if (step > 0) { final int maxValue = part.getMax(); if (minValue > maxValue) { throw new CronException("Invalid value {} > {}", minValue, maxValue); } //有步进 for (int i = minValue; i <= maxValue; i += step) { results.add(i); } } else { //固定时间 results.add(minValue); } return results; } //Range模式 final List parts = SplitUtil.split(value, StrUtil.DASHED); final int size = parts.size(); if (size == 1) {// 普通值 final int v1 = parseNumber(value, true); if (step > 0) {//类似 20/2的形式 NumberUtil.appendRange(v1, part.getMax(), step, results); } else { results.add(v1); } } else if (size == 2) {// range值 final int v1 = parseNumber(parts.get(0),true); final int v2 = parseNumber(parts.get(1), true); if (step < 1) { //在range模式下,如果步进不存在,表示步进为1 step = 1; } if (v1 <= v2) {// 正常范围,例如:2-5 // 对于类似3-3这种形式,忽略step,即3-3/2与单值3一致 NumberUtil.appendRange(v1, v2, step, results); } else {// 逆向范围,反选模式,例如:5-2 NumberUtil.appendRange(v1, part.getMax(), step, results); NumberUtil.appendRange(part.getMin(), v2, step, results); } } else { throw new CronException("Invalid syntax of field: [{}]", value); } return results; } /** * 是否为全匹配符
* 全匹配符指 * 或者 ? * * @param value 被检查的值 * @return 是否为全匹配符 * @since 4.1.18 */ private static boolean isMatchAllStr(final String value) { return (1 == value.length()) && ("*".equals(value) || "?".equals(value)); } /** * 解析单个int值,支持别名 * * @param value 被解析的值 * @param checkValue 是否检查值在有效范围内 * @return 解析结果 * @throws CronException 当无效数字或无效别名时抛出 */ private int parseNumber(final String value, final boolean checkValue) throws CronException { int i; try { i = Integer.parseInt(value); } catch (final NumberFormatException ignore) { i = parseAlias(value); } // 支持负数 if(i < 0){ i += part.getMax(); } // 周日可以用0或7表示,统一转换为0 if(Part.DAY_OF_WEEK.equals(this.part) && Week.SUNDAY.getIso8601Value() == i){ i = Week.SUNDAY.ordinal(); } return checkValue ? part.checkValue(i) : i; } /** * 解析别名支持包括:
*
    *
  • L 表示最大值
  • *
  • {@link Part#MONTH}和{@link Part#DAY_OF_WEEK}别名
  • *
* * @param name 别名 * @return 解析int值 * @throws CronException 无匹配别名时抛出异常 */ private int parseAlias(final String name) throws CronException { if ("L".equalsIgnoreCase(name)) { // L表示最大值 return part.getMax(); } switch (this.part) { case MONTH: // 月份从1开始 return Month.of(name).getValueBaseOne(); case DAY_OF_WEEK: // 周从0开始,0表示周日 return Week.of(name).ordinal(); } throw new CronException("Invalid alias value: [{}]", name); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy