From 978590161aae6faee46fb59463a0cf0c227637ee Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 1 Jul 2025 17:51:14 +0200 Subject: [PATCH 1/2] Upgrade to Checkstyle 10.26.1 --- .../java/org/springframework/build/CheckstyleConventions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java b/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java index ac752c81aef..64296c58574 100644 --- a/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java @@ -50,7 +50,7 @@ public class CheckstyleConventions { project.getPlugins().apply(CheckstylePlugin.class); project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g")); CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class); - checkstyle.setToolVersion("10.26.0"); + checkstyle.setToolVersion("10.26.1"); checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle")); String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion(); DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies(); From 15364cf59fc61d088f5dab4ab3720a769bda9995 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 1 Jul 2025 17:51:23 +0200 Subject: [PATCH 2/2] Polishing --- .../format/annotation/DurationFormat.java | 17 +- .../standard/DurationFormatterUtils.java | 155 +++++++++--------- 2 files changed, 87 insertions(+), 85 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/format/annotation/DurationFormat.java b/spring-context/src/main/java/org/springframework/format/annotation/DurationFormat.java index 64de15c0845..e4541aed245 100644 --- a/spring-context/src/main/java/org/springframework/format/annotation/DurationFormat.java +++ b/spring-context/src/main/java/org/springframework/format/annotation/DurationFormat.java @@ -53,11 +53,19 @@ public @interface DurationFormat { */ Unit defaultUnit() default Unit.MILLIS; + /** * {@link Duration} format styles. */ enum Style { + /** + * ISO-8601 formatting. + *

This is what the JDK uses in {@link Duration#parse(CharSequence)} + * and {@link Duration#toString()}. + */ + ISO8601, + /** * Simple formatting based on a short suffix, for example '1s'. *

Supported unit suffixes include: {@code ns, us, ms, s, m, h, d}. @@ -72,13 +80,6 @@ public @interface DurationFormat { */ SIMPLE, - /** - * ISO-8601 formatting. - *

This is what the JDK uses in {@link Duration#parse(CharSequence)} - * and {@link Duration#toString()}. - */ - ISO8601, - /** * Like {@link #SIMPLE}, but allows multiple segments ordered from * largest-to-smallest units of time, like {@code 1h12m27s}. @@ -90,6 +91,7 @@ public @interface DurationFormat { COMPOSITE } + /** * {@link Duration} format unit, which mirrors a subset of {@link ChronoUnit} and * allows conversion to and from a supported {@code ChronoUnit} as well as @@ -227,7 +229,6 @@ public @interface DurationFormat { } throw new IllegalArgumentException("'" + suffix + "' is not a valid simple duration Unit"); } - } } diff --git a/spring-context/src/main/java/org/springframework/format/datetime/standard/DurationFormatterUtils.java b/spring-context/src/main/java/org/springframework/format/datetime/standard/DurationFormatterUtils.java index 369d7105a25..f63ae687e3a 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/standard/DurationFormatterUtils.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/standard/DurationFormatterUtils.java @@ -28,6 +28,7 @@ import org.springframework.util.StringUtils; /** * Support {@code Duration} parsing and printing in several styles, as listed in * {@link DurationFormat.Style}. + * *

Some styles may not enforce any unit to be present, defaulting to {@code DurationFormat.Unit#MILLIS} * in that case. Methods in this class offer overloads that take a {@link DurationFormat.Unit} to * be used as a fall-back instead of the ultimate MILLIS default. @@ -39,8 +40,38 @@ import org.springframework.util.StringUtils; */ public abstract class DurationFormatterUtils { - private DurationFormatterUtils() { - // singleton + private static final Pattern ISO_8601_PATTERN = Pattern.compile("^[+-]?[pP].*$"); + + private static final Pattern SIMPLE_PATTERN = Pattern.compile("^([+-]?\\d+)([a-zA-Z]{0,2})$"); + + private static final Pattern COMPOSITE_PATTERN = Pattern.compile("^([+-]?)\\(?\\s?(\\d+d)?\\s?(\\d+h)?\\s?(\\d+m)?" + + "\\s?(\\d+s)?\\s?(\\d+ms)?\\s?(\\d+us)?\\s?(\\d+ns)?\\)?$"); + + + /** + * Print the specified duration in the specified style. + * @param value the value to print + * @param style the style to print in + * @return the printed result + */ + public static String print(Duration value, DurationFormat.Style style) { + return print(value, style, null); + } + + /** + * Print the specified duration in the specified style using the given unit. + * @param value the value to print + * @param style the style to print in + * @param unit the unit to use for printing, if relevant ({@code null} will default + * to ms) + * @return the printed result + */ + public static String print(Duration value, DurationFormat.Style style, @Nullable DurationFormat.Unit unit) { + return switch (style) { + case ISO8601 -> value.toString(); + case SIMPLE -> printSimple(value, unit); + case COMPOSITE -> printComposite(value); + }; } /** @@ -71,29 +102,24 @@ public abstract class DurationFormatterUtils { } /** - * Print the specified duration in the specified style. - * @param value the value to print - * @param style the style to print in - * @return the printed result + * Detect the style from the given source value. + * @param value the source value + * @return the duration style + * @throws IllegalArgumentException if the value is not a known style */ - public static String print(Duration value, DurationFormat.Style style) { - return print(value, style, null); - } - - /** - * Print the specified duration in the specified style using the given unit. - * @param value the value to print - * @param style the style to print in - * @param unit the unit to use for printing, if relevant ({@code null} will default - * to ms) - * @return the printed result - */ - public static String print(Duration value, DurationFormat.Style style, @Nullable DurationFormat.Unit unit) { - return switch (style) { - case ISO8601 -> value.toString(); - case SIMPLE -> printSimple(value, unit); - case COMPOSITE -> printComposite(value); - }; + public static DurationFormat.Style detect(String value) { + Assert.notNull(value, "Value must not be null"); + // warning: the order of parsing starts to matter if multiple patterns accept a plain integer (no unit suffix) + if (ISO_8601_PATTERN.matcher(value).matches()) { + return DurationFormat.Style.ISO8601; + } + if (SIMPLE_PATTERN.matcher(value).matches()) { + return DurationFormat.Style.SIMPLE; + } + if (COMPOSITE_PATTERN.matcher(value).matches()) { + return DurationFormat.Style.COMPOSITE; + } + throw new IllegalArgumentException("'" + value + "' is not a valid duration, cannot detect any known style"); } /** @@ -120,31 +146,6 @@ public abstract class DurationFormatterUtils { return parse(value, detect(value), unit); } - /** - * Detect the style from the given source value. - * @param value the source value - * @return the duration style - * @throws IllegalArgumentException if the value is not a known style - */ - public static DurationFormat.Style detect(String value) { - Assert.notNull(value, "Value must not be null"); - // warning: the order of parsing starts to matter if multiple patterns accept a plain integer (no unit suffix) - if (ISO_8601_PATTERN.matcher(value).matches()) { - return DurationFormat.Style.ISO8601; - } - if (SIMPLE_PATTERN.matcher(value).matches()) { - return DurationFormat.Style.SIMPLE; - } - if (COMPOSITE_PATTERN.matcher(value).matches()) { - return DurationFormat.Style.COMPOSITE; - } - throw new IllegalArgumentException("'" + value + "' is not a valid duration, cannot detect any known style"); - } - - private static final Pattern ISO_8601_PATTERN = Pattern.compile("^[+-]?[pP].*$"); - private static final Pattern SIMPLE_PATTERN = Pattern.compile("^([+-]?\\d+)([a-zA-Z]{0,2})$"); - private static final Pattern COMPOSITE_PATTERN = Pattern.compile("^([+-]?)\\(?\\s?(\\d+d)?\\s?(\\d+h)?\\s?(\\d+m)?" + - "\\s?(\\d+s)?\\s?(\\d+ms)?\\s?(\\d+us)?\\s?(\\d+ns)?\\)?$"); private static Duration parseIso8601(String value) { try { @@ -155,6 +156,11 @@ public abstract class DurationFormatterUtils { } } + private static String printSimple(Duration duration, @Nullable DurationFormat.Unit unit) { + unit = (unit == null ? DurationFormat.Unit.MILLIS : unit); + return unit.print(duration); + } + private static Duration parseSimple(String text, @Nullable DurationFormat.Unit fallbackUnit) { try { Matcher matcher = SIMPLE_PATTERN.matcher(text); @@ -171,34 +177,6 @@ public abstract class DurationFormatterUtils { } } - private static String printSimple(Duration duration, @Nullable DurationFormat.Unit unit) { - unit = (unit == null ? DurationFormat.Unit.MILLIS : unit); - return unit.print(duration); - } - - private static Duration parseComposite(String text) { - try { - Matcher matcher = COMPOSITE_PATTERN.matcher(text); - Assert.state(matcher.matches() && matcher.groupCount() > 1, "Does not match composite duration pattern"); - String sign = matcher.group(1); - boolean negative = sign != null && sign.equals("-"); - - Duration result = Duration.ZERO; - DurationFormat.Unit[] units = DurationFormat.Unit.values(); - for (int i = 2; i < matcher.groupCount() + 1; i++) { - String segment = matcher.group(i); - if (StringUtils.hasText(segment)) { - DurationFormat.Unit unit = units[units.length - i + 1]; - result = result.plus(unit.parse(segment.replace(unit.asSuffix(), ""))); - } - } - return negative ? result.negated() : result; - } - catch (Exception ex) { - throw new IllegalArgumentException("'" + text + "' is not a valid composite duration", ex); - } - } - private static String printComposite(Duration duration) { if (duration.isZero()) { return DurationFormat.Unit.SECONDS.print(duration); @@ -243,4 +221,27 @@ public abstract class DurationFormatterUtils { return result.toString(); } + private static Duration parseComposite(String text) { + try { + Matcher matcher = COMPOSITE_PATTERN.matcher(text); + Assert.state(matcher.matches() && matcher.groupCount() > 1, "Does not match composite duration pattern"); + String sign = matcher.group(1); + boolean negative = sign != null && sign.equals("-"); + + Duration result = Duration.ZERO; + DurationFormat.Unit[] units = DurationFormat.Unit.values(); + for (int i = 2; i < matcher.groupCount() + 1; i++) { + String segment = matcher.group(i); + if (StringUtils.hasText(segment)) { + DurationFormat.Unit unit = units[units.length - i + 1]; + result = result.plus(unit.parse(segment.replace(unit.asSuffix(), ""))); + } + } + return negative ? result.negated() : result; + } + catch (Exception ex) { + throw new IllegalArgumentException("'" + text + "' is not a valid composite duration", ex); + } + } + }