diff --git a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/CronPatternBuilder.java b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/CronPatternBuilder.java deleted file mode 100644 index d56221726..000000000 --- a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/CronPatternBuilder.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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; - -import org.dromara.hutool.core.lang.builder.Builder; -import org.dromara.hutool.core.lang.Assert; -import org.dromara.hutool.core.text.StrJoiner; -import org.dromara.hutool.core.array.ArrayUtil; -import org.dromara.hutool.core.text.StrUtil; - -/** - * 定时任务表达式构建器 - * - * @author looly - * @since 5.8.0 - */ -public class CronPatternBuilder implements Builder { - private static final long serialVersionUID = 1L; - - final String[] parts = new String[7]; - - /** - * 创建构建器 - * @return CronPatternBuilder - */ - public static CronPatternBuilder of() { - return new CronPatternBuilder(); - } - - /** - * 设置值 - * - * @param part 部分,如秒、分、时等 - * @param values 时间值列表 - * @return this - */ - public CronPatternBuilder setValues(final Part part, final int... values) { - for (final int value : values) { - part.checkValue(value); - } - return set(part, ArrayUtil.join(values, ",")); - } - - /** - * 设置区间 - * - * @param part 部分,如秒、分、时等 - * @param begin 起始值 - * @param end 结束值 - * @return this - */ - public CronPatternBuilder setRange(final Part part, final int begin, final int end) { - Assert.notNull(part ); - part.checkValue(begin); - part.checkValue(end); - return set(part, StrUtil.format("{}-{}", begin, end)); - } - - /** - * 设置对应部分的定时任务值 - * - * @param part 部分,如秒、分、时等 - * @param value 表达式值,如"*"、"1,2"、"5-12"等 - * @return this - */ - public CronPatternBuilder set(final Part part, final String value) { - parts[part.ordinal()] = value; - return this; - } - - @Override - public String build() { - for (int i = Part.MINUTE.ordinal(); i < Part.YEAR.ordinal(); i++) { - // 从分到周,用户未设置使用默认值 - // 秒和年如果不设置,忽略之 - if(StrUtil.isBlank(parts[i])){ - parts[i] = "*"; - } - } - - return StrJoiner.of(StrUtil.SPACE) - .setNullMode(StrJoiner.NullMode.IGNORE) - .append(this.parts) - .toString(); - } -} diff --git a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/Part.java b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/Part.java index b1bb7d849..5dc7fdcaf 100644 --- a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/Part.java +++ b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/Part.java @@ -36,12 +36,33 @@ import java.util.Calendar; * @since 5.8.0 */ public enum Part { + /** + * 秒[0-59] + */ SECOND(Calendar.SECOND, 0, 59), + /** + * 分[0-59] + */ MINUTE(Calendar.MINUTE, 0, 59), + /** + * 时[0-23] + */ HOUR(Calendar.HOUR_OF_DAY, 0, 23), + /** + * 日[1-31] + */ DAY_OF_MONTH(Calendar.DAY_OF_MONTH, 1, 31), + /** + * 月[1-12] + */ MONTH(Calendar.MONTH, Month.JANUARY.getValueBaseOne(), Month.DECEMBER.getValueBaseOne()), + /** + * 周中的天,如周一、周日等,[0-6]即[SUNDAY-SATURDAY] + */ DAY_OF_WEEK(Calendar.DAY_OF_WEEK, Week.SUNDAY.ordinal(), Week.SATURDAY.ordinal()), + /** + * 年[1970-2099] + */ YEAR(Calendar.YEAR, 1970, 2099); // --------------------------------------------------------------- diff --git a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/builder/PartBuilder.java b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/builder/PartBuilder.java new file mode 100644 index 000000000..6b0c091dd --- /dev/null +++ b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/builder/PartBuilder.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 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.builder; + +import org.dromara.hutool.core.collection.CollUtil; +import org.dromara.hutool.core.collection.ListUtil; +import org.dromara.hutool.core.lang.builder.Builder; + +import java.util.ArrayList; +import java.util.List; + +/** + * Cron表达式的分片构建器 + * + * @author Looly + * @since 6.0.0 + */ +public interface PartBuilder extends Builder { + + /** + * 始终匹配 + * + * @return Always + */ + static Always always() { + return Always.INSTANCE; + } + + /** + * 始终匹配 + */ + class Always implements PartBuilder { + private static final long serialVersionUID = 1L; + + /** + * 始终匹配 + */ + public static final Always INSTANCE = new Always(); + + @Override + public String build() { + return "*"; + } + } + + /** + * 固定值 + */ + class On implements PartBuilder { + private static final long serialVersionUID = 1L; + + private final String value; + + /** + * 构造 + * + * @param value 值 + */ + public On(final int value) { + this(String.valueOf(value)); + } + + /** + * 构造 + * + * @param value 值 + */ + public On(final String value) { + this.value = value; + } + + @Override + public String build() { + return value; + } + } + + /** + * 区间表达式 + */ + class Range implements PartBuilder { + private static final long serialVersionUID = 1L; + + private final String start; + private final String end; + + /** + * 构造 + * + * @param start 起始值(包含) + * @param end 结束值(包含) + */ + public Range(final int start, final int end) { + this(String.valueOf(start), String.valueOf(end)); + } + + /** + * 构造 + * + * @param start 起始值(包含) + * @param end 结束值(包含) + */ + public Range(final String start, final String end) { + this.start = start; + this.end = end; + } + + @Override + public String build() { + return start + "-" + end; + } + } + + /** + * 逻辑与表达式 + */ + class And implements PartBuilder { + private static final long serialVersionUID = 1L; + + private final List builders; + + /** + * 构造 + * + * @param values 值列表 + */ + public And(final int... values) { + this.builders = new ArrayList<>(values.length); + for (final int value : values) { + this.builders.add(new On(value)); + } + } + + /** + * 构造 + * + * @param builders 表达式列表 + */ + public And(final PartBuilder... builders) { + this.builders = ListUtil.of(builders); + } + + /** + * 追加表达式 + * + * @param builder 表达式 + * @return this + */ + public And and(final PartBuilder builder) { + this.builders.add(builder); + return this; + } + + @Override + public String build() { + return CollUtil.join(builders, ",", PartBuilder::build); + } + } + + /** + * 每隔指定步长
+ * 如 5/3,表示每3步取一次,即5,8,11,14,17... + */ + class Every implements PartBuilder { + private static final long serialVersionUID = 1L; + + private final PartBuilder partBuilder; + private final int step; + + /** + * 构造 + * + * @param step 步长 + */ + public Every(final int step) { + this(PartBuilder.always(), step); + } + + /** + * 构造 + * + * @param partBuilder 表达式 + * @param step 步长 + */ + public Every(final PartBuilder partBuilder, final int step) { + this.partBuilder = partBuilder; + this.step = step; + } + + @Override + public String build() { + final String build = partBuilder.build(); + if ("*".equals(build) && 1 == step) { + return build; + } + return build + "/" + step; + } + } +} diff --git a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/builder/PatternBuilder.java b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/builder/PatternBuilder.java new file mode 100644 index 000000000..b358d3845 --- /dev/null +++ b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/builder/PatternBuilder.java @@ -0,0 +1,184 @@ +/* + * 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.builder; + +import org.dromara.hutool.core.lang.Assert; +import org.dromara.hutool.core.lang.builder.Builder; +import org.dromara.hutool.core.text.StrJoiner; +import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.cron.pattern.Part; + +/** + * 定时任务表达式构建器 + * + * @author looly + * @since 5.8.0 + */ +public class PatternBuilder implements Builder { + private static final long serialVersionUID = 1L; + + final String[] parts = new String[7]; + + /** + * 创建构建器 + * @return CronPatternBuilder + */ + public static PatternBuilder of() { + return new PatternBuilder(); + } + + // region ----- set + /** + * 设置值 + * + * @param part 部分,如秒、分、时等 + * @param values 时间值列表 + * @return this + */ + public PatternBuilder setValues(final Part part, final int... values) { + for (final int value : values) { + part.checkValue(value); + } + return set(part, new PartBuilder.And(values)); + } + + /** + * 设置区间 + * + * @param part 部分,如秒、分、时等 + * @param begin 起始值 + * @param end 结束值 + * @return this + */ + public PatternBuilder setRange(final Part part, final int begin, final int end) { + Assert.notNull(part); + part.checkValue(begin); + part.checkValue(end); + return set(part, new PartBuilder.Range(begin, end)); + } + + /** + * 设置年对应部分的定时任务值 + * + * @param value 表达式值,如"*"、"2024,2025"、"2015-2025"等 + * @return this + */ + public PatternBuilder setYear(final PartBuilder value) { + return set(Part.YEAR, value); + } + + /** + * 设置周中的天对应部分的定时任务值 + * + * @param value 表达式值,如"Sun"、"7"等 + * @return this + */ + public PatternBuilder setDayOfWeek(final PartBuilder value) { + return set(Part.DAY_OF_WEEK, value); + } + + /** + * 设置月份对应部分的定时任务值 + * + * @param value 表达式值,如"*"、"1,2"、"5-12"等 + * @return this + */ + public PatternBuilder setMonth(final PartBuilder value) { + return set(Part.MONTH, value); + } + + /** + * 设置日对应部分的定时任务值 + * + * @param value 表达式值,如"*"、"1,2"、"5-12"等 + * @return this + */ + public PatternBuilder setDayOfMonth(final PartBuilder value) { + return set(Part.DAY_OF_MONTH, value); + } + + /** + * 设置时对应部分的定时任务值 + * + * @param value 表达式值,如"*"、"1,2"、"5-12"等 + * @return this + */ + public PatternBuilder setHour(final PartBuilder value) { + return set(Part.HOUR, value); + } + + /** + * 设置分对应部分的定时任务值 + * + * @param value 表达式值,如"*"、"1,2"、"5-12"等 + * @return this + */ + public PatternBuilder setMinute(final PartBuilder value) { + return set(Part.MINUTE, value); + } + + /** + * 设置秒对应部分的定时任务值 + * + * @param value 表达式值,如"*"、"1,2"、"5-12"等 + * @return this + */ + public PatternBuilder setSecond(final PartBuilder value) { + return set(Part.SECOND, value); + } + + /** + * 设置对应部分的定时任务值 + * + * @param part 部分,如秒、分、时等 + * @param value 表达式值,如"*"、"1,2"、"5-12"等 + * @return this + */ + public PatternBuilder set(final Part part, final PartBuilder value) { + return set(part, ObjUtil.apply(value, PartBuilder::build)); + } + + /** + * 设置对应部分的定时任务值 + * + * @param part 部分,如秒、分、时等 + * @param value 表达式值,如"*"、"1,2"、"5-12"等 + * @return this + */ + public PatternBuilder set(final Part part, final String value) { + parts[part.ordinal()] = value; + return this; + } + // endregion + + @Override + public String build() { + for (int i = Part.MINUTE.ordinal(); i < Part.YEAR.ordinal(); i++) { + // 从分到周,用户未设置使用默认值 + // 秒和年如果不设置,忽略之 + if(StrUtil.isBlank(parts[i])){ + parts[i] = "*"; + } + } + + return StrJoiner.of(StrUtil.SPACE) + .setNullMode(StrJoiner.NullMode.IGNORE) + .append(this.parts) + .toString(); + } +} diff --git a/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/builder/package-info.java b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/builder/package-info.java new file mode 100644 index 000000000..c617d0869 --- /dev/null +++ b/hutool-cron/src/main/java/org/dromara/hutool/cron/pattern/builder/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 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. + */ + +/** + * 模式构建器 + * + * @author Looly + * @since 6.0.0 + */ +package org.dromara.hutool.cron.pattern.builder; diff --git a/hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/CronPatternBuilderTest.java b/hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/builder/PatternBuilderTest.java similarity index 50% rename from hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/CronPatternBuilderTest.java rename to hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/builder/PatternBuilderTest.java index 3a53a844e..7410bf00d 100644 --- a/hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/CronPatternBuilderTest.java +++ b/hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/builder/PatternBuilderTest.java @@ -14,34 +14,52 @@ * limitations under the License. */ -package org.dromara.hutool.cron.pattern; +package org.dromara.hutool.cron.pattern.builder; import org.dromara.hutool.cron.CronException; +import org.dromara.hutool.cron.pattern.Part; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class CronPatternBuilderTest { +public class PatternBuilderTest { @Test public void buildMatchAllTest(){ - String build = CronPatternBuilder.of().build(); + String build = PatternBuilder.of().build(); Assertions.assertEquals("* * * * *", build); - build = CronPatternBuilder.of() + build = PatternBuilder.of() .set(Part.SECOND, "*") .build(); Assertions.assertEquals("* * * * * *", build); - build = CronPatternBuilder.of() + build = PatternBuilder.of() .set(Part.SECOND, "*") .set(Part.YEAR, "*") .build(); Assertions.assertEquals("* * * * * * *", build); } + @Test + public void buildMatchAllTest2(){ + String build = PatternBuilder.of().build(); + Assertions.assertEquals("* * * * *", build); + + build = PatternBuilder.of() + .setSecond(PartBuilder.always()) + .build(); + Assertions.assertEquals("* * * * * *", build); + + build = PatternBuilder.of() + .setSecond(PartBuilder.always()) + .setYear(PartBuilder.always()) + .build(); + Assertions.assertEquals("* * * * * * *", build); + } + @Test public void buildRangeTest(){ - final String build = CronPatternBuilder.of() + final String build = PatternBuilder.of() .set(Part.SECOND, "*") .setRange(Part.HOUR, 2, 9) .build(); @@ -51,7 +69,7 @@ public class CronPatternBuilderTest { @Test public void buildRangeErrorTest(){ Assertions.assertThrows(CronException.class, ()->{ - final String build = CronPatternBuilder.of() + final String build = PatternBuilder.of() .set(Part.SECOND, "*") // 55无效值 .setRange(Part.HOUR, 2, 55) @@ -59,4 +77,32 @@ public class CronPatternBuilderTest { Assertions.assertEquals("* * 2-9 * * *", build); }); } + + @Test + public void buildValuesTest(){ + final String build = PatternBuilder.of() + .setSecond(PartBuilder.always()) + .setValues(Part.HOUR, 2, 9, 12) + .build(); + Assertions.assertEquals("* * 2,9,12 * * *", build); + } + + @Test + void buildOnTest(){ + final String build = PatternBuilder.of() + .setSecond(PartBuilder.always()) + .setHour(new PartBuilder.On(12)) + .build(); + Assertions.assertEquals("* * 12 * * *", build); + } + + + @Test + void buildEveryTest(){ + final String build = PatternBuilder.of() + .setSecond(PartBuilder.always()) + .setHour(new PartBuilder.Every(2)) + .build(); + Assertions.assertEquals("* * */2 * * *", build); + } } diff --git a/hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/IssueI7SMP7Test.java b/hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/parser/IssueI7SMP7Test.java old mode 100755 new mode 100644 similarity index 90% rename from hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/IssueI7SMP7Test.java rename to hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/parser/IssueI7SMP7Test.java index 32d57b724..fec33cd67 --- a/hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/IssueI7SMP7Test.java +++ b/hutool-cron/src/test/java/org/dromara/hutool/cron/pattern/parser/IssueI7SMP7Test.java @@ -14,10 +14,9 @@ * limitations under the License. */ -package org.dromara.hutool.cron.pattern; +package org.dromara.hutool.cron.pattern.parser; import org.dromara.hutool.cron.pattern.matcher.PatternMatcher; -import org.dromara.hutool.cron.pattern.parser.PatternParser; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test;