From bf39492c34b20435a354864e535a53af2730c5fb Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 4 Jul 2022 18:22:06 +0200 Subject: [PATCH 1/3] Introduce StringUtils.trimAllWhitespace(CharSequence) Closes gh-28757 --- .../org/springframework/util/StringUtils.java | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/StringUtils.java b/spring-core/src/main/java/org/springframework/util/StringUtils.java index ef6e215a388..b4573a336b1 100644 --- a/spring-core/src/main/java/org/springframework/util/StringUtils.java +++ b/spring-core/src/main/java/org/springframework/util/StringUtils.java @@ -241,21 +241,23 @@ public abstract class StringUtils { } /** - * Trim all whitespace from the given {@code String}: + * Trim all whitespace from the given {@code CharSequence}: * leading, trailing, and in between characters. - * @param str the {@code String} to check - * @return the trimmed {@code String} + * @param text the {@code CharSequence} to check + * @return the trimmed {@code CharSequence} + * @since 5.3.22 + * @see #trimAllWhitespace(String) * @see java.lang.Character#isWhitespace */ - public static String trimAllWhitespace(String str) { - if (!hasLength(str)) { - return str; + public static CharSequence trimAllWhitespace(CharSequence text) { + if (!hasLength(text)) { + return text; } - int len = str.length(); - StringBuilder sb = new StringBuilder(str.length()); + int len = text.length(); + StringBuilder sb = new StringBuilder(text.length()); for (int i = 0; i < len; i++) { - char c = str.charAt(i); + char c = text.charAt(i); if (!Character.isWhitespace(c)) { sb.append(c); } @@ -263,6 +265,21 @@ public abstract class StringUtils { return sb.toString(); } + /** + * Trim all whitespace from the given {@code String}: + * leading, trailing, and in between characters. + * @param str the {@code String} to check + * @return the trimmed {@code String} + * @see #trimAllWhitespace(CharSequence) + * @see java.lang.Character#isWhitespace + */ + public static String trimAllWhitespace(String str) { + if (str == null) { + return null; + } + return trimAllWhitespace((CharSequence) str).toString(); + } + /** * Trim leading whitespace from the given {@code String}. * @param str the {@code String} to check From 2bf5f7a6b326e321c8d0922fdf0882108d12a650 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 4 Jul 2022 18:26:00 +0200 Subject: [PATCH 2/3] Introduce lenient parsing in DataSize regarding whitespace Prior to this commit, a DataSize input string could not be parsed if it contained any whitespace. With this commit, a DataSize input string can contain leading, trailing, or 'in between' whitespace. For example, the following will be parsed to the same DataSize value. - "1024B" - "1024 B" - " 1024B " - " 1024 B " Closes gh-28643 --- .../springframework/util/unit/DataSize.java | 2 +- .../util/unit/DataSizeTests.java | 26 ++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/unit/DataSize.java b/spring-core/src/main/java/org/springframework/util/unit/DataSize.java index 75183a18e05..bc57d140dd9 100644 --- a/spring-core/src/main/java/org/springframework/util/unit/DataSize.java +++ b/spring-core/src/main/java/org/springframework/util/unit/DataSize.java @@ -174,7 +174,7 @@ public final class DataSize implements Comparable, Serializable { public static DataSize parse(CharSequence text, @Nullable DataUnit defaultUnit) { Assert.notNull(text, "Text must not be null"); try { - Matcher matcher = DataSizeUtils.PATTERN.matcher(text); + Matcher matcher = DataSizeUtils.PATTERN.matcher(StringUtils.trimAllWhitespace(text)); Assert.state(matcher.matches(), "Does not match data size pattern"); DataUnit unit = DataSizeUtils.determineDataUnit(matcher.group(2), defaultUnit); long amount = Long.parseLong(matcher.group(1)); diff --git a/spring-core/src/test/java/org/springframework/util/unit/DataSizeTests.java b/spring-core/src/test/java/org/springframework/util/unit/DataSizeTests.java index 74e830c5f66..2224ce83087 100644 --- a/spring-core/src/test/java/org/springframework/util/unit/DataSizeTests.java +++ b/spring-core/src/test/java/org/springframework/util/unit/DataSizeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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. @@ -17,6 +17,8 @@ package org.springframework.util.unit; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -25,6 +27,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * Tests for {@link DataSize}. * * @author Stephane Nicoll + * @author Sam Brannen */ class DataSizeTests { @@ -128,9 +131,17 @@ class DataSizeTests { assertThat(DataSize.parse("-1", DataUnit.KILOBYTES)).isEqualTo(DataSize.ofKilobytes(-1)); } - @Test - void parseWithBytes() { - assertThat(DataSize.parse("1024B")).isEqualTo(DataSize.ofKilobytes(1)); + @ParameterizedTest(name = "[{index}] text = ''{0}''") + @ValueSource(strings = { + "1024B", + "1024 B", + "1024B ", + " 1024B", + " 1024B ", + "\t1024 B\t" + }) + void parseWithBytes(CharSequence text) { + assertThat(DataSize.parse(text)).isEqualTo(DataSize.ofKilobytes(1)); } @Test @@ -210,9 +221,12 @@ class DataSizeTests { @Test void parseWithUnsupportedUnit() { - assertThatIllegalArgumentException().isThrownBy(() -> - DataSize.parse("3WB")) + assertThatIllegalArgumentException() + .isThrownBy(() -> DataSize.parse("3WB")) .withMessage("'3WB' is not a valid data size"); + assertThatIllegalArgumentException() + .isThrownBy(() -> DataSize.parse("3 WB")) + .withMessage("'3 WB' is not a valid data size"); } } From 2c3243c93ce1d68151e17b6ee0aee64646cd38fd Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Mon, 4 Jul 2022 19:20:56 +0200 Subject: [PATCH 3/3] Trim string input in PropertyEditors where whitespace is irrelevant Closes gh-28755 --- .../beans/propertyeditors/CharsetEditor.java | 5 ++-- .../beans/propertyeditors/CurrencyEditor.java | 8 ++++++- .../beans/propertyeditors/TimeZoneEditor.java | 6 ++++- .../beans/propertyeditors/ZoneIdEditor.java | 8 ++++++- .../propertyeditors/ZoneIdEditorTests.java | 23 ++++++++++++------- 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CharsetEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CharsetEditor.java index adcb806e684..ef772db749c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CharsetEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CharsetEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2022 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. @@ -29,6 +29,7 @@ import org.springframework.util.StringUtils; * e.g. {@code UTF-8}, {@code ISO-8859-16}, etc. * * @author Arjen Poutsma + * @author Sam Brannen * @since 2.5.4 * @see Charset */ @@ -37,7 +38,7 @@ public class CharsetEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { if (StringUtils.hasText(text)) { - setValue(Charset.forName(text)); + setValue(Charset.forName(text.trim())); } else { setValue(null); diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CurrencyEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CurrencyEditor.java index 0d044ffe11a..b6b9d318afe 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CurrencyEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CurrencyEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2022 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,11 +19,14 @@ package org.springframework.beans.propertyeditors; import java.beans.PropertyEditorSupport; import java.util.Currency; +import org.springframework.util.StringUtils; + /** * Editor for {@code java.util.Currency}, translating currency codes into Currency * objects. Exposes the currency code as text representation of a Currency object. * * @author Juergen Hoeller + * @author Sam Brannen * @since 3.0 * @see java.util.Currency */ @@ -31,6 +34,9 @@ public class CurrencyEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { + if (StringUtils.hasText(text)) { + text = text.trim(); + } setValue(Currency.getInstance(text)); } diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/TimeZoneEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/TimeZoneEditor.java index 6b809169b96..3833c026812 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/TimeZoneEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/TimeZoneEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2022 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. @@ -28,6 +28,7 @@ import org.springframework.util.StringUtils; * * @author Juergen Hoeller * @author Nicholas Williams + * @author Sam Brannen * @since 3.0 * @see java.util.TimeZone * @see ZoneIdEditor @@ -36,6 +37,9 @@ public class TimeZoneEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { + if (StringUtils.hasText(text)) { + text = text.trim(); + } setValue(StringUtils.parseTimeZoneString(text)); } diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ZoneIdEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ZoneIdEditor.java index 912bdd5fb74..4992e33aebd 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ZoneIdEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ZoneIdEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2022 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,11 +19,14 @@ package org.springframework.beans.propertyeditors; import java.beans.PropertyEditorSupport; import java.time.ZoneId; +import org.springframework.util.StringUtils; + /** * Editor for {@code java.time.ZoneId}, translating zone ID Strings into {@code ZoneId} * objects. Exposes the {@code TimeZone} ID as a text representation. * * @author Nicholas Williams + * @author Sam Brannen * @since 4.0 * @see java.time.ZoneId * @see TimeZoneEditor @@ -32,6 +35,9 @@ public class ZoneIdEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { + if (StringUtils.hasText(text)) { + text = text.trim(); + } setValue(ZoneId.of(text)); } diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ZoneIdEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ZoneIdEditorTests.java index 7a9162314b2..b62f952fdb1 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ZoneIdEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ZoneIdEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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,19 +19,26 @@ package org.springframework.beans.propertyeditors; import java.time.ZoneId; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.assertj.core.api.Assertions.assertThat; /** * @author Nicholas Williams + * @author Sam Brannen */ -public class ZoneIdEditorTests { +class ZoneIdEditorTests { private final ZoneIdEditor editor = new ZoneIdEditor(); - @Test - public void americaChicago() { - editor.setAsText("America/Chicago"); + @ParameterizedTest(name = "[{index}] text = ''{0}''") + @ValueSource(strings = { + "America/Chicago", + " America/Chicago ", + }) + void americaChicago(String text) { + editor.setAsText(text); ZoneId zoneId = (ZoneId) editor.getValue(); assertThat(zoneId).as("The zone ID should not be null.").isNotNull(); @@ -41,7 +48,7 @@ public class ZoneIdEditorTests { } @Test - public void americaLosAngeles() { + void americaLosAngeles() { editor.setAsText("America/Los_Angeles"); ZoneId zoneId = (ZoneId) editor.getValue(); @@ -52,12 +59,12 @@ public class ZoneIdEditorTests { } @Test - public void getNullAsText() { + void getNullAsText() { assertThat(editor.getAsText()).as("The returned value is not correct.").isEqualTo(""); } @Test - public void getValueAsText() { + void getValueAsText() { editor.setValue(ZoneId.of("America/New_York")); assertThat(editor.getAsText()).as("The text version is not correct.").isEqualTo("America/New_York"); }