package com.fasterxml.jackson.databind.util;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.UUID;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.io.SerializedString;
import com.fasterxml.jackson.core.util.JsonParserSequence;
import com.fasterxml.jackson.databind.*;
public class TestTokenBuffer extends BaseMapTest
{
private final ObjectMapper MAPPER = objectMapper();
/*
/**********************************************************
/* Basic TokenBuffer tests
/**********************************************************
*/
public void testBasicConfig() throws IOException
{
TokenBuffer buf;
buf = new TokenBuffer(MAPPER, false);
assertEquals(MAPPER.version(), buf.version());
assertSame(MAPPER, buf.getCodec());
assertNotNull(buf.getOutputContext());
assertFalse(buf.isClosed());
buf.setCodec(null);
assertNull(buf.getCodec());
assertFalse(buf.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII));
buf.enable(JsonGenerator.Feature.ESCAPE_NON_ASCII);
assertTrue(buf.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII));
buf.disable(JsonGenerator.Feature.ESCAPE_NON_ASCII);
assertFalse(buf.isEnabled(JsonGenerator.Feature.ESCAPE_NON_ASCII));
buf.close();
assertTrue(buf.isClosed());
}
/**
* Test writing of individual simple values
*/
public void testSimpleWrites() throws IOException
{
TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec
// First, with empty buffer
JsonParser p = buf.asParser();
assertNull(p.getCurrentToken());
assertNull(p.nextToken());
p.close();
// Then with simple text
buf.writeString("abc");
p = buf.asParser();
assertNull(p.getCurrentToken());
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertEquals("abc", p.getText());
assertNull(p.nextToken());
p.close();
// Then, let's append at root level
buf.writeNumber(13);
p = buf.asParser();
assertNull(p.getCurrentToken());
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(13, p.getIntValue());
assertNull(p.nextToken());
p.close();
buf.close();
}
// For 2.9, explicit "isNaN" check
public void testSimpleNumberWrites() throws IOException
{
TokenBuffer buf = new TokenBuffer(null, false);
double[] values1 = new double[] {
0.25, Double.NaN, -2.0, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY
};
float[] values2 = new float[] {
Float.NEGATIVE_INFINITY,
0.25f,
Float.POSITIVE_INFINITY
};
for (double v : values1) {
buf.writeNumber(v);
}
for (float v : values2) {
buf.writeNumber(v);
}
JsonParser p = buf.asParser();
assertNull(p.getCurrentToken());
for (double v : values1) {
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
double actual = p.getDoubleValue();
boolean expNan = Double.isNaN(v) || Double.isInfinite(v);
assertEquals(expNan, p.isNaN());
assertEquals(0, Double.compare(v, actual));
}
for (float v : values2) {
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
float actual = p.getFloatValue();
boolean expNan = Float.isNaN(v) || Float.isInfinite(v);
assertEquals(expNan, p.isNaN());
assertEquals(0, Float.compare(v, actual));
}
p.close();
buf.close();
}
public void testParentContext() throws IOException
{
TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec
buf.writeStartObject();
buf.writeFieldName("b");
buf.writeStartObject();
buf.writeFieldName("c");
//This assertion succeeds as expected
assertEquals("b", buf.getOutputContext().getParent().getCurrentName());
buf.writeString("cval");
buf.writeEndObject();
buf.writeEndObject();
buf.close();
}
public void testSimpleArray() throws IOException
{
TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec
// First, empty array
assertTrue(buf.getOutputContext().inRoot());
buf.writeStartArray();
assertTrue(buf.getOutputContext().inArray());
buf.writeEndArray();
assertTrue(buf.getOutputContext().inRoot());
JsonParser p = buf.asParser();
assertNull(p.getCurrentToken());
assertTrue(p.getParsingContext().inRoot());
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertTrue(p.getParsingContext().inArray());
assertToken(JsonToken.END_ARRAY, p.nextToken());
assertTrue(p.getParsingContext().inRoot());
assertNull(p.nextToken());
p.close();
buf.close();
// Then one with simple contents
buf = new TokenBuffer(null, false);
buf.writeStartArray();
buf.writeBoolean(true);
buf.writeNull();
buf.writeEndArray();
p = buf.asParser();
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_TRUE, p.nextToken());
assertTrue(p.getBooleanValue());
assertToken(JsonToken.VALUE_NULL, p.nextToken());
assertToken(JsonToken.END_ARRAY, p.nextToken());
assertNull(p.nextToken());
p.close();
buf.close();
// And finally, with array-in-array
buf = new TokenBuffer(null, false);
buf.writeStartArray();
buf.writeStartArray();
buf.writeBinary(new byte[3]);
buf.writeEndArray();
buf.writeEndArray();
p = buf.asParser();
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.START_ARRAY, p.nextToken());
// TokenBuffer exposes it as embedded object...
assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
Object ob = p.getEmbeddedObject();
assertNotNull(ob);
assertTrue(ob instanceof byte[]);
assertEquals(3, ((byte[]) ob).length);
assertToken(JsonToken.END_ARRAY, p.nextToken());
assertToken(JsonToken.END_ARRAY, p.nextToken());
assertNull(p.nextToken());
p.close();
buf.close();
}
public void testSimpleObject() throws IOException
{
TokenBuffer buf = new TokenBuffer(null, false);
// First, empty JSON Object
assertTrue(buf.getOutputContext().inRoot());
buf.writeStartObject();
assertTrue(buf.getOutputContext().inObject());
buf.writeEndObject();
assertTrue(buf.getOutputContext().inRoot());
JsonParser p = buf.asParser();
assertNull(p.getCurrentToken());
assertTrue(p.getParsingContext().inRoot());
assertToken(JsonToken.START_OBJECT, p.nextToken());
assertTrue(p.getParsingContext().inObject());
assertToken(JsonToken.END_OBJECT, p.nextToken());
assertTrue(p.getParsingContext().inRoot());
assertNull(p.nextToken());
p.close();
buf.close();
// Then one with simple contents
buf = new TokenBuffer(null, false);
buf.writeStartObject();
buf.writeNumberField("num", 1.25);
buf.writeEndObject();
p = buf.asParser();
assertNull(p.getCurrentToken());
assertToken(JsonToken.START_OBJECT, p.nextToken());
assertNull(p.getCurrentName());
assertToken(JsonToken.FIELD_NAME, p.nextToken());
assertEquals("num", p.getCurrentName());
// and override should also work:
p.overrideCurrentName("bah");
assertEquals("bah", p.getCurrentName());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
assertEquals(1.25, p.getDoubleValue());
// should still have access to (overridden) name
assertEquals("bah", p.getCurrentName());
assertToken(JsonToken.END_OBJECT, p.nextToken());
// but not any more
assertNull(p.getCurrentName());
assertNull(p.nextToken());
p.close();
buf.close();
}
/**
* Verify handling of that "standard" test document (from JSON
* specification)
*/
public void testWithJSONSampleDoc() throws Exception
{
// First, copy events from known good source (StringReader)
JsonParser p = createParserUsingReader(SAMPLE_DOC_JSON_SPEC);
TokenBuffer tb = new TokenBuffer(null, false);
while (p.nextToken() != null) {
tb.copyCurrentEvent(p);
}
// And then request verification; first structure only:
verifyJsonSpecSampleDoc(tb.asParser(), false);
// then content check too:
verifyJsonSpecSampleDoc(tb.asParser(), true);
tb.close();
p.close();
// 19-Oct-2016, tatu: Just for fun, trigger `toString()` for code coverage
String desc = tb.toString();
assertNotNull(desc);
}
public void testAppend() throws IOException
{
TokenBuffer buf1 = new TokenBuffer(null, false);
buf1.writeStartObject();
buf1.writeFieldName("a");
buf1.writeBoolean(true);
TokenBuffer buf2 = new TokenBuffer(null, false);
buf2.writeFieldName("b");
buf2.writeNumber(13);
buf2.writeEndObject();
buf1.append(buf2);
// and verify that we got it all...
JsonParser p = buf1.asParser();
assertToken(JsonToken.START_OBJECT, p.nextToken());
assertToken(JsonToken.FIELD_NAME, p.nextToken());
assertEquals("a", p.getCurrentName());
assertToken(JsonToken.VALUE_TRUE, p.nextToken());
assertToken(JsonToken.FIELD_NAME, p.nextToken());
assertEquals("b", p.getCurrentName());
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(13, p.getIntValue());
assertToken(JsonToken.END_OBJECT, p.nextToken());
p.close();
buf1.close();
buf2.close();
}
// Since 2.3 had big changes to UUID handling, let's verify we can
// deal with
public void testWithUUID() throws IOException
{
for (String value : new String[] {
"00000007-0000-0000-0000-000000000000",
"76e6d183-5f68-4afa-b94a-922c1fdb83f8",
"540a88d1-e2d8-4fb1-9396-9212280d0a7f",
"2c9e441d-1cd0-472d-9bab-69838f877574",
"591b2869-146e-41d7-8048-e8131f1fdec5",
"82994ac2-7b23-49f2-8cc5-e24cf6ed77be",
}) {
TokenBuffer buf = new TokenBuffer(MAPPER, false); // no ObjectCodec
UUID uuid = UUID.fromString(value);
MAPPER.writeValue(buf, uuid);
buf.close();
// and bring it back
UUID out = MAPPER.readValue(buf.asParser(), UUID.class);
assertEquals(uuid.toString(), out.toString());
// second part: As per [databind#362], should NOT use binary with TokenBuffer
JsonParser p = buf.asParser();
assertEquals(JsonToken.VALUE_STRING, p.nextToken());
String str = p.getText();
assertEquals(value, str);
p.close();
}
}
/*
/**********************************************************
/* Tests for read/output contexts
/**********************************************************
*/
// for [databind#984]: ensure output context handling identical
public void testOutputContext() throws IOException
{
TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec
StringWriter w = new StringWriter();
JsonGenerator gen = MAPPER.getFactory().createGenerator(w);
// test content: [{"a":1,"b":{"c":2}},{"a":2,"b":{"c":3}}]
buf.writeStartArray();
gen.writeStartArray();
_verifyOutputContext(buf, gen);
buf.writeStartObject();
gen.writeStartObject();
_verifyOutputContext(buf, gen);
buf.writeFieldName("a");
gen.writeFieldName("a");
_verifyOutputContext(buf, gen);
buf.writeNumber(1);
gen.writeNumber(1);
_verifyOutputContext(buf, gen);
buf.writeFieldName("b");
gen.writeFieldName("b");
_verifyOutputContext(buf, gen);
buf.writeStartObject();
gen.writeStartObject();
_verifyOutputContext(buf, gen);
buf.writeFieldName("c");
gen.writeFieldName("c");
_verifyOutputContext(buf, gen);
buf.writeNumber(2);
gen.writeNumber(2);
_verifyOutputContext(buf, gen);
buf.writeEndObject();
gen.writeEndObject();
_verifyOutputContext(buf, gen);
buf.writeEndObject();
gen.writeEndObject();
_verifyOutputContext(buf, gen);
buf.writeEndArray();
gen.writeEndArray();
_verifyOutputContext(buf, gen);
buf.close();
gen.close();
}
private void _verifyOutputContext(JsonGenerator gen1, JsonGenerator gen2)
{
_verifyOutputContext(gen1.getOutputContext(), gen2.getOutputContext());
}
private void _verifyOutputContext(JsonStreamContext ctxt1, JsonStreamContext ctxt2)
{
if (ctxt1 == null) {
if (ctxt2 == null) {
return;
}
fail("Context 1 null, context 2 not null: "+ctxt2);
} else if (ctxt2 == null) {
fail("Context 2 null, context 1 not null: "+ctxt1);
}
if (!ctxt1.toString().equals(ctxt2.toString())) {
fail("Different output context: token-buffer's = "+ctxt1+", json-generator's: "+ctxt2);
}
if (ctxt1.inObject()) {
assertTrue(ctxt2.inObject());
String str1 = ctxt1.getCurrentName();
String str2 = ctxt2.getCurrentName();
if ((str1 != str2) && !str1.equals(str2)) {
fail("Expected name '"+str2+"' (JsonParser), TokenBuffer had '"+str1+"'");
}
} else if (ctxt1.inArray()) {
assertTrue(ctxt2.inArray());
assertEquals(ctxt1.getCurrentIndex(), ctxt2.getCurrentIndex());
}
_verifyOutputContext(ctxt1.getParent(), ctxt2.getParent());
}
// [databind#1253]
public void testParentSiblingContext() throws IOException
{
TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec
// {"a":{},"b":{"c":"cval"}}
buf.writeStartObject();
buf.writeFieldName("a");
buf.writeStartObject();
buf.writeEndObject();
buf.writeFieldName("b");
buf.writeStartObject();
buf.writeFieldName("c");
//This assertion fails (because of 'a')
assertEquals("b", buf.getOutputContext().getParent().getCurrentName());
buf.writeString("cval");
buf.writeEndObject();
buf.writeEndObject();
buf.close();
}
public void testBasicSerialize() throws IOException
{
TokenBuffer buf;
// let's see how empty works...
buf = new TokenBuffer(MAPPER, false);
assertEquals("", MAPPER.writeValueAsString(buf));
buf.close();
buf = new TokenBuffer(MAPPER, false);
buf.writeStartArray();
buf.writeBoolean(true);
buf.writeBoolean(false);
long l = 1L + Integer.MAX_VALUE;
buf.writeNumber(l);
buf.writeNumber((short) 4);
buf.writeNumber(0.5);
buf.writeEndArray();
assertEquals(aposToQuotes("[true,false,"+l+",4,0.5]"), MAPPER.writeValueAsString(buf));
buf.close();
buf = new TokenBuffer(MAPPER, false);
buf.writeStartObject();
buf.writeFieldName(new SerializedString("foo"));
buf.writeNull();
buf.writeFieldName("bar");
buf.writeNumber(BigInteger.valueOf(123));
buf.writeFieldName("dec");
buf.writeNumber(BigDecimal.valueOf(5).movePointLeft(2));
assertEquals(aposToQuotes("{'foo':null,'bar':123,'dec':0.05}"), MAPPER.writeValueAsString(buf));
buf.close();
}
/*
/**********************************************************
/* Tests to verify interaction of TokenBuffer and JsonParserSequence
/**********************************************************
*/
public void testWithJsonParserSequenceSimple() throws IOException
{
// Let's join a TokenBuffer with JsonParser first
TokenBuffer buf = new TokenBuffer(null, false);
buf.writeStartArray();
buf.writeString("test");
JsonParser p = createParserUsingReader("[ true, null ]");
JsonParserSequence seq = JsonParserSequence.createFlattened(false, buf.asParser(), p);
assertEquals(2, seq.containedParsersCount());
assertFalse(p.isClosed());
assertFalse(seq.hasCurrentToken());
assertNull(seq.getCurrentToken());
assertNull(seq.getCurrentName());
assertToken(JsonToken.START_ARRAY, seq.nextToken());
assertToken(JsonToken.VALUE_STRING, seq.nextToken());
assertEquals("test", seq.getText());
// end of first parser input, should switch over:
assertToken(JsonToken.START_ARRAY, seq.nextToken());
assertToken(JsonToken.VALUE_TRUE, seq.nextToken());
assertToken(JsonToken.VALUE_NULL, seq.nextToken());
assertToken(JsonToken.END_ARRAY, seq.nextToken());
/* 17-Jan-2009, tatus: At this point, we may or may not get an
* exception, depending on how underlying parsers work.
* Ideally this should be fixed, probably by asking underlying
* parsers to disable checking for balanced start/end markers.
*/
// for this particular case, we won't get an exception tho...
assertNull(seq.nextToken());
// not an error to call again...
assertNull(seq.nextToken());
// also: original parsers should be closed
assertTrue(p.isClosed());
p.close();
buf.close();
seq.close();
}
/**
* Test to verify that TokenBuffer and JsonParserSequence work together
* as expected.
*/
@SuppressWarnings("resource")
public void testWithMultipleJsonParserSequences() throws IOException
{
TokenBuffer buf1 = new TokenBuffer(null, false);
buf1.writeStartArray();
TokenBuffer buf2 = new TokenBuffer(null, false);
buf2.writeString("a");
TokenBuffer buf3 = new TokenBuffer(null, false);
buf3.writeNumber(13);
TokenBuffer buf4 = new TokenBuffer(null, false);
buf4.writeEndArray();
JsonParserSequence seq1 = JsonParserSequence.createFlattened(false, buf1.asParser(), buf2.asParser());
assertEquals(2, seq1.containedParsersCount());
JsonParserSequence seq2 = JsonParserSequence.createFlattened(false, buf3.asParser(), buf4.asParser());
assertEquals(2, seq2.containedParsersCount());
JsonParserSequence combo = JsonParserSequence.createFlattened(false, seq1, seq2);
// should flatten it to have 4 underlying parsers
assertEquals(4, combo.containedParsersCount());
assertToken(JsonToken.START_ARRAY, combo.nextToken());
assertToken(JsonToken.VALUE_STRING, combo.nextToken());
assertEquals("a", combo.getText());
assertToken(JsonToken.VALUE_NUMBER_INT, combo.nextToken());
assertEquals(13, combo.getIntValue());
assertToken(JsonToken.END_ARRAY, combo.nextToken());
assertNull(combo.nextToken());
buf1.close();
buf2.close();
buf3.close();
buf4.close();
}
// [databind#743]
public void testRawValues() throws Exception
{
final String RAW = "{\"a\":1}";
TokenBuffer buf = new TokenBuffer(null, false);
buf.writeRawValue(RAW);
// first: raw value won't be transformed in any way:
JsonParser p = buf.asParser();
assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken());
assertEquals(RawValue.class, p.getEmbeddedObject().getClass());
assertNull(p.nextToken());
p.close();
buf.close();
// then verify it would be serialized just fine
assertEquals(RAW, MAPPER.writeValueAsString(buf));
}
}