Skip to content

Commit 5d23127

Browse files
committed
Merge branch '2.x' into 3.x
2 parents 8dbf7d5 + 6504b46 commit 5d23127

12 files changed

Lines changed: 189 additions & 21 deletions

File tree

avro/src/main/java/module-info.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
module tools.jackson.dataformat.avro
33
{
44
requires transitive com.fasterxml.jackson.annotation;
5-
requires tools.jackson.core;
6-
requires tools.jackson.databind;
5+
requires transitive tools.jackson.core;
6+
requires transitive tools.jackson.databind;
77

88
requires org.apache.avro;
99

cbor/src/main/java/module-info.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// CBOR Main artifact Module descriptor
22
module tools.jackson.dataformat.cbor
33
{
4-
requires tools.jackson.core;
5-
requires tools.jackson.databind;
4+
requires transitive tools.jackson.core;
5+
requires transitive tools.jackson.databind;
66

77
exports tools.jackson.dataformat.cbor;
88

cbor/src/main/java/tools/jackson/dataformat/cbor/CBORConstants.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@ public final class CBORConstants
113113

114114
public final static int INT_BREAK = 0xFF;
115115

116+
/**
117+
* Marker for "undefined" value in CBOR spec.
118+
*
119+
* @since 2.20
120+
*/
121+
public final static int SIMPLE_VALUE_UNDEFINED = 0xF7;
122+
116123
/*
117124
/**********************************************************
118125
/* Basic UTF-8 decode/encode table

cbor/src/main/java/tools/jackson/dataformat/cbor/CBORFactory.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ public class CBORFactory
4949
*/
5050
final static int DEFAULT_CBOR_GENERATOR_FEATURE_FLAGS = CBORWriteFeature.collectDefaults();
5151

52+
/**
53+
* Bitfield (set of flags) of all generator features that are enabled
54+
* by default.
55+
*/
56+
final static int DEFAULT_CBOR_PARSER_FEATURE_FLAGS = CBORReadFeature.collectDefaults();
57+
5258
/*
5359
/**********************************************************************
5460
/* Symbol table management

cbor/src/main/java/tools/jackson/dataformat/cbor/CBORFactoryBuilder.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ protected CBORFactoryBuilder() {
2424
super(StreamReadConstraints.defaults(),
2525
StreamWriteConstraints.defaults(),
2626
ErrorReportConfiguration.defaults(),
27-
0,
27+
CBORFactory.DEFAULT_CBOR_PARSER_FEATURE_FLAGS,
2828
CBORFactory.DEFAULT_CBOR_GENERATOR_FEATURE_FLAGS);
2929
}
3030

@@ -64,6 +64,38 @@ public CBORFactoryBuilder configure(CBORWriteFeature f, boolean state) {
6464
return state ? enable(f) : disable(f);
6565
}
6666

67+
// // // Parser features
68+
69+
public CBORFactoryBuilder enable(CBORReadFeature f) {
70+
_formatReadFeatures |= f.getMask();
71+
return _this();
72+
}
73+
74+
public CBORFactoryBuilder enable(CBORReadFeature first, CBORReadFeature... other) {
75+
_formatReadFeatures |= first.getMask();
76+
for (CBORReadFeature f : other) {
77+
_formatReadFeatures |= f.getMask();
78+
}
79+
return _this();
80+
}
81+
82+
public CBORFactoryBuilder disable(CBORReadFeature f) {
83+
_formatReadFeatures &= ~f.getMask();
84+
return _this();
85+
}
86+
87+
public CBORFactoryBuilder disable(CBORReadFeature first, CBORReadFeature... other) {
88+
_formatReadFeatures &= ~first.getMask();
89+
for (CBORReadFeature f : other) {
90+
_formatReadFeatures &= ~f.getMask();
91+
}
92+
return _this();
93+
}
94+
95+
public CBORFactoryBuilder configure(CBORReadFeature f, boolean state) {
96+
return state ? enable(f) : disable(f);
97+
}
98+
6799
@Override
68100
public CBORFactory build() {
69101
// 28-Dec-2017, tatu: No special settings beyond base class ones, so:

cbor/src/main/java/tools/jackson/dataformat/cbor/CBORParser.java

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1962,6 +1962,25 @@ private final byte[] _getBinaryFromString(Base64Variant variant) throws JacksonE
19621962
return _binaryValue;
19631963
}
19641964

1965+
/**
1966+
* Checking whether the current token represents an `undefined` value (0xF7).
1967+
* <p>
1968+
* This method allows distinguishing between real {@code null} and `undefined`,
1969+
* even if {@link CBORReadFeature#HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT} is disabled
1970+
* and the token is reported as {@link JsonToken#VALUE_NULL}.
1971+
*
1972+
* @return {@code true} if current token is an `undefined`, {@code false} otherwise
1973+
*
1974+
* @since 2.20
1975+
*/
1976+
public boolean isUndefined() {
1977+
if ((_currToken == JsonToken.VALUE_NULL) || (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT)) {
1978+
return (_inputBuffer != null)
1979+
&& (_inputBuffer[_inputPtr - 1] & 0xFF) == SIMPLE_VALUE_UNDEFINED;
1980+
}
1981+
return false;
1982+
}
1983+
19651984
/*
19661985
/**********************************************************************
19671986
/* Numeric accessors of public API
@@ -3660,11 +3679,22 @@ private final static long _long(int i1, int i2)
36603679
* Helper method to encapsulate details of handling of mysterious `undefined` value
36613680
* that is allowed to be used as something encoder could not handle (as per spec),
36623681
* whatever the heck that should be.
3663-
* Current definition for 2.9 is that we will be return {@link JsonToken#VALUE_NULL}, but
3664-
* for later versions it is likely that we will alternatively allow decoding as
3665-
* {@link JsonToken#VALUE_EMBEDDED_OBJECT} with "embedded value" of `null`.
3682+
* <p>
3683+
* For backward compatibility with Jackson 2.10 to 2.19, this value is decoded
3684+
* as {@link JsonToken#VALUE_NULL} by default.
3685+
* <p>
3686+
*
3687+
* since 2.20 If {@link CBORReadFeature#HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT} is enabled,
3688+
* the value will instead be decoded as {@link JsonToken#VALUE_EMBEDDED_OBJECT}
3689+
* with an embedded value of {@code null}.
3690+
*
3691+
* @since 2.10
36663692
*/
3667-
protected JsonToken _decodeUndefinedValue() throws JacksonException {
3693+
protected JsonToken _decodeUndefinedValue() {
3694+
if (CBORReadFeature.HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT.enabledIn(_formatFeatures)) {
3695+
_binaryValue = null; // should be clear but just in case
3696+
return JsonToken.VALUE_EMBEDDED_OBJECT;
3697+
}
36683698
return JsonToken.VALUE_NULL;
36693699
}
36703700

cbor/src/main/java/tools/jackson/dataformat/cbor/CBORReadFeature.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package tools.jackson.dataformat.cbor;
22

33
import tools.jackson.core.FormatFeature;
4+
import tools.jackson.core.JsonToken;
45

56
/**
67
* Enumeration that defines all togglable features for CBOR parser.
@@ -25,6 +26,20 @@ public enum CBORReadFeature implements FormatFeature
2526
* The default value is {@code false} for backwards compatibility.
2627
*/
2728
DECODE_USING_STANDARD_NEGATIVE_BIGINT_ENCODING(false),
29+
30+
/**
31+
* Feature that determines how an ` undefined ` value (0xF7) is decoded.
32+
* <p>
33+
* When enabled, the parser returns {@link JsonToken#VALUE_EMBEDDED_OBJECT} with a
34+
* value of {@code null}, allowing the caller to distinguish `undefined` from actual
35+
* {@link JsonToken#VALUE_NULL}.
36+
*<p>
37+
* When disabled (default, for backwards compatibility), `undefined` value is
38+
* reported as {@link JsonToken#VALUE_NULL}, maintaining legacy behavior from Jackson 2.10 to 2.19.
39+
*
40+
* @since 2.20
41+
*/
42+
HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT(false)
2843
;
2944

3045
private final boolean _defaultState;

cbor/src/test/java/tools/jackson/dataformat/cbor/parse/UndefinedValueTest.java

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,35 @@
1212

1313
import static org.junit.jupiter.api.Assertions.assertEquals;
1414
import static org.junit.jupiter.api.Assertions.assertNull;
15+
import static org.junit.jupiter.api.Assertions.assertTrue;
1516

1617
// for [dataformat-binary#93]
1718
public class UndefinedValueTest extends CBORTestBase
1819
{
19-
private final static byte BYTE_UNDEFINED = (byte) 0xF7;
20+
private final static byte BYTE_UNDEFINED = (byte) CBORConstants.SIMPLE_VALUE_UNDEFINED;
21+
22+
private final CBORFactory CBOR_F = cborFactory();
2023

2124
@Test
2225
public void testUndefinedLiteralStreaming() throws Exception
2326
{
24-
JsonParser p = cborParser(new byte[] { BYTE_UNDEFINED });
25-
assertEquals(JsonToken.VALUE_NULL, p.nextToken());
27+
try (CBORParser p = cborParser(new byte[] { BYTE_UNDEFINED })) {
28+
assertEquals(JsonToken.VALUE_NULL, p.nextToken());
29+
assertTrue(p.isUndefined());
30+
assertNull(p.nextToken());
31+
}
32+
}
33+
34+
// @since 2.20 [jackson-dataformats-binary/137]
35+
@Test
36+
public void testUndefinedLiteralAsEmbeddedObject() throws Exception {
37+
CBORFactory f = CBORFactory.builder()
38+
.enable(CBORReadFeature.HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT)
39+
.build();
40+
CBORParser p = cborParser(f, new byte[] { BYTE_UNDEFINED });
41+
42+
assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
43+
assertTrue(p.isUndefined());
2644
assertNull(p.nextToken());
2745
p.close();
2846
}
@@ -34,9 +52,30 @@ public void testUndefinedInArray() throws Exception
3452
out.write(CBORConstants.BYTE_ARRAY_INDEFINITE);
3553
out.write(BYTE_UNDEFINED);
3654
out.write(CBORConstants.BYTE_BREAK);
37-
JsonParser p = cborParser(out.toByteArray());
55+
try (CBORParser p = cborParser(CBOR_F, out.toByteArray())) {
56+
assertEquals(JsonToken.START_ARRAY, p.nextToken());
57+
assertEquals(JsonToken.VALUE_NULL, p.nextToken());
58+
assertTrue(p.isUndefined());
59+
assertEquals(JsonToken.END_ARRAY, p.nextToken());
60+
assertNull(p.nextToken());
61+
}
62+
}
63+
64+
// @since 2.20 [jackson-dataformats-binary/137]
65+
@Test
66+
public void testUndefinedInArrayAsEmbeddedObject() throws Exception {
67+
CBORFactory f = CBORFactory.builder()
68+
.enable(CBORReadFeature.HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT)
69+
.build();
70+
71+
ByteArrayOutputStream out = new ByteArrayOutputStream();
72+
out.write(CBORConstants.BYTE_ARRAY_INDEFINITE);
73+
out.write(BYTE_UNDEFINED);
74+
out.write(CBORConstants.BYTE_BREAK);
75+
CBORParser p = cborParser(f, out.toByteArray());
3876
assertEquals(JsonToken.START_ARRAY, p.nextToken());
39-
assertEquals(JsonToken.VALUE_NULL, p.nextToken());
77+
assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
78+
assertTrue(p.isUndefined());
4079
assertEquals(JsonToken.END_ARRAY, p.nextToken());
4180
assertNull(p.nextToken());
4281
p.close();
@@ -57,11 +96,42 @@ public void testUndefinedInObject() throws Exception
5796
// assume we use end marker for Object, so
5897
doc[doc.length-2] = BYTE_UNDEFINED;
5998

60-
JsonParser p = cborParser(doc);
99+
try (CBORParser p = cborParser(CBOR_F, doc)) {
100+
assertEquals(JsonToken.START_OBJECT, p.nextToken());
101+
assertEquals(JsonToken.PROPERTY_NAME, p.nextToken());
102+
assertEquals("bar", p.currentName());
103+
assertEquals(JsonToken.VALUE_NULL, p.nextToken());
104+
assertTrue(p.isUndefined());
105+
assertEquals(JsonToken.END_OBJECT, p.nextToken());
106+
assertNull(p.nextToken());
107+
}
108+
}
109+
110+
// @since 2.20 [jackson-dataformats-binary/137]
111+
@Test
112+
public void testUndefinedInObjectAsEmbeddedObject() throws Exception {
113+
CBORFactory f = CBORFactory.builder()
114+
.enable(CBORReadFeature.HANDLE_UNDEFINED_AS_EMBEDDED_OBJECT)
115+
.build();
116+
117+
ByteArrayOutputStream out = new ByteArrayOutputStream();
118+
CBORGenerator g = cborGenerator(out);
119+
g.writeStartObject();
120+
g.writeName("bar");
121+
g.writeBoolean(true);
122+
g.writeEndObject();
123+
g.close();
124+
125+
byte[] doc = out.toByteArray();
126+
// assume we use end marker for Object, so
127+
doc[doc.length - 2] = BYTE_UNDEFINED;
128+
129+
CBORParser p = cborParser(f, doc);
61130
assertEquals(JsonToken.START_OBJECT, p.nextToken());
62131
assertEquals(JsonToken.PROPERTY_NAME, p.nextToken());
63132
assertEquals("bar", p.currentName());
64-
assertEquals(JsonToken.VALUE_NULL, p.nextToken());
133+
assertEquals(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
134+
assertTrue(p.isUndefined());
65135
assertEquals(JsonToken.END_OBJECT, p.nextToken());
66136
assertNull(p.nextToken());
67137
p.close();

ion/src/main/java/module-info.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// Ion Main artifact Module descriptor
22
module tools.jackson.dataformat.ion
33
{
4-
requires tools.jackson.core;
5-
requires tools.jackson.databind;
6-
requires java.sql;
4+
requires transitive tools.jackson.core;
5+
requires transitive tools.jackson.databind;
6+
7+
requires java.sql; // why?
78

89
// ion-java has no explicit module-info; but automatic name is:
910
requires com.amazon.ion;

release-notes/CREDITS-2.x

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,9 @@ Brian Gruber (@bgruber)
391391
(2.20.0)
392392

393393
Fawzi Essam (@iifawzi)
394+
* Contributed implementation of #137: (cbor) Allow exposing CBOR "undefined" value as
395+
`JsonToken.VALUE_EMBEDDED_OBJECT`; with embedded value of `null`
396+
(2.20.0)
394397
* Contributed fix for #431: (cbor) Negative `BigInteger` values not encoded/decoded
395398
correctly
396399
(2.20.0)

0 commit comments

Comments
 (0)