add PartBuilder

This commit is contained in:
Looly 2024-12-16 10:26:45 +08:00
parent 6bcdce2d20
commit 4ad131777d
7 changed files with 495 additions and 109 deletions

View File

@ -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<String> {
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();
}
}

View File

@ -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);
// ---------------------------------------------------------------

View File

@ -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<String> {
/**
* 始终匹配
*
* @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<PartBuilder> 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);
}
}
/**
* 每隔指定步长<br>
* 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;
}
}
}

View File

@ -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<String> {
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();
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;