DelegatesTest.java
package tools.jackson.core.unittest.util;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import org.junit.jupiter.api.Test;
import tools.jackson.core.JsonGenerator;
import tools.jackson.core.JsonParser;
import tools.jackson.core.JsonPointer;
import tools.jackson.core.JsonToken;
import tools.jackson.core.JsonTokenId;
import tools.jackson.core.ObjectReadContext;
import tools.jackson.core.ObjectWriteContext;
import tools.jackson.core.StreamReadConstraints;
import tools.jackson.core.StreamReadFeature;
import tools.jackson.core.StreamWriteFeature;
import tools.jackson.core.TreeNode;
import tools.jackson.core.JsonParser.NumberType;
import tools.jackson.core.JsonParser.NumberTypeFP;
import tools.jackson.core.json.JsonFactory;
import tools.jackson.core.unittest.*;
import tools.jackson.core.util.JsonGeneratorDelegate;
import tools.jackson.core.util.JsonParserDelegate;
import static org.junit.jupiter.api.Assertions.*;
class DelegatesTest extends JacksonCoreTestBase
{
static class POJO {
public int x = 3;
}
static class BogusTree implements TreeNode {
@Override
public JsonToken asToken() {
return null;
}
@Override
public NumberType numberType() {
return null;
}
@Override
public int size() {
return 0;
}
@Override
public boolean isValueNode() {
return false;
}
@Override
public boolean isContainer() {
return false;
}
@Override
public boolean isMissingNode() {
return false;
}
@Override
public boolean isEmbeddedValue() {
return false;
}
@Override
public boolean isArray() {
return false;
}
@Override
public boolean isObject() {
return false;
}
@Override
public boolean isNull() {
return false;
}
@Override
public TreeNode get(String fieldName) {
return null;
}
@Override
public TreeNode get(int index) {
return null;
}
@Override
public TreeNode path(String fieldName) {
return null;
}
@Override
public TreeNode path(int index) {
return null;
}
@Override
public Collection<String> propertyNames() {
return null;
}
@Override
public TreeNode at(JsonPointer ptr) {
return null;
}
@Override
public TreeNode at(String jsonPointerExpression) {
return null;
}
@Override
public JsonParser traverse(ObjectReadContext readCtxt) {
return null;
}
}
private final JsonFactory JSON_F = new JsonFactory();
/**
* Test default, non-overridden parser delegate.
*/
@Test
void parserDelegate() throws IOException
{
final int MAX_NUMBER_LEN = 200;
StreamReadConstraints CUSTOM_CONSTRAINTS = StreamReadConstraints.builder()
.maxNumberLength(MAX_NUMBER_LEN)
.build();
JsonFactory jsonF = JsonFactory.builder()
.streamReadConstraints(CUSTOM_CONSTRAINTS)
.build();
JsonParser parser = jsonF.createParser(ObjectReadContext.empty(),
"[ 1, true, null, { \"a\": \"foo\" }, \"AQI=\" ]");
JsonParserDelegate del = new JsonParserDelegate(parser);
final String TOKEN = "foo";
// Basic capabilities for parser:
assertFalse(del.canParseAsync());
assertFalse(del.canReadObjectId());
assertFalse(del.canReadTypeId());
assertEquals(parser.version(), del.version());
assertSame(parser.streamReadConstraints(), del.streamReadConstraints());
assertEquals(MAX_NUMBER_LEN, parser.streamReadConstraints().getMaxNumberLength());
assertSame(parser.streamReadCapabilities(), del.streamReadCapabilities());
// configuration
assertFalse(del.isEnabled(StreamReadFeature.IGNORE_UNDEFINED));
assertSame(parser, del.delegate());
assertNull(del.getSchema());
// initial state
assertNull(del.currentToken());
assertFalse(del.hasCurrentToken());
assertFalse(del.hasStringCharacters());
assertNull(del.currentValue());
assertNull(del.currentName());
assertNull(del.getLastClearedToken());
assertToken(JsonToken.START_ARRAY, del.nextToken());
assertEquals(JsonTokenId.ID_START_ARRAY, del.currentTokenId());
assertTrue(del.hasToken(JsonToken.START_ARRAY));
assertFalse(del.hasToken(JsonToken.START_OBJECT));
assertTrue(del.hasTokenId(JsonTokenId.ID_START_ARRAY));
assertFalse(del.hasTokenId(JsonTokenId.ID_START_OBJECT));
assertTrue(del.isExpectedStartArrayToken());
assertFalse(del.isExpectedStartObjectToken());
assertFalse(del.isExpectedNumberIntToken());
assertEquals("[", del.getString());
assertNotNull(del.streamReadContext());
assertSame(parser.streamReadContext(), del.streamReadContext());
assertToken(JsonToken.VALUE_NUMBER_INT, del.nextToken());
assertEquals(1, del.getIntValue());
assertEquals(1, del.getValueAsInt());
assertEquals(1, del.getValueAsInt(3));
assertEquals(1L, del.getValueAsLong());
assertEquals(1L, del.getValueAsLong(3L));
assertEquals(1L, del.getLongValue());
assertEquals(1d, del.getValueAsDouble());
assertEquals(1d, del.getValueAsDouble(0.25));
assertEquals(1d, del.getDoubleValue());
assertTrue(del.getValueAsBoolean());
assertTrue(del.getValueAsBoolean(false));
assertEquals((byte)1, del.getByteValue());
assertEquals((short)1, del.getShortValue());
assertEquals(1f, del.getFloatValue());
assertFalse(del.isNaN());
assertTrue(del.isExpectedNumberIntToken());
assertEquals(NumberType.INT, del.getNumberType());
assertEquals(NumberTypeFP.UNKNOWN, del.getNumberTypeFP());
assertEquals(Integer.valueOf(1), del.getNumberValue());
assertNull(del.getEmbeddedObject());
assertToken(JsonToken.VALUE_TRUE, del.nextToken());
assertTrue(del.getBooleanValue());
assertEquals(parser.currentLocation(), del.currentLocation());
assertNull(del.getTypeId());
assertNull(del.getObjectId());
assertToken(JsonToken.VALUE_NULL, del.nextToken());
assertNull(del.currentValue());
del.assignCurrentValue(TOKEN);
assertToken(JsonToken.START_OBJECT, del.nextToken());
assertNull(del.currentValue());
assertToken(JsonToken.PROPERTY_NAME, del.nextToken());
assertEquals("a", del.currentName());
assertToken(JsonToken.VALUE_STRING, del.nextToken());
del.finishToken();
assertTrue(del.hasStringCharacters());
assertEquals("foo", del.getString());
assertEquals(3, del.getStringLength());
assertToken(JsonToken.END_OBJECT, del.nextToken());
assertEquals(TOKEN, del.currentValue());
assertToken(JsonToken.VALUE_STRING, del.nextToken());
assertArrayEquals(new byte[] { 1, 2 }, del.getBinaryValue());
assertToken(JsonToken.END_ARRAY, del.nextToken());
del.close();
assertTrue(del.isClosed());
assertTrue(parser.isClosed());
parser.close();
}
/**
* Test default, non-overridden generator delegate.
*/
@Test
void generatorDelegate() throws IOException
{
final String TOKEN ="foo";
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
// Basic capabilities for parser:
assertTrue(del.canOmitProperties());
assertFalse(del.canWriteObjectId());
assertFalse(del.canWriteTypeId());
assertEquals(g0.version(), del.version());
// configuration
assertFalse(del.isEnabled(StreamWriteFeature.IGNORE_UNKNOWN));
assertSame(g0, del.delegate());
// initial state
assertNull(del.getSchema());
del.writeStartArray();
assertEquals(1, del.streamWriteOutputBuffered());
del.writeNumber(13);
del.writeNumber(BigInteger.ONE);
del.writeNumber(new BigDecimal(0.5));
del.writeNumber("137");
del.writeNull();
del.writeBoolean(false);
del.writeString("foo");
// verify that we can actually set/get "current value" as expected, even with delegates
assertNull(del.currentValue());
del.assignCurrentValue(TOKEN);
del.writeStartObject(null, 0);
assertNull(del.currentValue());
del.writeEndObject();
assertEquals(TOKEN, del.currentValue());
del.writeStartArray(0);
del.writeEndArray();
del.writeEndArray();
del.flush();
del.close();
assertTrue(del.isClosed());
assertTrue(g0.isClosed());
assertEquals("[13,1,0.5,137,null,false,\"foo\",{},[]]", sw.toString());
g0.close();
}
@Test
void generatorDelegateArrays() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
final Object MARKER = new Object();
del.writeStartArray(MARKER);
assertSame(MARKER, del.currentValue());
del.writeArray(new int[] { 1, 2, 3 }, 0, 3);
del.writeArray(new long[] { 1, 123456, 2 }, 1, 1);
del.writeArray(new double[] { 0.25, 0.5, 0.75 }, 0, 2);
del.writeArray(new String[] { "Aa", "Bb", "Cc" }, 1, 2);
del.close();
assertEquals("[[1,2,3],[123456],[0.25,0.5],[\"Bb\",\"Cc\"]]", sw.toString());
g0.close();
}
@Test
void generatorDelegateComments() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
final Object MARKER = new Object();
del.writeStartArray(MARKER, 5);
assertSame(MARKER, del.currentValue());
del.writeNumber((short) 1);
del.writeNumber(12L);
del.writeNumber(0.25);
del.writeNumber(0.5f);
del.writeRawValue("/*foo*/");
del.writeRaw(" ");
del.close();
assertEquals("[1,12,0.25,0.5,/*foo*/ ]", sw.toString());
g0.close();
}
@Test
void delegateCopyMethods() throws IOException
{
JsonParser p = JSON_F.createParser(ObjectReadContext.empty(), "[123,[true,false]]");
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
assertToken(JsonToken.START_ARRAY, p.nextToken());
del.copyCurrentEvent(p);
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
del.copyCurrentStructure(p);
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_TRUE, p.nextToken());
assertToken(JsonToken.VALUE_FALSE, p.nextToken());
del.copyCurrentEvent(p);
g0.writeEndArray();
del.close();
g0.close();
p.close();
assertEquals("[123,false]", sw.toString());
}
@Test
void notDelegateCopyMethods() throws IOException
{
JsonParser p = JSON_F.createParser(ObjectReadContext.empty(), "[{\"a\":[1,2,{\"b\":3}],\"c\":\"d\"},{\"e\":false},null]");
StringWriter sw = new StringWriter();
JsonGenerator g = new JsonGeneratorDelegate(JSON_F.createGenerator(ObjectWriteContext.empty(), sw), false) {
@Override
public JsonGenerator writeName(String name) {
super.writeName(name+"-test");
super.writeBoolean(true);
super.writeName(name);
return this;
}
};
p.nextToken();
g.copyCurrentStructure(p);
g.flush();
assertEquals("[{\"a-test\":true,\"a\":[1,2,{\"b-test\":true,\"b\":3}],\"c-test\":true,\"c\":\"d\"},{\"e-test\":true,\"e\":false},null]", sw.toString());
p.close();
g.close();
}
@Test
void generatorDelegateWriteString() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
del.writeStartArray();
// writeString(String)
del.writeString("test");
// writeString(char[], int, int)
char[] chars = "hello world".toCharArray();
del.writeString(chars, 0, 5);
// writeString(SerializableString)
del.writeString(new tools.jackson.core.io.SerializedString("serialized"));
del.writeEndArray();
del.close();
assertEquals("[\"test\",\"hello\",\"serialized\"]", sw.toString());
g0.close();
}
@Test
void generatorDelegateWriteBinary() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
del.writeStartArray();
byte[] data = new byte[] { 1, 2, 3, 4, 5 };
del.writeBinary(data, 1, 3);
del.writeEndArray();
del.close();
// Binary should be base64 encoded
assertNotNull(sw.toString());
assertTrue(sw.toString().contains("[\""));
g0.close();
}
@Test
void generatorDelegateWriteRaw() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
del.writeStartArray();
// writeRaw(String)
del.writeRaw("123");
del.writeRaw(',');
// writeRaw(String, int, int)
del.writeRaw("456789", 0, 3);
del.writeRaw(',');
// writeRaw(char[], int, int)
char[] chars = "abc".toCharArray();
del.writeRaw(chars, 0, 3);
del.writeEndArray();
del.close();
assertEquals("[123,456,abc]", sw.toString());
g0.close();
}
@Test
void generatorDelegateWriteName() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
del.writeStartObject();
// writeName(String)
del.writeName("field1");
del.writeNumber(1);
// writeName(SerializableString)
del.writeName(new tools.jackson.core.io.SerializedString("field2"));
del.writeString("value");
del.writeEndObject();
del.close();
assertEquals("{\"field1\":1,\"field2\":\"value\"}", sw.toString());
g0.close();
}
@Test
void generatorDelegateConfigure() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
assertFalse(del.isEnabled(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN));
JsonGenerator result = del.configure(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN, true);
assertSame(del, result, "configure() should return the delegate, not the underlying generator");
assertTrue(del.isEnabled(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN));
assertTrue(g0.isEnabled(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN));
del.close();
g0.close();
}
@Test
void generatorDelegateReturnValues() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
// Verify that all write methods return the delegate (this), not the underlying generator
assertSame(del, del.writeStartArray());
assertSame(del, del.writeNumber(1));
assertSame(del, del.writeString("test"));
assertSame(del, del.writeBoolean(true));
assertSame(del, del.writeNull());
assertSame(del, del.writeEndArray());
assertSame(del, del.writeStartObject());
assertSame(del, del.writeName("field"));
assertSame(del, del.writeNumber(2));
assertSame(del, del.writeEndObject());
del.close();
g0.close();
}
@Test
void generatorDelegateCapabilities() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
// Test capability methods
assertSame(g0.streamWriteCapabilities(), del.streamWriteCapabilities());
// Test has() method for various capabilities
for (tools.jackson.core.StreamWriteCapability cap : tools.jackson.core.StreamWriteCapability.values()) {
assertEquals(g0.has(cap), del.has(cap), "Capability " + cap + " should match");
}
del.close();
g0.close();
}
@Test
void generatorDelegateContexts() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
// streamWriteContext should be delegated
assertSame(g0.streamWriteContext(), del.streamWriteContext());
del.writeStartArray();
assertSame(g0.streamWriteContext(), del.streamWriteContext());
// objectWriteContext should be delegated
assertSame(g0.objectWriteContext(), del.objectWriteContext());
del.close();
g0.close();
}
@Test
void generatorDelegateOutputInfo() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
// streamWriteOutputTarget should be delegated
assertSame(g0.streamWriteOutputTarget(), del.streamWriteOutputTarget());
del.writeStartArray();
del.writeNumber(123);
// streamWriteOutputBuffered should be delegated
assertEquals(g0.streamWriteOutputBuffered(), del.streamWriteOutputBuffered());
del.close();
g0.close();
}
@Test
void generatorDelegateWritePOJOWithDelegation() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0, true); // delegateCopyMethods = true
del.writeStartArray();
// null POJO should write null (works regardless of delegateCopyMethods)
del.writePOJO(null);
del.writeEndArray();
del.close();
assertEquals("[null]", sw.toString());
g0.close();
}
@Test
void generatorDelegateWriteTreeWithDelegation() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0, true); // delegateCopyMethods = true
del.writeStartArray();
// null tree should write null
del.writeTree(null);
del.writeEndArray();
del.close();
assertEquals("[null]", sw.toString());
g0.close();
}
@Test
void generatorDelegateNumberVariations() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
del.writeStartArray();
// Test all number writing variations
del.writeNumber((short) 1);
del.writeNumber(2);
del.writeNumber(3L);
del.writeNumber(BigInteger.valueOf(4));
del.writeNumber(5.0);
del.writeNumber(6.0f);
del.writeNumber(new BigDecimal("7.5"));
del.writeNumber("8");
// writeNumber(char[], int, int)
char[] numChars = "123".toCharArray();
del.writeNumber(numChars, 0, 3);
del.writeEndArray();
del.close();
assertEquals("[1,2,3,4,5.0,6.0,7.5,8,123]", sw.toString());
g0.close();
}
@Test
void generatorDelegateGettersAndSetters() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
// Test various getter methods
assertEquals(g0.getHighestNonEscapedChar(), del.getHighestNonEscapedChar());
assertEquals(g0.getCharacterEscapes(), del.getCharacterEscapes());
assertEquals(g0.getPrettyPrinter(), del.getPrettyPrinter());
assertEquals(g0.streamWriteFeatures(), del.streamWriteFeatures());
del.close();
g0.close();
}
@Test
void generatorDelegateFlushBehavior() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
del.writeStartArray();
del.writeNumber(1);
assertFalse(del.isClosed());
del.flush();
assertFalse(del.isClosed());
assertFalse(g0.isClosed());
// Should be flushed to output
assertTrue(sw.toString().startsWith("["));
del.close();
assertTrue(del.isClosed());
assertTrue(g0.isClosed());
g0.close();
}
@Test
void generatorDelegateUTF8Methods() throws IOException
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), out);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
del.writeStartArray();
byte[] utf8 = "hello".getBytes("UTF-8");
del.writeUTF8String(utf8, 0, utf8.length);
del.writeEndArray();
del.close();
String result = out.toString("UTF-8");
assertEquals("[\"hello\"]", result);
g0.close();
}
@Test
void generatorDelegateAccess() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
// Test delegate() method
assertSame(g0, del.delegate());
del.close();
g0.close();
}
@Test
void generatorDelegateWriteOmittedProperty() throws IOException
{
StringWriter sw = new StringWriter();
JsonGenerator g0 = JSON_F.createGenerator(ObjectWriteContext.empty(), sw);
JsonGeneratorDelegate del = new JsonGeneratorDelegate(g0);
del.writeStartObject();
del.writeName("visible");
del.writeNumber(1);
// writeOmittedProperty should be delegated
assertSame(del, del.writeOmittedProperty("omitted"));
del.writeEndObject();
del.close();
// Omitted property should not appear in output
assertEquals("{\"visible\":1}", sw.toString());
g0.close();
}
}