Skip to content

Commit 92f3f6d

Browse files
authored
Merge PR #364 from 2.19 to master (3.0) (#366)
1 parent b3bf4f9 commit 92f3f6d

File tree

2 files changed

+75
-34
lines changed

2 files changed

+75
-34
lines changed

datetime/src/main/java/tools/jackson/datatype/jsr310/deser/OneBasedMonthDeserializer.java

+40-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package tools.jackson.datatype.jsr310.deser;
22

33
import java.time.Month;
4-
import java.util.regex.Pattern;
54

65
import tools.jackson.core.JsonParser;
76
import tools.jackson.core.JsonToken;
@@ -14,31 +13,58 @@
1413
* @since 2.17
1514
*/
1615
public class OneBasedMonthDeserializer extends DelegatingDeserializer {
17-
private static final Pattern HAS_ONE_OR_TWO_DIGITS = Pattern.compile("^\\d{1,2}$");
18-
1916
public OneBasedMonthDeserializer(ValueDeserializer<?> defaultDeserializer) {
2017
super(defaultDeserializer);
2118
}
2219

2320
@Override
2421
public Object deserialize(JsonParser parser, DeserializationContext context) {
2522
JsonToken token = parser.currentToken();
26-
Month zeroBaseMonth = (Month) getDelegatee().deserialize(parser, context);
27-
if (!_isNumericValue(parser.getString(), token)) {
28-
return zeroBaseMonth;
29-
}
30-
if (zeroBaseMonth == Month.JANUARY) {
31-
throw new InvalidFormatException(parser, "Month.JANUARY value not allowed for 1-based Month.", zeroBaseMonth, Month.class);
23+
switch (token) {
24+
case VALUE_NUMBER_INT:
25+
return _decodeMonth(parser.getIntValue(), parser);
26+
case VALUE_STRING:
27+
String monthSpec = parser.getString();
28+
int oneBasedMonthNumber = _decodeNumber(monthSpec);
29+
if (oneBasedMonthNumber >= 0) {
30+
return _decodeMonth(oneBasedMonthNumber, parser);
31+
}
32+
default:
33+
// Otherwise fall through to default handling
34+
break;
3235
}
33-
return zeroBaseMonth.minus(1);
36+
return getDelegatee().deserialize(parser, context);
3437
}
3538

36-
private boolean _isNumericValue(String text, JsonToken token) {
37-
return token == JsonToken.VALUE_NUMBER_INT || _isNumberAsString(text, token);
39+
/**
40+
* @return Numeric value of input text that represents a 1-digit or 2-digit number.
41+
* Negative value in other cases (empty string, not a number, 3 or more digits).
42+
*/
43+
private int _decodeNumber(String text) {
44+
int numValue;
45+
switch (text.length()) {
46+
case 1:
47+
char c = text.charAt(0);
48+
boolean cValid = ('0' <= c && c <= '9');
49+
numValue = cValid ? (c - '0') : -1;
50+
break;
51+
case 2:
52+
char c1 = text.charAt(0);
53+
char c2 = text.charAt(1);
54+
boolean c12valid = ('0' <= c1 && c1 <= '9' && '0' <= c2 && c2 <= '9');
55+
numValue = c12valid ? (10 * (c1 - '0') + (c2 - '0')) : -1;
56+
break;
57+
default:
58+
numValue = -1;
59+
}
60+
return numValue;
3861
}
3962

40-
private boolean _isNumberAsString(String text, JsonToken token) {
41-
return token == JsonToken.VALUE_STRING && HAS_ONE_OR_TWO_DIGITS.matcher(text).matches();
63+
private Month _decodeMonth(int oneBasedMonthNumber, JsonParser parser) throws InvalidFormatException {
64+
if (Month.JANUARY.getValue() <= oneBasedMonthNumber && oneBasedMonthNumber <= Month.DECEMBER.getValue()) {
65+
return Month.of(oneBasedMonthNumber);
66+
}
67+
throw new InvalidFormatException(parser, "Month number " + oneBasedMonthNumber + " not allowed for 1-based Month.", oneBasedMonthNumber, Integer.class);
4268
}
4369

4470
@Override

datetime/src/test/java/tools/jackson/datatype/jsr310/deser/OneBasedMonthDeserTest.java

+35-20
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
import org.junit.jupiter.api.function.Executable;
77
import org.junit.jupiter.api.Test;
8+
import org.junit.jupiter.params.ParameterizedTest;
9+
import org.junit.jupiter.params.provider.CsvSource;
10+
import org.junit.jupiter.params.provider.EnumSource;
811

912
import tools.jackson.databind.ObjectMapper;
1013
import tools.jackson.databind.ObjectReader;
@@ -29,39 +32,51 @@ static class Wrapper {
2932
public Wrapper() { }
3033
}
3134

32-
@Test
33-
public void testDeserializationAsString01_oneBased() throws Exception
35+
@ParameterizedTest
36+
@EnumSource(Month.class)
37+
public void testDeserializationAsString01_oneBased(Month expectedMonth) throws Exception
3438
{
35-
assertEquals(Month.JANUARY, readerForOneBased().readValue("\"1\""));
39+
int monthNum = expectedMonth.getValue();
40+
assertEquals(expectedMonth, readerForOneBased().readValue("\"" + monthNum + '"'));
3641
}
3742

38-
@Test
39-
public void testDeserializationAsString01_zeroBased() throws Exception
43+
@ParameterizedTest
44+
@EnumSource(Month.class)
45+
public void testDeserializationAsString01_zeroBased(Month expectedMonth) throws Exception
4046
{
41-
assertEquals(Month.FEBRUARY, readerForZeroBased().readValue("\"1\""));
47+
int monthNum = expectedMonth.ordinal();
48+
assertEquals(expectedMonth, readerForZeroBased().readValue("\"" + monthNum + '"'));
4249
}
4350

4451

45-
@Test
46-
public void testDeserializationAsString02_oneBased() throws Exception
52+
@ParameterizedTest
53+
@EnumSource(Month.class)
54+
public void testDeserializationAsString02_oneBased(Month month) throws Exception
4755
{
48-
assertEquals(Month.JANUARY, readerForOneBased().readValue("\"JANUARY\""));
56+
assertEquals(month, readerForOneBased().readValue("\"" + month.name() + '"'));
4957
}
5058

51-
@Test
52-
public void testDeserializationAsString02_zeroBased() throws Exception
59+
@ParameterizedTest
60+
@EnumSource(Month.class)
61+
public void testDeserializationAsString02_zeroBased(Month month) throws Exception
5362
{
54-
assertEquals(Month.JANUARY, readerForZeroBased().readValue("\"JANUARY\""));
55-
}
56-
57-
@Test
58-
public void testBadDeserializationAsString01_oneBased() {
63+
assertEquals(month, readerForOneBased().readValue("\"" + month.name() + '"'));
64+
}
65+
66+
@ParameterizedTest
67+
@CsvSource({
68+
"notamonth , 'Cannot deserialize value of type `java.time.Month` from String \"notamonth\": not one of the values accepted for Enum class:'",
69+
"JANUAR , 'Cannot deserialize value of type `java.time.Month` from String \"JANUAR\": not one of the values accepted for Enum class:'",
70+
"march , 'Cannot deserialize value of type `java.time.Month` from String \"march\": not one of the values accepted for Enum class:'",
71+
"0 , 'Month number 0 not allowed for 1-based Month.'",
72+
"13 , 'Month number 13 not allowed for 1-based Month.'",
73+
})
74+
public void testBadDeserializationAsString01_oneBased(String monthSpec, String expectedMessage) {
75+
String value = "\"" + monthSpec + '"';
5976
assertError(
60-
() -> readerForOneBased().readValue("\"notamonth\""),
77+
() -> readerForOneBased().readValue(value),
6178
InvalidFormatException.class,
62-
// Order of enumerated values not stable, so don't check:
63-
"Cannot deserialize value of type `java.time.Month` from String \"notamonth\":"
64-
+" not one of the values accepted for Enum class: ["
79+
expectedMessage
6580
);
6681
}
6782

0 commit comments

Comments
 (0)