From ab9e7f46d87cc3faa35f7f2b9a4cd6da41fd56d7 Mon Sep 17 00:00:00 2001 From: Grubhart Date: Sat, 25 Apr 2020 11:40:40 -0500 Subject: [PATCH 1/9] Add Period converter support Add basic support to convert String to Period. See GH-21080 --- .../convert/ApplicationConversionService.java | 3 +- .../boot/convert/PeriodUnit.java | 45 ++++++++++++++ .../boot/convert/StringToPeriodConverter.java | 60 +++++++++++++++++++ .../convert/StringToPeriodConverterTest.java | 54 +++++++++++++++++ 4 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/StringToPeriodConverterTest.java diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/ApplicationConversionService.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/ApplicationConversionService.java index 948314c66178..08c3573de762 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/ApplicationConversionService.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/ApplicationConversionService.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -110,6 +110,7 @@ public static void configure(FormatterRegistry registry) { public static void addApplicationConverters(ConverterRegistry registry) { addDelimitedStringConverters(registry); registry.addConverter(new StringToDurationConverter()); + registry.addConverter(new StringToPeriodConverter()); registry.addConverter(new DurationToStringConverter()); registry.addConverter(new NumberToDurationConverter()); registry.addConverter(new DurationToNumberConverter()); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java new file mode 100644 index 000000000000..7d64dcfeabcd --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java @@ -0,0 +1,45 @@ +/* + * Copyright 2012-2020 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 org.springframework.boot.convert; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.time.Period; +import java.time.temporal.ChronoUnit; + +/** + * Annotation that can be used to change the default unit used when converting a + * {@link Period}. + * + * @author Edson Chávez + * @since 2.3.0 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface PeriodUnit { + + /** + * The Period unit to use if one is not specified. + * @return the Period unit + */ + ChronoUnit value(); + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java new file mode 100644 index 000000000000..6849611aad32 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2020 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 org.springframework.boot.convert; + +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.GenericConverter; +import org.springframework.util.ObjectUtils; + +import java.time.Period; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.Set; + +/** + * {@link Converter} to convert from a {@link String} to a {@link Period}. Supports + * {@link Period#parse(CharSequence)} as well a more readable {@code 10s} form. + * + * @author Edson Chávez + * @see PeriodUnit + */ +public class StringToPeriodConverter implements GenericConverter { + + @Override + public Set getConvertibleTypes() { + return Collections.singleton(new GenericConverter.ConvertiblePair(String.class, Period.class)); + } + + @Override + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + if (ObjectUtils.isEmpty(source)) { + return null; + } + return convert(source.toString(), getPeriodUnit(targetType)); + } + + private ChronoUnit getPeriodUnit(TypeDescriptor targetType) { + PeriodUnit annotation = targetType.getAnnotation(PeriodUnit.class); + return (annotation != null) ? annotation.value() : null; + } + + private Period convert(String source, ChronoUnit unit) { + return Period.parse(source); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/StringToPeriodConverterTest.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/StringToPeriodConverterTest.java new file mode 100644 index 000000000000..47f9795b8c21 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/StringToPeriodConverterTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-2020 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 org.springframework.boot.convert; + +import org.junit.jupiter.params.provider.Arguments; +import org.springframework.core.convert.ConversionService; + +import java.time.Period; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link StringToPeriodConverter}. + * + * @author Edson Chávez + */ +public class StringToPeriodConverterTest { + + @ConversionServiceTest + void convertWhenIso8601ShouldReturnPeriod(ConversionService conversionService) { + assertThat(convert(conversionService, "P2Y")).isEqualTo(Period.parse("P2Y")); + assertThat(convert(conversionService, "P3M")).isEqualTo(Period.parse("P3M")); + assertThat(convert(conversionService, "P4W")).isEqualTo(Period.parse("P4W")); + assertThat(convert(conversionService, "P5D")).isEqualTo(Period.parse("P5D")); + assertThat(convert(conversionService, "P1Y2M3D")).isEqualTo(Period.parse("P1Y2M3D")); + assertThat(convert(conversionService, "P1Y2M3W4D")).isEqualTo(Period.parse("P1Y2M3W4D")); + assertThat(convert(conversionService, "P-1Y2M")).isEqualTo(Period.parse("P-1Y2M")); + assertThat(convert(conversionService, "-P1Y2M")).isEqualTo(Period.parse("-P1Y2M")); + } + + private Period convert(ConversionService conversionService, String source) { + return conversionService.convert(source, Period.class); + } + + static Stream conversionServices() { + return ConversionServiceArguments.with(new StringToPeriodConverter()); + } + +} From 8fd785487ff091d843ecd7f89d482fca1d2e4150 Mon Sep 17 00:00:00 2001 From: Grubhart Date: Sat, 25 Apr 2020 11:58:27 -0500 Subject: [PATCH 2/9] Add Period converter support Add basic support to convert Period to String. --- .../convert/ApplicationConversionService.java | 1 + .../boot/convert/PeriodToStringConverter.java | 38 ++++++++++++++++ .../convert/PeriodToStringConverterTests.java | 44 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/ApplicationConversionService.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/ApplicationConversionService.java index 08c3573de762..186abb84cc04 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/ApplicationConversionService.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/ApplicationConversionService.java @@ -112,6 +112,7 @@ public static void addApplicationConverters(ConverterRegistry registry) { registry.addConverter(new StringToDurationConverter()); registry.addConverter(new StringToPeriodConverter()); registry.addConverter(new DurationToStringConverter()); + registry.addConverter(new PeriodToStringConverter()); registry.addConverter(new NumberToDurationConverter()); registry.addConverter(new DurationToNumberConverter()); registry.addConverter(new StringToDataSizeConverter()); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java new file mode 100644 index 000000000000..1fca9608f46d --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java @@ -0,0 +1,38 @@ +package org.springframework.boot.convert; + +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.GenericConverter; +import org.springframework.util.ObjectUtils; + +import java.time.Period; +import java.time.Period; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.Set; + +public class PeriodToStringConverter implements GenericConverter { + + @Override + public Set getConvertibleTypes() { + return Collections.singleton(new ConvertiblePair(Period.class, String.class)); + } + + @Override + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + if (ObjectUtils.isEmpty(source)) { + return null; + } + return convert((Period) source, getPeriodUnit(sourceType)); + } + + private String convert(Period source, ChronoUnit unit) { + + return source.toString(); + } + + private ChronoUnit getPeriodUnit(TypeDescriptor sourceType) { + PeriodUnit annotation = sourceType.getAnnotation(PeriodUnit.class); + return (annotation != null) ? annotation.value() : null; + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java new file mode 100644 index 000000000000..bd7f1d1b0eb0 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2020 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 org.springframework.boot.convert; + +import org.junit.jupiter.params.provider.Arguments; +import org.springframework.core.convert.ConversionService; + +import java.time.Period; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link DurationToStringConverter}. + * + * @author Edson Chávez + */ +class PeriodToStringConverterTests { + + @ConversionServiceTest + void convertWithoutStyleShouldReturnIso8601(ConversionService conversionService) { + String converted = conversionService.convert(Period.ofDays(1), String.class); + assertThat(converted).isEqualTo(Period.ofDays(1).toString()); + } + + static Stream conversionServices() throws Exception { + return ConversionServiceArguments.with(new PeriodToStringConverter()); + } + +} From 28e51dded42bb15f645243d756ef7b2d12ac9167 Mon Sep 17 00:00:00 2001 From: Grubhart Date: Sat, 25 Apr 2020 12:27:19 -0500 Subject: [PATCH 3/9] Add Period converter support Add initial values to bind periods --- .../javac/JavaCompilerFieldValuesParser.java | 19 ++++++++++++++++++- .../AbstractFieldValuesProcessorTests.java | 7 ++++++- .../fieldvalues/FieldValues.java | 13 ++++++++++++- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/javac/JavaCompilerFieldValuesParser.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/javac/JavaCompilerFieldValuesParser.java index 5c15c4f6b61f..0311eafe72fc 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/javac/JavaCompilerFieldValuesParser.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/fieldvalues/javac/JavaCompilerFieldValuesParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -130,6 +130,19 @@ private static class FieldCollector implements TreeVisitor { DATA_SIZE_SUFFIX = Collections.unmodifiableMap(values); } + private static final String PERIOD_OF = "Period.of"; + + private static final Map PERIOD_SUFFIX; + + static { + Map values = new HashMap<>(); + values.put("Days", "d"); + values.put("Weeks", "w"); + values.put("Months", "m"); + values.put("Years", "y"); + PERIOD_SUFFIX = Collections.unmodifiableMap(values); + } + private final Map fieldValues = new HashMap<>(); private final Map staticFinals = new HashMap<>(); @@ -194,6 +207,10 @@ private Object getFactoryValue(ExpressionTree expression, Object factoryValue) { if (dataSizeValue != null) { return dataSizeValue; } + Object periodValue = getFactoryValue(expression, factoryValue, PERIOD_OF, PERIOD_SUFFIX); + if (periodValue != null) { + return periodValue; + } return factoryValue; } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java index 29ba8dccd6fb..da4c0cf5d178 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -100,6 +100,11 @@ void getFieldValues() throws Exception { assertThat(values.get("dataSizeMegabytes")).isEqualTo("20MB"); assertThat(values.get("dataSizeGigabytes")).isEqualTo("30GB"); assertThat(values.get("dataSizeTerabytes")).isEqualTo("40TB"); + assertThat(values.get("periodNone")).isNull(); + assertThat(values.get("periodDays")).isEqualTo("3d"); + assertThat(values.get("periodWeeks")).isEqualTo("2w"); + assertThat(values.get("periodMonths")).isEqualTo("10m"); + assertThat(values.get("periodYears")).isEqualTo("15y"); } @SupportedAnnotationTypes({ "org.springframework.boot.configurationsample.ConfigurationProperties" }) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/fieldvalues/FieldValues.java b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/fieldvalues/FieldValues.java index 7dbdc96de1f6..e6cd231366bb 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/fieldvalues/FieldValues.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationsample/fieldvalues/FieldValues.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 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. @@ -19,6 +19,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.time.Duration; +import java.time.Period; import org.springframework.boot.configurationsample.ConfigurationProperties; import org.springframework.util.MimeType; @@ -136,4 +137,14 @@ public class FieldValues { private DataSize dataSizeTerabytes = DataSize.ofTerabytes(40); + private Period periodNone; + + private Period periodDays = Period.ofDays(3); + + private Period periodWeeks = Period.ofWeeks(2); + + private Period periodMonths = Period.ofMonths(10); + + private Period periodYears = Period.ofYears(15); + } From c7190bcd8654e3a291373a73cceb0af60bf7310e Mon Sep 17 00:00:00 2001 From: Grubhart Date: Sun, 26 Apr 2020 14:45:44 -0500 Subject: [PATCH 4/9] Add PeriodStyle support --- .../boot/convert/PeriodFormat.java | 40 +++ .../boot/convert/PeriodStyle.java | 312 ++++++++++++++++++ .../boot/convert/PeriodToStringConverter.java | 12 +- .../boot/convert/PeriodUnit.java | 8 +- .../boot/convert/StringToPeriodConverter.java | 12 +- .../convert/MockPeriodTypeDescriptor.java | 60 ++++ .../boot/convert/PereiodStyleTests.java | 198 +++++++++++ .../convert/PeriodToStringConverterTests.java | 18 + .../convert/StringToPeriodConverterTest.java | 46 +++ 9 files changed, 694 insertions(+), 12 deletions(-) create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/MockPeriodTypeDescriptor.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PereiodStyleTests.java diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java new file mode 100644 index 000000000000..7b07175a3b13 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012-2020 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 org.springframework.boot.convert; + +import java.lang.annotation.*; + +/** + * Annotation that can be used to indicate the format to use when converting a + * {@link Period}. + * + * @author Eddú Meléndez + * @author Edson Chávez + * @since 2.3.0 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface PeriodFormat { + + /** + * The Period format style. + * @return the period format style. + */ + PeriodStyle value(); + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java new file mode 100644 index 000000000000..e3561f8887af --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java @@ -0,0 +1,312 @@ +/* + * Copyright 2002-2020 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 org.springframework.boot.convert; + +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; +import org.springframework.util.unit.DataSize; + +import java.time.Period; +import java.time.temporal.ChronoUnit; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A standard set of {@link DataSize} units. + * + *

+ * The unit prefixes used in this class are + * binary prefixes indicating + * multiplication by powers of 2. The following table displays the enum constants defined + * in this class and corresponding values. + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ConstantData SizePower of 2Size in Bytes
{@link #BYTES}1B2^01
{@link #KILOBYTES}1KB2^101,024
{@link #MEGABYTES}1MB2^201,048,576
{@link #GIGABYTES}1GB2^301,073,741,824
{@link #TERABYTES}1TB2^401,099,511,627,776
+ * + * @author Eddú Meléndez + * @author Edson Chávez + * @since 2.3.0 + * @see Period + */ +public enum PeriodStyle { + + /** + * Simple formatting, for example '1s'. + */ + SIMPLE("^([\\+\\-]?\\d+)([a-zA-Z]{0,2})$") { + + @Override + public Period parse(String value, ChronoUnit unit) { + try { + Matcher matcher = matcher(value); + Assert.state(matcher.matches(), "Does not match simple period pattern"); + String suffix = matcher.group(2); + return (StringUtils.hasLength(suffix) ? Unit.fromSuffix(suffix) : Unit.fromChronoUnit(unit)) + .parse(matcher.group(1)); + } + catch (Exception ex) { + throw new IllegalArgumentException("'" + value + "' is not a valid simple period", ex); + } + } + + @Override + public String print(Period value, ChronoUnit unit) { + return Unit.fromChronoUnit(unit).print(value); + } + + }, + + /** + * ISO-8601 formatting. + */ + ISO8601("^[\\+\\-]?P.*$") { + + @Override + public Period parse(String value, ChronoUnit unit) { + try { + return Period.parse(value); + } + catch (Exception ex) { + throw new IllegalArgumentException("'" + value + "' is not a valid ISO-8601 period", ex); + } + } + + @Override + public String print(Period value, ChronoUnit unit) { + return value.toString(); + } + + }; + + private final Pattern pattern; + + PeriodStyle(String pattern) { + this.pattern = Pattern.compile(pattern); + } + + protected final boolean matches(String value) { + return this.pattern.matcher(value).matches(); + } + + protected final Matcher matcher(String value) { + return this.pattern.matcher(value); + } + + /** + * Parse the given value to a duration. + * @param value the value to parse + * @return a duration + */ + public Period parse(String value) { + return parse(value, null); + } + + /** + * Parse the given value to a duration. + * @param value the value to parse + * @param unit the duration unit to use if the value doesn't specify one ({@code null} + * will default to ms) + * @return a duration + */ + public abstract Period parse(String value, ChronoUnit unit); + + /** + * Print the specified duration. + * @param value the value to print + * @return the printed result + */ + public String print(Period value) { + return print(value, null); + } + + /** + * Print the specified duration using the given unit. + * @param value the value to print + * @param unit the value to use for printing + * @return the printed result + */ + public abstract String print(Period value, ChronoUnit unit); + + /** + * Detect the style then parse the value to return a duration. + * @param value the value to parse + * @return the parsed duration + * @throws IllegalStateException if the value is not a known style or cannot be parsed + */ + public static Period detectAndParse(String value) { + return detectAndParse(value, null); + } + + /** + * Detect the style then parse the value to return a duration. + * @param value the value to parse + * @param unit the duration unit to use if the value doesn't specify one ({@code null} + * will default to ms) + * @return the parsed duration + * @throws IllegalStateException if the value is not a known style or cannot be parsed + */ + public static Period detectAndParse(String value, ChronoUnit unit) { + return detect(value).parse(value, unit); + } + + /** + * Detect the style from the given source value. + * @param value the source value + * @return the duration style + * @throws IllegalStateException if the value is not a known style + */ + public static PeriodStyle detect(String value) { + Assert.notNull(value, "Value must not be null"); + for (PeriodStyle candidate : values()) { + if (candidate.matches(value)) { + return candidate; + } + } + throw new IllegalArgumentException("'" + value + "' is not a valid period"); + } + + enum Unit { + + /** + * Days, represented by suffix {@code B}. + */ + DAYS(ChronoUnit.DAYS, "d", Period::getDays), + + /** + * Days, represented by suffix {@code B}. + */ + // WEEKS(ChronoUnit.WEEKS,"w", Period::ofWeeks), + + /** + * Days, represented by suffix {@code B}. + */ + MONTHS(ChronoUnit.MONTHS, "m", Period::getMonths), + + /** + * Days, represented by suffix {@code B}. + */ + YEARS(ChronoUnit.YEARS, "y", Period::getYears); + + private final ChronoUnit chronoUnit; + + private final String suffix; + + private final Function intValue; + + Unit(ChronoUnit chronoUnit, String suffix, Function intValue) { + this.chronoUnit = chronoUnit; + this.suffix = suffix; + this.intValue = intValue; + } + + /** + * Return the {@link Unit} matching the specified {@code suffix}. + * @param suffix one of the standard suffixes + * @return the {@link Unit} matching the specified {@code suffix} + * @throws IllegalArgumentException if the suffix does not match the suffix of any + * of this enum's constants + */ + public static Unit fromSuffix(String suffix) { + for (Unit candidate : values()) { + if (candidate.suffix.equalsIgnoreCase(suffix)) { + return candidate; + } + } + throw new IllegalArgumentException("Unknown unit suffix '" + suffix + "'"); + } + + public Period parse(String value) { + + int intValue = Integer.parseInt(value); + + if (ChronoUnit.DAYS == this.chronoUnit) { + return Period.ofDays(intValue); + } + else if (ChronoUnit.WEEKS == this.chronoUnit) { + return Period.ofWeeks(intValue); + } + else if (ChronoUnit.MONTHS == this.chronoUnit) { + return Period.ofMonths(intValue); + } + else if (ChronoUnit.YEARS == this.chronoUnit) { + return Period.ofYears(intValue); + } + + throw new IllegalArgumentException("Unknow unit '" + this.chronoUnit + "'"); + } + + public String print(Period value) { + return longValue(value) + this.suffix; + } + + public long longValue(Period value) { + return this.intValue.apply(value); + } + + public static Unit fromChronoUnit(ChronoUnit chronoUnit) { + if (chronoUnit == null) { + return Unit.DAYS; + } + for (Unit candidate : values()) { + if (candidate.chronoUnit == chronoUnit) { + return candidate; + } + } + throw new IllegalArgumentException("Unknown unit " + chronoUnit); + } + + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java index 1fca9608f46d..13805e275d0e 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java @@ -4,6 +4,7 @@ import org.springframework.core.convert.converter.GenericConverter; import org.springframework.util.ObjectUtils; +import java.time.Duration; import java.time.Period; import java.time.Period; import java.time.temporal.ChronoUnit; @@ -22,12 +23,17 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t if (ObjectUtils.isEmpty(source)) { return null; } - return convert((Period) source, getPeriodUnit(sourceType)); + return convert((Period) source, getPeriodStyle(sourceType), getPeriodUnit(sourceType)); } - private String convert(Period source, ChronoUnit unit) { + private PeriodStyle getPeriodStyle(TypeDescriptor sourceType) { + PeriodFormat annotation = sourceType.getAnnotation(PeriodFormat.class); + return (annotation != null) ? annotation.value() : null; + } - return source.toString(); + private String convert(Period source, PeriodStyle style, ChronoUnit unit) { + style = (style != null) ? style : PeriodStyle.ISO8601; + return style.print(source, unit); } private ChronoUnit getPeriodUnit(TypeDescriptor sourceType) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java index 7d64dcfeabcd..ceb3ca73e8ee 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java @@ -16,12 +16,8 @@ package org.springframework.boot.convert; -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.time.Period; +import java.lang.annotation.*; +import java.time.Duration; import java.time.temporal.ChronoUnit; /** diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java index 6849611aad32..a481422cfa5b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java @@ -45,7 +45,12 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t if (ObjectUtils.isEmpty(source)) { return null; } - return convert(source.toString(), getPeriodUnit(targetType)); + return convert(source.toString(), getStyle(targetType), getPeriodUnit(targetType)); + } + + private PeriodStyle getStyle(TypeDescriptor targetType) { + PeriodFormat annotation = targetType.getAnnotation(PeriodFormat.class); + return (annotation != null) ? annotation.value() : null; } private ChronoUnit getPeriodUnit(TypeDescriptor targetType) { @@ -53,8 +58,9 @@ private ChronoUnit getPeriodUnit(TypeDescriptor targetType) { return (annotation != null) ? annotation.value() : null; } - private Period convert(String source, ChronoUnit unit) { - return Period.parse(source); + private Period convert(String source, PeriodStyle style, ChronoUnit unit) { + style = (style != null) ? style : PeriodStyle.detect(source); + return style.parse(source, unit); } } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/MockPeriodTypeDescriptor.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/MockPeriodTypeDescriptor.java new file mode 100644 index 000000000000..e5d9e7838f28 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/MockPeriodTypeDescriptor.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2020 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 org.springframework.boot.convert; + +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.convert.TypeDescriptor; + +import java.time.Duration; +import java.time.Period; +import java.time.temporal.ChronoUnit; +import java.util.Collections; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Create a mock {@link TypeDescriptor} with optional {@link PeriodUnit @DurationUnit} and + * {@link PeriodFormat @DurationFormat} annotations. + * + * @author Eddú Meléndez + * @author Edson Chávez + */ +public final class MockPeriodTypeDescriptor { + + private MockPeriodTypeDescriptor() { + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static TypeDescriptor get(ChronoUnit unit, PeriodStyle style) { + TypeDescriptor descriptor = mock(TypeDescriptor.class); + if (unit != null) { + PeriodUnit unitAnnotation = AnnotationUtils.synthesizeAnnotation(Collections.singletonMap("value", unit), + PeriodUnit.class, null); + given(descriptor.getAnnotation(PeriodUnit.class)).willReturn(unitAnnotation); + } + if (style != null) { + PeriodFormat formatAnnotation = AnnotationUtils + .synthesizeAnnotation(Collections.singletonMap("value", style), PeriodFormat.class, null); + given(descriptor.getAnnotation(PeriodFormat.class)).willReturn(formatAnnotation); + } + given(descriptor.getType()).willReturn((Class) Period.class); + given(descriptor.getObjectType()).willReturn((Class) Period.class); + return descriptor; + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PereiodStyleTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PereiodStyleTests.java new file mode 100644 index 000000000000..19e7ae91b091 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PereiodStyleTests.java @@ -0,0 +1,198 @@ +/* + * Copyright 2012-2020 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 org.springframework.boot.convert; + +import org.junit.jupiter.api.Test; + +import java.time.Period; +import java.time.temporal.ChronoUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + +/** + * Tests for {@link PeriodStyle}. + * + * @author Eddú Meléndez + * @author Edson Chávez + */ +class PereiodStyleTests { + + @Test + void detectAndParseWhenValueIsNullShouldThrowException() { + assertThatIllegalArgumentException().isThrownBy(() -> PeriodStyle.detectAndParse(null)) + .withMessageContaining("Value must not be null"); + } + + @Test + void detectAndParseWhenIso8601ShouldReturnPeriod() { + assertThat(PeriodStyle.detectAndParse("P15M")).isEqualTo(Period.parse("P15M")); + assertThat(PeriodStyle.detectAndParse("-P15M")).isEqualTo(Period.parse("P-15M")); + assertThat(PeriodStyle.detectAndParse("+P15M")).isEqualTo(Period.parse("P15M")); + assertThat(PeriodStyle.detectAndParse("P2D")).isEqualTo(Period.parse("P2D")); + assertThat(PeriodStyle.detectAndParse("-P20Y")).isEqualTo(Period.parse("P-20Y")); + + } + + @Test + void detectAndParseWhenSimpleDaysShouldReturnPeriod() { + assertThat(PeriodStyle.detectAndParse("10d")).isEqualTo(Period.ofDays(10)); + assertThat(PeriodStyle.detectAndParse("10D")).isEqualTo(Period.ofDays(10)); + assertThat(PeriodStyle.detectAndParse("+10d")).isEqualTo(Period.ofDays(10)); + assertThat(PeriodStyle.detectAndParse("-10D")).isEqualTo(Period.ofDays(-10)); + } + + @Test + void detectAndParseWhenSimpleMonthsShouldReturnPeriod() { + assertThat(PeriodStyle.detectAndParse("10m")).isEqualTo(Period.ofMonths(10)); + assertThat(PeriodStyle.detectAndParse("10M")).isEqualTo(Period.ofMonths(10)); + assertThat(PeriodStyle.detectAndParse("+10m")).isEqualTo(Period.ofMonths(10)); + assertThat(PeriodStyle.detectAndParse("-10M")).isEqualTo(Period.ofMonths(-10)); + } + + @Test + void detectAndParseWhenSimpleYearsShouldReturnPeriod() { + assertThat(PeriodStyle.detectAndParse("10y")).isEqualTo(Period.ofYears(10)); + assertThat(PeriodStyle.detectAndParse("10Y")).isEqualTo(Period.ofYears(10)); + assertThat(PeriodStyle.detectAndParse("+10y")).isEqualTo(Period.ofYears(10)); + assertThat(PeriodStyle.detectAndParse("-10Y")).isEqualTo(Period.ofYears(-10)); + } + + @Test + void detectAndParseWhenSimpleWithoutSuffixShouldReturnPeriod() { + assertThat(PeriodStyle.detectAndParse("10")).isEqualTo(Period.ofDays(10)); + assertThat(PeriodStyle.detectAndParse("+10")).isEqualTo(Period.ofDays(10)); + assertThat(PeriodStyle.detectAndParse("-10")).isEqualTo(Period.ofDays(-10)); + } + + @Test + void detectAndParseWhenSimpleWithoutSuffixButWithChronoUnitShouldReturnPeriod() { + assertThat(PeriodStyle.detectAndParse("10", ChronoUnit.MONTHS)).isEqualTo(Period.ofMonths(10)); + assertThat(PeriodStyle.detectAndParse("+10", ChronoUnit.MONTHS)).isEqualTo(Period.ofMonths(10)); + assertThat(PeriodStyle.detectAndParse("-10", ChronoUnit.MONTHS)).isEqualTo(Period.ofMonths(-10)); + } + + @Test + void detectAndParseWhenBadFormatShouldThrowException() { + assertThatIllegalArgumentException().isThrownBy(() -> PeriodStyle.detectAndParse("10foo")) + .withMessageContaining("'10foo' is not a valid period"); + } + + @Test + void detectWhenSimpleShouldReturnSimple() { + assertThat(PeriodStyle.detect("10")).isEqualTo(PeriodStyle.SIMPLE); + assertThat(PeriodStyle.detect("+10")).isEqualTo(PeriodStyle.SIMPLE); + assertThat(PeriodStyle.detect("-10")).isEqualTo(PeriodStyle.SIMPLE); + assertThat(PeriodStyle.detect("10m")).isEqualTo(PeriodStyle.SIMPLE); + assertThat(PeriodStyle.detect("10y")).isEqualTo(PeriodStyle.SIMPLE); + assertThat(PeriodStyle.detect("10d")).isEqualTo(PeriodStyle.SIMPLE); + assertThat(PeriodStyle.detect("10D")).isEqualTo(PeriodStyle.SIMPLE); + } + + @Test + void detectWhenIso8601ShouldReturnIso8601() { + assertThat(PeriodStyle.detect("P20")).isEqualTo(PeriodStyle.ISO8601); + assertThat(PeriodStyle.detect("-P15M")).isEqualTo(PeriodStyle.ISO8601); + assertThat(PeriodStyle.detect("+P15M")).isEqualTo(PeriodStyle.ISO8601); + assertThat(PeriodStyle.detect("P10Y")).isEqualTo(PeriodStyle.ISO8601); + assertThat(PeriodStyle.detect("P2D")).isEqualTo(PeriodStyle.ISO8601); + assertThat(PeriodStyle.detect("-P6")).isEqualTo(PeriodStyle.ISO8601); + assertThat(PeriodStyle.detect("-P-6M")).isEqualTo(PeriodStyle.ISO8601); + } + + @Test + void detectWhenUnknownShouldThrowException() { + assertThatIllegalArgumentException().isThrownBy(() -> PeriodStyle.detect("bad")) + .withMessageContaining("'bad' is not a valid period"); + } + + @Test + void parseIso8601ShouldParse() { + assertThat(PeriodStyle.ISO8601.parse("P20D")).isEqualTo(Period.parse("P20D")); + assertThat(PeriodStyle.ISO8601.parse("P15M")).isEqualTo(Period.parse("P15M")); + assertThat(PeriodStyle.ISO8601.parse("+P15M")).isEqualTo(Period.parse("P15M")); + assertThat(PeriodStyle.ISO8601.parse("P10Y")).isEqualTo(Period.parse("P10Y")); + assertThat(PeriodStyle.ISO8601.parse("P2D")).isEqualTo(Period.parse("P2D")); + assertThat(PeriodStyle.ISO8601.parse("-P6D")).isEqualTo(Period.parse("-P6D")); + assertThat(PeriodStyle.ISO8601.parse("-P-6Y+3M")).isEqualTo(Period.parse("-P-6Y+3M")); + } + + @Test + void parseIso8601WithUnitShouldIgnoreUnit() { + assertThat(PeriodStyle.ISO8601.parse("P20D", ChronoUnit.SECONDS)).isEqualTo(Period.parse("P20D")); + assertThat(PeriodStyle.ISO8601.parse("P15M", ChronoUnit.SECONDS)).isEqualTo(Period.parse("P15M")); + assertThat(PeriodStyle.ISO8601.parse("+P15M", ChronoUnit.SECONDS)).isEqualTo(Period.parse("P15M")); + assertThat(PeriodStyle.ISO8601.parse("P10Y", ChronoUnit.SECONDS)).isEqualTo(Period.parse("P10Y")); + assertThat(PeriodStyle.ISO8601.parse("P2D", ChronoUnit.SECONDS)).isEqualTo(Period.parse("P2D")); + assertThat(PeriodStyle.ISO8601.parse("-P6D", ChronoUnit.SECONDS)).isEqualTo(Period.parse("-P6D")); + assertThat(PeriodStyle.ISO8601.parse("-P-6Y+3M", ChronoUnit.SECONDS)).isEqualTo(Period.parse("-P-6Y+3M")); + } + + @Test + void parseIso8601WhenSimpleShouldThrowException() { + assertThatIllegalArgumentException().isThrownBy(() -> PeriodStyle.ISO8601.parse("10d")) + .withMessageContaining("'10d' is not a valid ISO-8601 period"); + } + + @Test + void parseSimpleShouldParse() { + assertThat(PeriodStyle.SIMPLE.parse("10m")).isEqualTo(Period.ofMonths(10)); + } + + @Test + void parseSimpleWithUnitShouldUseUnitAsFallback() { + assertThat(PeriodStyle.SIMPLE.parse("10m", ChronoUnit.DAYS)).isEqualTo(Period.ofMonths(10)); + assertThat(PeriodStyle.SIMPLE.parse("10", ChronoUnit.MONTHS)).isEqualTo(Period.ofMonths(10)); + } + + @Test + void parseSimpleWhenUnknownUnitShouldThrowException() { + assertThatIllegalArgumentException().isThrownBy(() -> PeriodStyle.SIMPLE.parse("10mb")) + .satisfies((ex) -> assertThat(ex.getCause().getMessage()).isEqualTo("Unknown unit suffix 'mb'")); + } + + @Test + void parseSimpleWhenIso8601ShouldThrowException() { + assertThatIllegalArgumentException().isThrownBy(() -> PeriodStyle.SIMPLE.parse("PT10H")) + .withMessageContaining("'PT10H' is not a valid simple period"); + } + + @Test + void printIso8601ShouldPrint() { + Period period = Period.parse("-P-6M+3D"); + assertThat(PeriodStyle.ISO8601.print(period)).isEqualTo("P6M-3D"); + } + + @Test + void printIso8601ShouldIgnoreUnit() { + Period period = Period.parse("-P3Y"); + assertThat(PeriodStyle.ISO8601.print(period, ChronoUnit.DAYS)).isEqualTo("P-3Y"); + } + + @Test + void printSimpleWithoutUnitShouldPrintInDays() { + Period period = Period.ofMonths(1); + assertThat(PeriodStyle.SIMPLE.print(period)).isEqualTo("0d"); + } + + @Test + void printSimpleWithUnitShouldPrintInUnit() { + Period period = Period.ofYears(1000); + assertThat(PeriodStyle.SIMPLE.print(period, ChronoUnit.YEARS)).isEqualTo("1000y"); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java index bd7f1d1b0eb0..2191426a8ff8 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java @@ -18,8 +18,11 @@ import org.junit.jupiter.params.provider.Arguments; import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.TypeDescriptor; +import java.time.Duration; import java.time.Period; +import java.time.temporal.ChronoUnit; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -37,6 +40,21 @@ void convertWithoutStyleShouldReturnIso8601(ConversionService conversionService) assertThat(converted).isEqualTo(Period.ofDays(1).toString()); } + @ConversionServiceTest + void convertWithFormatShouldUseFormatAndDays(ConversionService conversionService) { + String converted = (String) conversionService.convert(Period.ofMonths(1), + MockPeriodTypeDescriptor.get(null, PeriodStyle.SIMPLE), TypeDescriptor.valueOf(String.class)); + assertThat(converted).isEqualTo("0d"); + } + + @ConversionServiceTest + void convertWithFormatAndUnitShouldUseFormatAndUnit(ConversionService conversionService) { + String converted = (String) conversionService.convert(Period.ofYears(1), + MockPeriodTypeDescriptor.get(ChronoUnit.YEARS, PeriodStyle.SIMPLE), + TypeDescriptor.valueOf(String.class)); + assertThat(converted).isEqualTo("1y"); + } + static Stream conversionServices() throws Exception { return ConversionServiceArguments.with(new PeriodToStringConverter()); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/StringToPeriodConverterTest.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/StringToPeriodConverterTest.java index 47f9795b8c21..55ebe3b301a5 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/StringToPeriodConverterTest.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/StringToPeriodConverterTest.java @@ -18,8 +18,10 @@ import org.junit.jupiter.params.provider.Arguments; import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.TypeDescriptor; import java.time.Period; +import java.time.temporal.ChronoUnit; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -27,6 +29,7 @@ /** * Tests for {@link StringToPeriodConverter}. * + * @author Eddú Meléndez * @author Edson Chávez */ public class StringToPeriodConverterTest { @@ -43,10 +46,53 @@ void convertWhenIso8601ShouldReturnPeriod(ConversionService conversionService) { assertThat(convert(conversionService, "-P1Y2M")).isEqualTo(Period.parse("-P1Y2M")); } + @ConversionServiceTest + void convertWhenSimpleDaysShouldReturnPeriod(ConversionService conversionService) { + assertThat(convert(conversionService, "10d")).isEqualTo(Period.ofDays(10)); + assertThat(convert(conversionService, "10D")).isEqualTo(Period.ofDays(10)); + assertThat(convert(conversionService, "+10d")).isEqualTo(Period.ofDays(10)); + assertThat(convert(conversionService, "-10D")).isEqualTo(Period.ofDays(-10)); + } + + @ConversionServiceTest + void convertWhenSimpleMonthsShouldReturnPeriod(ConversionService conversionService) { + assertThat(convert(conversionService, "10m")).isEqualTo(Period.ofMonths(10)); + assertThat(convert(conversionService, "10M")).isEqualTo(Period.ofMonths(10)); + assertThat(convert(conversionService, "+10m")).isEqualTo(Period.ofMonths(10)); + assertThat(convert(conversionService, "-10M")).isEqualTo(Period.ofMonths(-10)); + } + + @ConversionServiceTest + void convertWhenSimpleYearsShouldReturnPeriod(ConversionService conversionService) { + assertThat(convert(conversionService, "10y")).isEqualTo(Period.ofYears(10)); + assertThat(convert(conversionService, "10Y")).isEqualTo(Period.ofYears(10)); + assertThat(convert(conversionService, "+10y")).isEqualTo(Period.ofYears(10)); + assertThat(convert(conversionService, "-10Y")).isEqualTo(Period.ofYears(-10)); + } + + @ConversionServiceTest + void convertWhenSimpleWithoutSuffixShouldReturnPeriod(ConversionService conversionService) { + assertThat(convert(conversionService, "10")).isEqualTo(Period.ofDays(10)); + assertThat(convert(conversionService, "+10")).isEqualTo(Period.ofDays(10)); + assertThat(convert(conversionService, "-10")).isEqualTo(Period.ofDays(-10)); + } + + @ConversionServiceTest + void convertWhenSimpleWithoutSuffixButWithAnnotationShouldReturnPeriod(ConversionService conversionService) { + assertThat(convert(conversionService, "10", ChronoUnit.MONTHS, null)).isEqualTo(Period.ofMonths(10)); + assertThat(convert(conversionService, "+10", ChronoUnit.MONTHS, null)).isEqualTo(Period.ofMonths(10)); + assertThat(convert(conversionService, "-10", ChronoUnit.MONTHS, null)).isEqualTo(Period.ofMonths(-10)); + } + private Period convert(ConversionService conversionService, String source) { return conversionService.convert(source, Period.class); } + private Period convert(ConversionService conversionService, String source, ChronoUnit unit, PeriodStyle style) { + return (Period) conversionService.convert(source, TypeDescriptor.forObject(source), + MockPeriodTypeDescriptor.get(unit, style)); + } + static Stream conversionServices() { return ConversionServiceArguments.with(new StringToPeriodConverter()); } From fcecad693e89304b5d7c2e251e7a3a7b9807137a Mon Sep 17 00:00:00 2001 From: Grubhart Date: Sun, 26 Apr 2020 14:59:09 -0500 Subject: [PATCH 5/9] Add NumberToPeriodConverter support --- .../convert/ApplicationConversionService.java | 1 + .../boot/convert/NumberToPeriodConverter.java | 53 +++++++++++++ .../convert/NumberToPeriodConverterTests.java | 75 +++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/NumberToPeriodConverterTests.java diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/ApplicationConversionService.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/ApplicationConversionService.java index 186abb84cc04..6217d3887ed0 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/ApplicationConversionService.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/ApplicationConversionService.java @@ -114,6 +114,7 @@ public static void addApplicationConverters(ConverterRegistry registry) { registry.addConverter(new DurationToStringConverter()); registry.addConverter(new PeriodToStringConverter()); registry.addConverter(new NumberToDurationConverter()); + registry.addConverter(new NumberToPeriodConverter()); registry.addConverter(new DurationToNumberConverter()); registry.addConverter(new StringToDataSizeConverter()); registry.addConverter(new NumberToDataSizeConverter()); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java new file mode 100644 index 000000000000..d216f55af5b8 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012-2020 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 org.springframework.boot.convert; + +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.GenericConverter; + +import java.time.Duration; +import java.time.Period; +import java.util.Collections; +import java.util.Set; + +/** + * {@link Converter} to convert from a {@link Number} to a {@link Duration}. Supports + * {@link Duration#parse(CharSequence)} as well a more readable {@code 10s} form. + * + * @author Eddú Meléndez + * @author Edson Chávez + * + * @see DurationFormat + * @see DurationUnit + */ +final class NumberToPeriodConverter implements GenericConverter { + + private final StringToPeriodConverter delegate = new StringToPeriodConverter(); + + @Override + public Set getConvertibleTypes() { + return Collections.singleton(new ConvertiblePair(Number.class, Period.class)); + } + + @Override + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + return this.delegate.convert((source != null) ? source.toString() : null, TypeDescriptor.valueOf(String.class), + targetType); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/NumberToPeriodConverterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/NumberToPeriodConverterTests.java new file mode 100644 index 000000000000..d5242d9031c4 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/NumberToPeriodConverterTests.java @@ -0,0 +1,75 @@ +/* + * Copyright 2012-2020 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 org.springframework.boot.convert; + +import org.junit.jupiter.params.provider.Arguments; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.TypeDescriptor; + +import java.time.Period; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link NumberToPeriodConverter}. + * + * @author Eddú Meléndez + * @author Edson Chávez + */ +class NumberToPeriodConverterTests { + + @ConversionServiceTest + void convertWhenSimpleWithoutSuffixShouldReturnDuration(ConversionService conversionService) { + assertThat(convert(conversionService, 10)).isEqualTo(Period.ofDays(10)); + assertThat(convert(conversionService, +10)).isEqualTo(Period.ofDays(10)); + assertThat(convert(conversionService, -10)).isEqualTo(Period.ofDays(-10)); + } + + @ConversionServiceTest + void convertWhenSimpleWithoutSuffixButWithAnnotationShouldReturnDuration(ConversionService conversionService) { + assertThat(convert(conversionService, 10, ChronoUnit.MONTHS)).isEqualTo(Period.ofMonths(10)); + assertThat(convert(conversionService, +10, ChronoUnit.MONTHS)).isEqualTo(Period.ofMonths(10)); + assertThat(convert(conversionService, -10, ChronoUnit.MONTHS)).isEqualTo(Period.ofMonths(-10)); + } + + private Period convert(ConversionService conversionService, Integer source) { + return conversionService.convert(source, Period.class); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private Period convert(ConversionService conversionService, Integer source, ChronoUnit defaultUnit) { + TypeDescriptor targetType = mock(TypeDescriptor.class); + if (defaultUnit != null) { + PeriodUnit unitAnnotation = AnnotationUtils + .synthesizeAnnotation(Collections.singletonMap("value", defaultUnit), PeriodUnit.class, null); + given(targetType.getAnnotation(PeriodUnit.class)).willReturn(unitAnnotation); + } + given(targetType.getType()).willReturn((Class) Period.class); + return (Period) conversionService.convert(source, TypeDescriptor.forObject(source), targetType); + } + + static Stream conversionServices() { + return ConversionServiceArguments.with(new NumberToPeriodConverter()); + } + +} From eb8a01176aaae33ed4e414ef0b183bd813ea02f9 Mon Sep 17 00:00:00 2001 From: Grubhart Date: Sun, 26 Apr 2020 19:35:43 -0500 Subject: [PATCH 6/9] Polish: Fix Comments, fix Imports and added @author mention --- .../boot/convert/NumberToPeriodConverter.java | 9 +- .../boot/convert/PeriodFormat.java | 9 +- .../boot/convert/PeriodStyle.java | 86 ++++--------------- .../boot/convert/PeriodToStringConverter.java | 27 +++++- .../boot/convert/PeriodUnit.java | 10 ++- .../boot/convert/StringToPeriodConverter.java | 3 +- .../convert/MockPeriodTypeDescriptor.java | 5 +- .../convert/NumberToPeriodConverterTests.java | 4 +- ...dStyleTests.java => PeriodStyleTests.java} | 2 +- .../convert/PeriodToStringConverterTests.java | 2 +- 10 files changed, 69 insertions(+), 88 deletions(-) rename spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/{PereiodStyleTests.java => PeriodStyleTests.java} (99%) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java index d216f55af5b8..75da0e526d32 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java @@ -20,20 +20,19 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.GenericConverter; -import java.time.Duration; import java.time.Period; import java.util.Collections; import java.util.Set; /** - * {@link Converter} to convert from a {@link Number} to a {@link Duration}. Supports - * {@link Duration#parse(CharSequence)} as well a more readable {@code 10s} form. + * {@link Converter} to convert from a {@link Number} to a {@link Period}. Supports + * {@link Period#parse(CharSequence)} as well a more readable {@code 10s} form. * * @author Eddú Meléndez * @author Edson Chávez * - * @see DurationFormat - * @see DurationUnit + * @see PeriodFormat + * @see PeriodUnit */ final class NumberToPeriodConverter implements GenericConverter { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java index 7b07175a3b13..782dbbc0df0b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java @@ -16,7 +16,12 @@ package org.springframework.boot.convert; -import java.lang.annotation.*; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Documented; +import java.time.Period; /** * Annotation that can be used to indicate the format to use when converting a @@ -32,7 +37,7 @@ public @interface PeriodFormat { /** - * The Period format style. + * The {@link Period} format style. * @return the period format style. */ PeriodStyle value(); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java index e3561f8887af..af8e179bbafd 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java @@ -18,7 +18,6 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; -import org.springframework.util.unit.DataSize; import java.time.Period; import java.time.temporal.ChronoUnit; @@ -27,53 +26,7 @@ import java.util.regex.Pattern; /** - * A standard set of {@link DataSize} units. - * - *

- * The unit prefixes used in this class are - * binary prefixes indicating - * multiplication by powers of 2. The following table displays the enum constants defined - * in this class and corresponding values. - * - *

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
ConstantData SizePower of 2Size in Bytes
{@link #BYTES}1B2^01
{@link #KILOBYTES}1KB2^101,024
{@link #MEGABYTES}1MB2^201,048,576
{@link #GIGABYTES}1GB2^301,073,741,824
{@link #TERABYTES}1TB2^401,099,511,627,776
+ * A standard set of {@link Period} units. * * @author Eddú Meléndez * @author Edson Chávez @@ -83,7 +36,7 @@ public enum PeriodStyle { /** - * Simple formatting, for example '1s'. + * Simple formatting, for example '1d'. */ SIMPLE("^([\\+\\-]?\\d+)([a-zA-Z]{0,2})$") { @@ -145,25 +98,25 @@ protected final Matcher matcher(String value) { } /** - * Parse the given value to a duration. + * Parse the given value to a Period. * @param value the value to parse - * @return a duration + * @return a period */ public Period parse(String value) { return parse(value, null); } /** - * Parse the given value to a duration. + * Parse the given value to a period. * @param value the value to parse - * @param unit the duration unit to use if the value doesn't specify one ({@code null} - * will default to ms) - * @return a duration + * @param unit the period unit to use if the value doesn't specify one ({@code null} + * will default to d) + * @return a period */ public abstract Period parse(String value, ChronoUnit unit); /** - * Print the specified duration. + * Print the specified period. * @param value the value to print * @return the printed result */ @@ -172,7 +125,7 @@ public String print(Period value) { } /** - * Print the specified duration using the given unit. + * Print the specified period using the given unit. * @param value the value to print * @param unit the value to use for printing * @return the printed result @@ -190,11 +143,11 @@ public static Period detectAndParse(String value) { } /** - * Detect the style then parse the value to return a duration. + * Detect the style then parse the value to return a period. * @param value the value to parse - * @param unit the duration unit to use if the value doesn't specify one ({@code null} + * @param unit the period unit to use if the value doesn't specify one ({@code null} * will default to ms) - * @return the parsed duration + * @return the parsed period * @throws IllegalStateException if the value is not a known style or cannot be parsed */ public static Period detectAndParse(String value, ChronoUnit unit) { @@ -204,7 +157,7 @@ public static Period detectAndParse(String value, ChronoUnit unit) { /** * Detect the style from the given source value. * @param value the source value - * @return the duration style + * @return the period style * @throws IllegalStateException if the value is not a known style */ public static PeriodStyle detect(String value) { @@ -220,22 +173,17 @@ public static PeriodStyle detect(String value) { enum Unit { /** - * Days, represented by suffix {@code B}. + * Days, represented by suffix {@code d}. */ DAYS(ChronoUnit.DAYS, "d", Period::getDays), /** - * Days, represented by suffix {@code B}. - */ - // WEEKS(ChronoUnit.WEEKS,"w", Period::ofWeeks), - - /** - * Days, represented by suffix {@code B}. + * Months, represented by suffix {@code m}. */ MONTHS(ChronoUnit.MONTHS, "m", Period::getMonths), /** - * Days, represented by suffix {@code B}. + * Years, represented by suffix {@code y}. */ YEARS(ChronoUnit.YEARS, "y", Period::getYears); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java index 13805e275d0e..b581aaa5241f 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java @@ -1,16 +1,39 @@ +/* + * Copyright 2002-2020 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 org.springframework.boot.convert; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.GenericConverter; import org.springframework.util.ObjectUtils; -import java.time.Duration; -import java.time.Period; import java.time.Period; import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.Set; +/** + * {@link Converter} to convert from a {@link Period} to a {@link String}. + * + * @author Eddú Meléndez + * @author Edson Chávez + * @since 2.3.0 + * @see Period + */ public class PeriodToStringConverter implements GenericConverter { @Override diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java index ceb3ca73e8ee..43153452970f 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java @@ -16,14 +16,20 @@ package org.springframework.boot.convert; -import java.lang.annotation.*; -import java.time.Duration; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.Documented; + +import java.time.Period; import java.time.temporal.ChronoUnit; /** * Annotation that can be used to change the default unit used when converting a * {@link Period}. * + * @author Eddú Meléndez * @author Edson Chávez * @since 2.3.0 */ diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java index a481422cfa5b..7df59684bd32 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java @@ -28,8 +28,9 @@ /** * {@link Converter} to convert from a {@link String} to a {@link Period}. Supports - * {@link Period#parse(CharSequence)} as well a more readable {@code 10s} form. + * {@link Period#parse(CharSequence)} as well a more readable form. * + * @author Eddú Meléndez * @author Edson Chávez * @see PeriodUnit */ diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/MockPeriodTypeDescriptor.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/MockPeriodTypeDescriptor.java index e5d9e7838f28..1a7c1cece845 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/MockPeriodTypeDescriptor.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/MockPeriodTypeDescriptor.java @@ -19,7 +19,6 @@ import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.convert.TypeDescriptor; -import java.time.Duration; import java.time.Period; import java.time.temporal.ChronoUnit; import java.util.Collections; @@ -28,8 +27,8 @@ import static org.mockito.Mockito.mock; /** - * Create a mock {@link TypeDescriptor} with optional {@link PeriodUnit @DurationUnit} and - * {@link PeriodFormat @DurationFormat} annotations. + * Create a mock {@link TypeDescriptor} with optional {@link PeriodUnit @PeriodUnit} and + * {@link PeriodFormat @PeriodFormat} annotations. * * @author Eddú Meléndez * @author Edson Chávez diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/NumberToPeriodConverterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/NumberToPeriodConverterTests.java index d5242d9031c4..175a2e19ef9c 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/NumberToPeriodConverterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/NumberToPeriodConverterTests.java @@ -39,14 +39,14 @@ class NumberToPeriodConverterTests { @ConversionServiceTest - void convertWhenSimpleWithoutSuffixShouldReturnDuration(ConversionService conversionService) { + void convertWhenSimpleWithoutSuffixShouldReturnPeriod(ConversionService conversionService) { assertThat(convert(conversionService, 10)).isEqualTo(Period.ofDays(10)); assertThat(convert(conversionService, +10)).isEqualTo(Period.ofDays(10)); assertThat(convert(conversionService, -10)).isEqualTo(Period.ofDays(-10)); } @ConversionServiceTest - void convertWhenSimpleWithoutSuffixButWithAnnotationShouldReturnDuration(ConversionService conversionService) { + void convertWhenSimpleWithoutSuffixButWithAnnotationShouldReturnPeriod(ConversionService conversionService) { assertThat(convert(conversionService, 10, ChronoUnit.MONTHS)).isEqualTo(Period.ofMonths(10)); assertThat(convert(conversionService, +10, ChronoUnit.MONTHS)).isEqualTo(Period.ofMonths(10)); assertThat(convert(conversionService, -10, ChronoUnit.MONTHS)).isEqualTo(Period.ofMonths(-10)); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PereiodStyleTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodStyleTests.java similarity index 99% rename from spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PereiodStyleTests.java rename to spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodStyleTests.java index 19e7ae91b091..8bdd9cf9f600 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PereiodStyleTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodStyleTests.java @@ -30,7 +30,7 @@ * @author Eddú Meléndez * @author Edson Chávez */ -class PereiodStyleTests { +class PeriodStyleTests { @Test void detectAndParseWhenValueIsNullShouldThrowException() { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java index 2191426a8ff8..9e973385cb7d 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java @@ -20,7 +20,6 @@ import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; -import java.time.Duration; import java.time.Period; import java.time.temporal.ChronoUnit; import java.util.stream.Stream; @@ -30,6 +29,7 @@ /** * Tests for {@link DurationToStringConverter}. * + * @author Eddú Melendez * @author Edson Chávez */ class PeriodToStringConverterTests { From bcc9fb65d85500814c2749daad1d5f5e0d4f421b Mon Sep 17 00:00:00 2001 From: Grubhart Date: Sun, 26 Apr 2020 20:26:53 -0500 Subject: [PATCH 7/9] Format --- .../springframework/boot/convert/NumberToPeriodConverter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java index 75da0e526d32..ecee65e558ed 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java @@ -30,7 +30,6 @@ * * @author Eddú Meléndez * @author Edson Chávez - * * @see PeriodFormat * @see PeriodUnit */ From 9d8a9c202be63255db7bda37a90f62b056783276 Mon Sep 17 00:00:00 2001 From: Grubhart Date: Sun, 26 Apr 2020 20:37:40 -0500 Subject: [PATCH 8/9] Polish, fix typos, and format --- .../boot/convert/NumberToPeriodConverter.java | 2 +- .../java/org/springframework/boot/convert/PeriodStyle.java | 6 ++---- .../boot/convert/PeriodToStringConverterTests.java | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java index ecee65e558ed..5c9d229aa003 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java @@ -26,7 +26,7 @@ /** * {@link Converter} to convert from a {@link Number} to a {@link Period}. Supports - * {@link Period#parse(CharSequence)} as well a more readable {@code 10s} form. + * {@link Period#parse(CharSequence)} as well a more readable {@code 10m} form. * * @author Eddú Meléndez * @author Edson Chávez diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java index af8e179bbafd..1f453ec8505f 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java @@ -133,9 +133,9 @@ public String print(Period value) { public abstract String print(Period value, ChronoUnit unit); /** - * Detect the style then parse the value to return a duration. + * Detect the style then parse the value to return a period. * @param value the value to parse - * @return the parsed duration + * @return the parsed period * @throws IllegalStateException if the value is not a known style or cannot be parsed */ public static Period detectAndParse(String value) { @@ -216,7 +216,6 @@ public static Unit fromSuffix(String suffix) { } public Period parse(String value) { - int intValue = Integer.parseInt(value); if (ChronoUnit.DAYS == this.chronoUnit) { @@ -231,7 +230,6 @@ else if (ChronoUnit.MONTHS == this.chronoUnit) { else if (ChronoUnit.YEARS == this.chronoUnit) { return Period.ofYears(intValue); } - throw new IllegalArgumentException("Unknow unit '" + this.chronoUnit + "'"); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java index 9e973385cb7d..da803cf47ad7 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java @@ -27,7 +27,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link DurationToStringConverter}. + * Tests for {@link PeriodToStringConverter}. * * @author Eddú Melendez * @author Edson Chávez From e86b4114af770a88df9b7917ebbb48acc3e58405 Mon Sep 17 00:00:00 2001 From: Grubhart Date: Sun, 26 Apr 2020 23:02:12 -0500 Subject: [PATCH 9/9] Polish, sorting imports --- .../boot/convert/NumberToPeriodConverter.java | 8 ++++---- .../springframework/boot/convert/PeriodFormat.java | 4 ++-- .../org/springframework/boot/convert/PeriodStyle.java | 6 +++--- .../boot/convert/PeriodToStringConverter.java | 10 +++++----- .../org/springframework/boot/convert/PeriodUnit.java | 3 +-- .../boot/convert/StringToPeriodConverter.java | 11 ++++++----- .../boot/convert/MockPeriodTypeDescriptor.java | 6 +++--- .../boot/convert/NumberToPeriodConverterTests.java | 11 ++++++----- .../boot/convert/PeriodStyleTests.java | 4 ++-- .../boot/convert/PeriodToStringConverterTests.java | 9 +++++---- .../boot/convert/StringToPeriodConverterTest.java | 9 +++++---- 11 files changed, 42 insertions(+), 39 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java index 5c9d229aa003..db7bb7c707fe 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/NumberToPeriodConverter.java @@ -16,14 +16,14 @@ package org.springframework.boot.convert; -import org.springframework.core.convert.TypeDescriptor; -import org.springframework.core.convert.converter.Converter; -import org.springframework.core.convert.converter.GenericConverter; - import java.time.Period; import java.util.Collections; import java.util.Set; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.GenericConverter; + /** * {@link Converter} to convert from a {@link Number} to a {@link Period}. Supports * {@link Period#parse(CharSequence)} as well a more readable {@code 10m} form. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java index 782dbbc0df0b..45d45183b812 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodFormat.java @@ -16,11 +16,11 @@ package org.springframework.boot.convert; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.lang.annotation.ElementType; -import java.lang.annotation.Documented; import java.time.Period; /** diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java index 1f453ec8505f..c1e2dfc1fec7 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodStyle.java @@ -16,15 +16,15 @@ package org.springframework.boot.convert; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - import java.time.Period; import java.time.temporal.ChronoUnit; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + /** * A standard set of {@link Period} units. * diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java index b581aaa5241f..c157c05cd725 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodToStringConverter.java @@ -16,16 +16,16 @@ package org.springframework.boot.convert; -import org.springframework.core.convert.TypeDescriptor; -import org.springframework.core.convert.converter.Converter; -import org.springframework.core.convert.converter.GenericConverter; -import org.springframework.util.ObjectUtils; - import java.time.Period; import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.Set; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.GenericConverter; +import org.springframework.util.ObjectUtils; + /** * {@link Converter} to convert from a {@link Period} to a {@link String}. * diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java index 43153452970f..e34c43948aff 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/PeriodUnit.java @@ -16,12 +16,11 @@ package org.springframework.boot.convert; +import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.lang.annotation.Documented; - import java.time.Period; import java.time.temporal.ChronoUnit; diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java index 7df59684bd32..e15bfd294680 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToPeriodConverter.java @@ -16,22 +16,23 @@ package org.springframework.boot.convert; -import org.springframework.core.convert.TypeDescriptor; -import org.springframework.core.convert.converter.Converter; -import org.springframework.core.convert.converter.GenericConverter; -import org.springframework.util.ObjectUtils; - import java.time.Period; import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.Set; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.GenericConverter; +import org.springframework.util.ObjectUtils; + /** * {@link Converter} to convert from a {@link String} to a {@link Period}. Supports * {@link Period#parse(CharSequence)} as well a more readable form. * * @author Eddú Meléndez * @author Edson Chávez + * @since 2.3.0 * @see PeriodUnit */ public class StringToPeriodConverter implements GenericConverter { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/MockPeriodTypeDescriptor.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/MockPeriodTypeDescriptor.java index 1a7c1cece845..599adffe8a07 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/MockPeriodTypeDescriptor.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/MockPeriodTypeDescriptor.java @@ -16,13 +16,13 @@ package org.springframework.boot.convert; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.core.convert.TypeDescriptor; - import java.time.Period; import java.time.temporal.ChronoUnit; import java.util.Collections; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.convert.TypeDescriptor; + import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/NumberToPeriodConverterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/NumberToPeriodConverterTests.java index 175a2e19ef9c..045715d3a7e0 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/NumberToPeriodConverterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/NumberToPeriodConverterTests.java @@ -16,16 +16,17 @@ package org.springframework.boot.convert; -import org.junit.jupiter.params.provider.Arguments; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.TypeDescriptor; - import java.time.Period; import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.stream.Stream; +import org.junit.jupiter.params.provider.Arguments; + +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.TypeDescriptor; + import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodStyleTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodStyleTests.java index 8bdd9cf9f600..0e9c08d2e4f7 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodStyleTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodStyleTests.java @@ -16,11 +16,11 @@ package org.springframework.boot.convert; -import org.junit.jupiter.api.Test; - import java.time.Period; import java.time.temporal.ChronoUnit; +import org.junit.jupiter.api.Test; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java index da803cf47ad7..78ec4760111f 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/PeriodToStringConverterTests.java @@ -16,14 +16,15 @@ package org.springframework.boot.convert; -import org.junit.jupiter.params.provider.Arguments; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.TypeDescriptor; - import java.time.Period; import java.time.temporal.ChronoUnit; import java.util.stream.Stream; +import org.junit.jupiter.params.provider.Arguments; + +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.TypeDescriptor; + import static org.assertj.core.api.Assertions.assertThat; /** diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/StringToPeriodConverterTest.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/StringToPeriodConverterTest.java index 55ebe3b301a5..62373c6c571c 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/StringToPeriodConverterTest.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/convert/StringToPeriodConverterTest.java @@ -16,14 +16,15 @@ package org.springframework.boot.convert; -import org.junit.jupiter.params.provider.Arguments; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.TypeDescriptor; - import java.time.Period; import java.time.temporal.ChronoUnit; import java.util.stream.Stream; +import org.junit.jupiter.params.provider.Arguments; + +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.TypeDescriptor; + import static org.assertj.core.api.Assertions.assertThat; /**