Checking in Jackson 0.9.1 baseline.
diff --git a/src/test/main/BaseTest.java b/src/test/main/BaseTest.java
new file mode 100644
index 0000000..7c4390c
--- /dev/null
+++ b/src/test/main/BaseTest.java
@@ -0,0 +1,109 @@
+package main;
+
+import java.io.*;
+
+import junit.framework.TestCase;
+
+import org.codehaus.jackson.*;
+
+public class BaseTest
+ extends TestCase
+{
+ /*
+ ////////////////////////////////////////////////////////
+ // Some sample documents:
+ ////////////////////////////////////////////////////////
+ */
+
+ protected final static int SAMPLE_SPEC_VALUE_WIDTH = 800;
+ protected final static int SAMPLE_SPEC_VALUE_HEIGHT = 600;
+ protected final static String SAMPLE_SPEC_VALUE_TITLE = "View from 15th Floor";
+ protected final static String SAMPLE_SPEC_VALUE_TN_URL = "http://www.example.com/image/481989943";
+ protected final static int SAMPLE_SPEC_VALUE_TN_HEIGHT = 125;
+ protected final static String SAMPLE_SPEC_VALUE_TN_WIDTH = "100";
+ protected final static int SAMPLE_SPEC_VALUE_TN_ID1 = 116;
+ protected final static int SAMPLE_SPEC_VALUE_TN_ID2 = 943;
+ protected final static int SAMPLE_SPEC_VALUE_TN_ID3 = 234;
+ protected final static int SAMPLE_SPEC_VALUE_TN_ID4 = 38793;
+
+ protected final static String SAMPLE_DOC_JSON_SPEC =
+ "{\n"
+ +" \"Image\" : {\n"
+ +" \"Width\" : "+SAMPLE_SPEC_VALUE_WIDTH+",\n"
+ +" \"Height\" : "+SAMPLE_SPEC_VALUE_HEIGHT+","
+ +"\"Title\" : \""+SAMPLE_SPEC_VALUE_TITLE+"\",\n"
+ +" \"Thumbnail\" : {\n"
+ +" \"Url\" : \""+SAMPLE_SPEC_VALUE_TN_URL+"\",\n"
+ +"\"Height\" : "+SAMPLE_SPEC_VALUE_TN_HEIGHT+",\n"
+ +" \"Width\" : \""+SAMPLE_SPEC_VALUE_TN_WIDTH+"\"\n"
+ +" },\n"
+ +" \"IDs\" : ["+SAMPLE_SPEC_VALUE_TN_ID1+","+SAMPLE_SPEC_VALUE_TN_ID2+","+SAMPLE_SPEC_VALUE_TN_ID3+","+SAMPLE_SPEC_VALUE_TN_ID4+"]\n"
+ +" }"
+ +"}"
+ ;
+
+ /*
+ ////////////////////////////////////////////////////////
+ // Parser/generator construction
+ ////////////////////////////////////////////////////////
+ */
+
+ protected JsonParser createParserUsingReader(String input)
+ throws IOException, JsonParseException
+ {
+ return new JsonFactory().createJsonParser(new StringReader(input));
+ }
+
+ protected JsonParser createParserUsingStream(String input, String encoding)
+ throws IOException, JsonParseException
+ {
+ byte[] data = input.getBytes(encoding);
+ InputStreamReader is = new InputStreamReader(new ByteArrayInputStream(data), encoding);
+ return new JsonFactory().createJsonParser(is);
+ }
+
+ /*
+ ////////////////////////////////////////////////////////
+ // Additional assertion methods
+ ////////////////////////////////////////////////////////
+ */
+
+ protected void assertToken(JsonToken expToken, JsonToken actToken)
+ {
+ if (actToken != expToken) {
+ fail("Expected token "+expToken+", current token "+actToken);
+ }
+ }
+
+ protected void assertToken(JsonToken expToken, JsonParser jp)
+ {
+ assertToken(expToken, jp.getCurrentToken());
+ }
+
+ protected void verifyException(Exception e, String match)
+ {
+ String msg = e.getMessage();
+ if (msg.indexOf(match) < 0) {
+ fail("Expected an exception with sub-string \""+match+"\": got one with message \""+msg+"\"");
+ }
+ }
+
+ /**
+ * Method that gets textual contents of the current token using
+ * available methods, and ensures results are consistent, before
+ * returning them
+ */
+ protected String getAndVerifyText(JsonParser jp)
+ throws IOException, JsonParseException
+ {
+ String str = jp.getText();
+
+ // Ok, let's verify other accessors
+ int actLen = jp.getTextLength();
+ assertEquals(str.length(), actLen);
+ char[] ch = jp.getTextCharacters();
+ /*String str2 =*/ new String(ch, jp.getTextOffset(), actLen);
+
+ return str;
+ }
+}
diff --git a/src/test/main/TestArrayParsing.java b/src/test/main/TestArrayParsing.java
new file mode 100644
index 0000000..36f6726
--- /dev/null
+++ b/src/test/main/TestArrayParsing.java
@@ -0,0 +1,73 @@
+package main;
+
+import org.codehaus.jackson.*;
+
+/**
+ * Set of additional unit for verifying array parsing, specifically
+ * edge cases.
+ */
+public class TestArrayParsing
+ extends BaseTest
+{
+ public void testValidEmpty()
+ throws Exception
+ {
+ final String DOC = "[ \n ]";
+
+ JsonParser jp = createParserUsingStream(DOC, "UTF-8");
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.END_ARRAY, jp.nextToken());
+ assertNull(jp.nextToken());
+ jp.close();
+ }
+
+ public void testInvalidEmptyMissingClose()
+ throws Exception
+ {
+ final String DOC = "[ ";
+
+ JsonParser jp = createParserUsingStream(DOC, "UTF-8");
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+
+ try {
+ jp.nextToken();
+ fail("Expected a parsing error for missing array close marker");
+ } catch (JsonParseException jex) {
+ verifyException(jex, "expected close marker for ARRAY");
+ }
+ }
+
+ public void testInvalidMissingFieldName()
+ throws Exception
+ {
+ final String DOC = "[ : 3 ] ";
+
+ JsonParser jp = createParserUsingStream(DOC, "UTF-8");
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+
+ try {
+ jp.nextToken();
+ fail("Expected a parsing error for missing array close marker");
+ } catch (JsonParseException jex) {
+ verifyException(jex, "Unexpected character");
+ }
+ }
+
+ public void testInvalidExtraComma()
+ throws Exception
+ {
+ final String DOC = "[ 24, ] ";
+
+ JsonParser jp = createParserUsingStream(DOC, "UTF-8");
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(24, jp.getIntValue());
+
+ try {
+ jp.nextToken();
+ fail("Expected a parsing error for missing array close marker");
+ } catch (JsonParseException jex) {
+ verifyException(jex, "expected a value");
+ }
+ }
+}
diff --git a/src/test/main/TestCharEscaping.java b/src/test/main/TestCharEscaping.java
new file mode 100644
index 0000000..95f0cab
--- /dev/null
+++ b/src/test/main/TestCharEscaping.java
@@ -0,0 +1,100 @@
+package main;
+
+import org.codehaus.jackson.*;
+
+/**
+ * Set of basic unit tests for verifying that the basic parser
+ * functionality works as expected.
+ */
+public class TestCharEscaping
+ extends BaseTest
+{
+ public void testMissingEscaping()
+ throws Exception
+ {
+ // Invalid: control chars, including lf, must be escaped
+ final String DOC = "["
+ +"\"Linefeed: \n.\""
+ +"]";
+ JsonParser jp = createParserUsingReader(DOC);
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ try {
+ // This may or may not trigger exception
+ JsonToken t = jp.nextToken();
+ assertToken(JsonToken.VALUE_STRING, t);
+ // and if not, should get it here:
+ jp.getText();
+ fail("Expected an exception for un-escaped linefeed in string value");
+ } catch (JsonParseException jex) {
+ verifyException(jex, "has to be escaped");
+ }
+ }
+
+ public void testSimpleEscaping()
+ throws Exception
+ {
+ String DOC = "["
+ +"\"LF=\\n\""
+ +"]";
+
+ JsonParser jp = createParserUsingReader(DOC);
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals("LF=\n", jp.getText());
+ jp.close();
+
+
+ /* Note: must split Strings, so that javac won't try to handle
+ * escape and inline null char
+ */
+ DOC = "[\"NULL:\\u0000!\"]";
+
+ jp = createParserUsingReader(DOC);
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals("NULL:\0!", jp.getText());
+
+ // Then just a single char escaping
+ jp = createParserUsingReader("[\"\\u0123\"]");
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals("\u0123", jp.getText());
+
+ // And then double sequence
+ jp = createParserUsingReader("[\"\\u0041\\u0043\"]");
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals("AC", jp.getText());
+ }
+
+ public void testInvalid()
+ throws Exception
+ {
+ // 2-char sequences not allowed:
+ String DOC = "[\"\\u41=A\"]";
+ JsonParser jp = createParserUsingReader(DOC);
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ try {
+ jp.nextToken();
+ jp.getText();
+ fail("Expected an exception for unclosed ARRAY");
+ } catch (JsonParseException jpe) {
+ verifyException(jpe, "for character escape");
+ }
+ }
+
+ /**
+ * Test to verify that decoder does not allow 8-digit escapes
+ * (non-BMP characters must be escaped using two 4-digit sequences)
+ */
+ public void test8DigitSequence()
+ throws Exception
+ {
+ String DOC = "[\"\\u00411234\"]";
+ JsonParser jp = createParserUsingReader(DOC);
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals("A1234", jp.getText());
+ }
+}
+
diff --git a/src/test/main/TestJsonGenerator.java b/src/test/main/TestJsonGenerator.java
new file mode 100644
index 0000000..5866005
--- /dev/null
+++ b/src/test/main/TestJsonGenerator.java
@@ -0,0 +1,325 @@
+package main;
+
+import org.codehaus.jackson.*;
+
+import java.io.*;
+
+/**
+ * Set of basic unit tests for verifying that the basic generator
+ * functionality works as expected.
+ */
+public class TestJsonGenerator
+ extends BaseTest
+{
+ // // // First, tests for primitive (non-structured) values
+
+ public void testStringWrite()
+ throws Exception
+ {
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ String VALUE = "";
+ gen.writeString(VALUE);
+ gen.close();
+ String docStr = sw.toString();
+ JsonParser jp = createParserUsingReader(docStr);
+ JsonToken t = jp.nextToken();
+ assertNotNull("Document \""+docStr+"\" yielded no tokens", t);
+ assertEquals(JsonToken.VALUE_STRING, t);
+ assertEquals(VALUE, jp.getText());
+ assertEquals(null, jp.nextToken());
+ jp.close();
+ }
+
+ public void testIntWrite()
+ throws Exception
+ {
+ doTestIntWrite(false);
+ doTestIntWrite(true);
+ }
+
+ public void testLongWrite()
+ throws Exception
+ {
+ doTestLongWrite(false);
+ doTestLongWrite(true);
+ }
+
+ public void testBooleanWrite()
+ throws Exception
+ {
+ for (int i = 0; i < 4; ++i) {
+ boolean state = (i & 1) == 0;
+ boolean pad = (i & 2) == 0;
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ gen.writeBoolean(state);
+ if (pad) {
+ gen.writeRaw(" ");
+ }
+ gen.close();
+ String docStr = sw.toString();
+ JsonParser jp = createParserUsingReader(docStr);
+ JsonToken t = jp.nextToken();
+ String exp = Boolean.valueOf(state).toString();
+ if (!exp.equals(jp.getText())) {
+ fail("Expected '"+exp+"', got '"+jp.getText());
+ }
+ assertEquals(state ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE, t);
+ assertEquals(null, jp.nextToken());
+ jp.close();
+ }
+ }
+
+ public void testNullWrite()
+ throws Exception
+ {
+ for (int i = 0; i < 2; ++i) {
+ boolean pad = (i & 1) == 0;
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ gen.writeNull();
+ if (pad) {
+ gen.writeRaw(" ");
+ }
+ gen.close();
+ String docStr = sw.toString();
+ JsonParser jp = createParserUsingReader(docStr);
+ JsonToken t = jp.nextToken();
+ String exp = "null";
+ if (!exp.equals(jp.getText())) {
+ fail("Expected '"+exp+"', got '"+jp.getText());
+ }
+ assertEquals(JsonToken.VALUE_NULL, t);
+ assertEquals(null, jp.nextToken());
+ jp.close();
+ }
+ }
+
+ // // // Then tests for structured values
+
+ public void testEmptyArrayWrite()
+ throws Exception
+ {
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ gen.writeStartArray();
+ gen.writeEndArray();
+ gen.close();
+ String docStr = sw.toString();
+ JsonParser jp = createParserUsingReader(docStr);
+ assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+ assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+ jp.close();
+
+ // Ok, then array with nested empty array
+ sw = new StringWriter();
+ gen = new JsonFactory().createJsonGenerator(sw);
+ gen.writeStartArray();
+ gen.writeStartArray();
+ gen.writeEndArray();
+ gen.writeEndArray();
+ gen.close();
+ docStr = sw.toString();
+ jp = createParserUsingReader(docStr);
+ assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+ assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+ assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+ assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+ assertEquals(null, jp.nextToken());
+ jp.close();
+ }
+
+ public void testInvalidArrayWrite()
+ throws Exception
+ {
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ gen.writeStartArray();
+ // Mismatch:
+ try {
+ gen.writeEndObject();
+ fail("Expected an exception for mismatched array/object write");
+ } catch (JsonGenerationException e) {
+ verifyException(e, "Current context not an object");
+ }
+ }
+
+ public void testSimpleArrayWrite()
+ throws Exception
+ {
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ gen.writeStartArray();
+ gen.writeNumber(13);
+ gen.writeBoolean(true);
+ gen.writeString("foobar");
+ gen.writeEndArray();
+ gen.close();
+ String docStr = sw.toString();
+ JsonParser jp = createParserUsingReader(docStr);
+ assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(13, jp.getIntValue());
+ assertEquals(JsonToken.VALUE_TRUE, jp.nextToken());
+ assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals("foobar", jp.getText());
+ assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+ assertEquals(null, jp.nextToken());
+ jp.close();
+ }
+
+ public void testEmptyObjectWrite()
+ throws Exception
+ {
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ gen.writeStartObject();
+ gen.writeEndObject();
+ gen.close();
+ String docStr = sw.toString();
+ JsonParser jp = createParserUsingReader(docStr);
+ assertEquals(JsonToken.START_OBJECT, jp.nextToken());
+ assertEquals(JsonToken.END_OBJECT, jp.nextToken());
+ assertEquals(null, jp.nextToken());
+ }
+
+ public void testInvalidObjectWrite()
+ throws Exception
+ {
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ gen.writeStartObject();
+ // Mismatch:
+ try {
+ gen.writeEndArray();
+ fail("Expected an exception for mismatched array/object write");
+ } catch (JsonGenerationException e) {
+ verifyException(e, "Current context not an array");
+ }
+ }
+
+ public void testSimpleObjectWrite()
+ throws Exception
+ {
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ gen.writeStartObject();
+ gen.writeFieldName("first");
+ gen.writeNumber(-901);
+ gen.writeFieldName("sec");
+ gen.writeBoolean(false);
+ gen.writeFieldName("3rd!"); // json field names are just strings, not ids with restrictions
+ gen.writeString("yee-haw");
+ gen.writeEndObject();
+ gen.close();
+ String docStr = sw.toString();
+ JsonParser jp = createParserUsingReader(docStr);
+ assertEquals(JsonToken.START_OBJECT, jp.nextToken());
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals("first", jp.getText());
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(-901, jp.getIntValue());
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals("sec", jp.getText());
+ assertEquals(JsonToken.VALUE_FALSE, jp.nextToken());
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals("3rd!", jp.getText());
+ assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals("yee-haw", jp.getText());
+ assertEquals(JsonToken.END_OBJECT, jp.nextToken());
+ assertEquals(null, jp.nextToken());
+ jp.close();
+ }
+
+ // // Then root-level output testing
+
+ public void testRootIntsWrite()
+ throws Exception
+ {
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ gen.writeNumber(1);
+ gen.writeNumber(2);
+ gen.writeNumber(-13);
+ gen.close();
+
+ String docStr = sw.toString();
+
+ JsonParser jp = createParserUsingReader(docStr);
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(1, jp.getIntValue());
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(2, jp.getIntValue());
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(-13, jp.getIntValue());
+ jp.close();
+ }
+
+ /*
+ //////////////////////////////////////////////////
+ // Internal methods
+ //////////////////////////////////////////////////
+ */
+
+ private void doTestIntWrite(boolean pad)
+ throws Exception
+ {
+ int[] VALUES = new int[] {
+ 0, 1, -9, 32, -32, 57, 13240, -9999, Integer.MAX_VALUE, Integer.MAX_VALUE
+ };
+ for (int i = 0; i < VALUES.length; ++i) {
+ int VALUE = VALUES[i];
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ gen.writeNumber(VALUE);
+ if (pad) {
+ gen.writeRaw(" ");
+ }
+ gen.close();
+ String docStr = sw.toString();
+ JsonParser jp = createParserUsingReader(docStr);
+ JsonToken t = jp.nextToken();
+ assertNotNull("Document \""+docStr+"\" yielded no tokens", t);
+ // Number are always available as lexical representation too
+ String exp = ""+VALUE;
+ if (!exp.equals(jp.getText())) {
+ fail("Expected '"+exp+"', got '"+jp.getText());
+ }
+ assertEquals(JsonToken.VALUE_NUMBER_INT, t);
+ assertEquals(VALUE, jp.getIntValue());
+ assertEquals(null, jp.nextToken());
+ jp.close();
+ }
+ }
+
+ private void doTestLongWrite(boolean pad)
+ throws Exception
+ {
+ long[] VALUES = new long[] {
+ 0L, 1L, -1L, -12005002294L, Long.MIN_VALUE, Long.MAX_VALUE
+ };
+ for (int i = 0; i < VALUES.length; ++i) {
+ long VALUE = VALUES[i];
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ gen.writeNumber(VALUE);
+ if (pad) {
+ gen.writeRaw(" ");
+ }
+ gen.close();
+ String docStr = sw.toString();
+ JsonParser jp = createParserUsingReader(docStr);
+ JsonToken t = jp.nextToken();
+ assertNotNull("Document \""+docStr+"\" yielded no tokens", t);
+ String exp = ""+VALUE;
+ if (!exp.equals(jp.getText())) {
+ fail("Expected '"+exp+"', got '"+jp.getText());
+ }
+ assertEquals(JsonToken.VALUE_NUMBER_INT, t);
+ assertEquals(VALUE, jp.getLongValue());
+ assertEquals(null, jp.nextToken());
+ jp.close();
+ }
+ }
+}
diff --git a/src/test/main/TestJsonParser.java b/src/test/main/TestJsonParser.java
new file mode 100644
index 0000000..de36df2
--- /dev/null
+++ b/src/test/main/TestJsonParser.java
@@ -0,0 +1,262 @@
+package main;
+
+import org.codehaus.jackson.*;
+
+import java.io.IOException;
+
+/**
+ * Set of basic unit tests for verifying that the basic parser
+ * functionality works as expected.
+ */
+public class TestJsonParser
+ extends BaseTest
+{
+
+ /**
+ * This basic unit test verifies that example given in the Json
+ * specification (RFC-4627 or later) is properly parsed at
+ * high-level, without verifying values.
+ */
+ public void testSpecExampleSkipping()
+ throws Exception
+ {
+ doTestSpec(false);
+ }
+
+ /**
+ * Unit test that verifies that the spec example JSON is completely
+ * parsed, and proper values are given for contents of all
+ * events/tokens.
+ */
+ public void testSpecExampleFully()
+ throws Exception
+ {
+ doTestSpec(true);
+ }
+
+ /**
+ * Unit test that verifies that 3 basic keywords (null, true, false)
+ * are properly parsed in various contexts.
+ */
+ public void testKeywords()
+ throws Exception
+ {
+ final String DOC = "{\n"
+ +"\"key1\" : null,\n"
+ +"\"key2\" : true,\n"
+ +"\"key3\" : false,\n"
+ +"\"key4\" : [ false, null, true ]\n"
+ +"}"
+ ;
+
+ JsonParser jp = createParserUsingStream(DOC, "UTF-8");
+ assertToken(JsonToken.START_OBJECT, jp.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+ verifyFieldName(jp, "key1");
+ assertToken(JsonToken.VALUE_NULL, jp.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+ verifyFieldName(jp, "key2");
+ assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+ verifyFieldName(jp, "key3");
+ assertToken(JsonToken.VALUE_FALSE, jp.nextToken());
+
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+ verifyFieldName(jp, "key4");
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_FALSE, jp.nextToken());
+ assertToken(JsonToken.VALUE_NULL, jp.nextToken());
+ assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
+ assertToken(JsonToken.END_ARRAY, jp.nextToken());
+
+ assertToken(JsonToken.END_OBJECT, jp.nextToken());
+ }
+
+ public void testInvalidKeywords()
+ throws Exception
+ {
+ doTestInvalidKeyword1("nul");
+ doTestInvalidKeyword2("nulla", JsonToken.VALUE_NULL);
+ doTestInvalidKeyword1("fal");
+ doTestInvalidKeyword3("False");
+ doTestInvalidKeyword2("falsett0", JsonToken.VALUE_FALSE);
+ doTestInvalidKeyword1("tr");
+ doTestInvalidKeyword1("truE");
+ doTestInvalidKeyword2("trueenough", JsonToken.VALUE_TRUE);
+ }
+
+ /*
+ /////////////////////////////////////////////
+ // Helper methods
+ /////////////////////////////////////////////
+ */
+
+ private void doTestSpec(boolean verify)
+ throws IOException
+ {
+ // First, using a StringReader:
+ doTestSpecIndividual(null, verify);
+
+ // Then with streams using supported encodings:
+ doTestSpecIndividual("UTF-8", verify);
+ doTestSpecIndividual("UTF-16BE", verify);
+ doTestSpecIndividual("UTF-16LE", verify);
+
+ /* Hmmh. UTF-32 is harder only because JDK doesn't come with
+ * a codec for it. Can't test it yet using this method
+ */
+ //doTestSpecIndividual("UTF-32", verify);
+ }
+
+ private void doTestSpecIndividual(String enc, boolean verify)
+ throws IOException
+ {
+ String doc = SAMPLE_DOC_JSON_SPEC;
+ JsonParser jp;
+
+ if (enc == null) {
+ jp = createParserUsingReader(doc);
+ } else {
+ jp = createParserUsingStream(doc, enc);
+ }
+
+ assertToken(JsonToken.START_OBJECT, jp.nextToken()); // main object
+
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Image'
+ if (verify) {
+ verifyFieldName(jp, "Image");
+ }
+
+ assertToken(JsonToken.START_OBJECT, jp.nextToken()); // 'image' object
+
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Width'
+ if (verify) {
+ verifyFieldName(jp, "Width");
+ }
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ if (verify) {
+ verifyIntValue(jp, SAMPLE_SPEC_VALUE_WIDTH);
+ }
+
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Height'
+ if (verify) {
+ verifyFieldName(jp, "Height");
+ }
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ verifyIntValue(jp, SAMPLE_SPEC_VALUE_HEIGHT);
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Title'
+ if (verify) {
+ verifyFieldName(jp, "Title");
+ }
+ assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals(SAMPLE_SPEC_VALUE_TITLE, getAndVerifyText(jp));
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Thumbnail'
+ if (verify) {
+ verifyFieldName(jp, "Thumbnail");
+ }
+
+ assertToken(JsonToken.START_OBJECT, jp.nextToken()); // 'thumbnail' object
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Url'
+ if (verify) {
+ verifyFieldName(jp, "Url");
+ }
+ assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ if (verify) {
+ assertEquals(SAMPLE_SPEC_VALUE_TN_URL, getAndVerifyText(jp));
+ }
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Height'
+ if (verify) {
+ verifyFieldName(jp, "Height");
+ }
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_HEIGHT);
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Width'
+ if (verify) {
+ verifyFieldName(jp, "Width");
+ }
+ // Width value is actually a String in the example
+ assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, getAndVerifyText(jp));
+
+ assertToken(JsonToken.END_OBJECT, jp.nextToken()); // 'thumbnail' object
+
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'IDs'
+ assertToken(JsonToken.START_ARRAY, jp.nextToken()); // 'ids' array
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); // ids[0]
+ verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID1);
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); // ids[1]
+ verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID2);
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); // ids[2]
+ verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID3);
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); // ids[3]
+ verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID4);
+ assertToken(JsonToken.END_ARRAY, jp.nextToken()); // 'ids' array
+
+ assertToken(JsonToken.END_OBJECT, jp.nextToken()); // 'image' object
+
+ assertToken(JsonToken.END_OBJECT, jp.nextToken()); // main object
+ }
+
+ private void verifyFieldName(JsonParser jp, String expName)
+ throws IOException
+ {
+ assertEquals(expName, jp.getText());
+ assertEquals(expName, jp.getCurrentName());
+ }
+
+ private void verifyIntValue(JsonParser jp, long expValue)
+ throws IOException
+ {
+ // First, via textual
+ assertEquals(String.valueOf(expValue), jp.getText());
+ }
+
+ private void doTestInvalidKeyword1(String value)
+ throws IOException
+ {
+ JsonParser jp = createParserUsingStream("{ \"key1\" : "+value+" }", "UTF-8");
+ assertToken(JsonToken.START_OBJECT, jp.nextToken());
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+ try {
+ jp.nextToken();
+ fail("Expected an exception for malformed value keyword");
+ } catch (JsonParseException jex) {
+ verifyException(jex, "Unrecognized token");
+ }
+ }
+
+ private void doTestInvalidKeyword2(String value, JsonToken firstValue)
+ throws IOException
+ {
+ JsonParser jp = createParserUsingStream("{ \"key1\" : "+value+" }", "UTF-8");
+ assertToken(JsonToken.START_OBJECT, jp.nextToken());
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+ assertToken(firstValue, jp.nextToken());
+ try {
+ jp.nextToken();
+ fail("Expected an exception for malformed value keyword");
+ } catch (JsonParseException jex) {
+ verifyException(jex, "Unexpected character");
+ }
+ }
+
+ private void doTestInvalidKeyword3(String value)
+ throws IOException
+ {
+ JsonParser jp = createParserUsingStream("{ \"key1\" : "+value+" }", "UTF-8");
+ assertToken(JsonToken.START_OBJECT, jp.nextToken());
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+ try {
+ jp.nextToken();
+ fail("Expected an exception for malformed value keyword");
+ } catch (JsonParseException jex) {
+ verifyException(jex, "expected a valid value");
+ }
+ }
+}
+
diff --git a/src/test/main/TestNumberParsing.java b/src/test/main/TestNumberParsing.java
new file mode 100644
index 0000000..6d024ec
--- /dev/null
+++ b/src/test/main/TestNumberParsing.java
@@ -0,0 +1,42 @@
+package main;
+
+import org.codehaus.jackson.io.NumberInput;
+
+/**
+ * Set of basic unit tests for verifying that the low-level number
+ * handling methods work as expected.
+ */
+public class TestNumberParsing
+ extends BaseTest
+{
+ public void testIntParsing()
+ throws Exception
+ {
+ char[] testChars = "123456789".toCharArray();
+
+ assertEquals(3, NumberInput.parseInt(testChars, 2, 1));
+ assertEquals(123, NumberInput.parseInt(testChars, 0, 3));
+ assertEquals(2345, NumberInput.parseInt(testChars, 1, 4));
+ assertEquals(9, NumberInput.parseInt(testChars, 8, 1));
+ assertEquals(456789, NumberInput.parseInt(testChars, 3, 6));
+ assertEquals(23456, NumberInput.parseInt(testChars, 1, 5));
+ assertEquals(123456789, NumberInput.parseInt(testChars, 0, 9));
+
+ testChars = "32".toCharArray();
+ assertEquals(32, NumberInput.parseInt(testChars, 0, 2));
+ testChars = "189".toCharArray();
+ assertEquals(189, NumberInput.parseInt(testChars, 0, 3));
+
+ testChars = "10".toCharArray();
+ assertEquals(10, NumberInput.parseInt(testChars, 0, 2));
+ assertEquals(0, NumberInput.parseInt(testChars, 1, 1));
+ }
+
+ public void testLongParsing()
+ throws Exception
+ {
+ char[] testChars = "123456789012345678".toCharArray();
+
+ assertEquals(123456789012345678L, NumberInput.parseLong(testChars, 0, 18));
+ }
+}
diff --git a/src/test/main/TestNumberPrinting.java b/src/test/main/TestNumberPrinting.java
new file mode 100644
index 0000000..04862a6
--- /dev/null
+++ b/src/test/main/TestNumberPrinting.java
@@ -0,0 +1,102 @@
+package main;
+
+import org.codehaus.jackson.io.NumberOutput;
+
+import java.util.Random;
+
+/**
+ * Set of basic unit tests for verifying that the low-level number
+ * printingg methods work as expected.
+ */
+public class TestNumberPrinting
+ extends BaseTest
+{
+ public void testIntPrinting()
+ throws Exception
+ {
+ assertIntPrint(0);
+ assertIntPrint(-3);
+ assertIntPrint(1234);
+ assertIntPrint(-1234);
+ assertIntPrint(56789);
+ assertIntPrint(-56789);
+ assertIntPrint(999999);
+ assertIntPrint(-999999);
+ assertIntPrint(1000000);
+ assertIntPrint(-1000000);
+ assertIntPrint(10000001);
+ assertIntPrint(-10000001);
+ assertIntPrint(-100000012);
+ assertIntPrint(100000012);
+ assertIntPrint(1999888777);
+ assertIntPrint(-1999888777);
+ assertIntPrint(Integer.MAX_VALUE);
+ assertIntPrint(Integer.MIN_VALUE);
+
+ Random rnd = new Random(12345L);
+ for (int i = 0; i < 251000; ++i) {
+ assertIntPrint(rnd.nextInt());
+ }
+ }
+
+ public void testLongPrinting()
+ throws Exception
+ {
+ // First, let's just cover couple of edge cases
+ assertLongPrint(0L, 0);
+ assertLongPrint(1L, 0);
+ assertLongPrint(-1L, 0);
+ assertLongPrint(Long.MAX_VALUE, 0);
+ assertLongPrint(Long.MIN_VALUE, 0);
+ assertLongPrint(Long.MAX_VALUE-1L, 0);
+ assertLongPrint(Long.MIN_VALUE+1L, 0);
+
+ Random rnd = new Random(12345L);
+ // Bigger value space, need more iterations for long
+ for (int i = 0; i < 678000; ++i) {
+ long l = ((long) rnd.nextInt() << 32) | (long) rnd.nextInt();
+ assertLongPrint(l, i);
+ }
+ }
+
+ /*
+ ////////////////////////////////////////////////////////
+ // Internal methods
+ ////////////////////////////////////////////////////////
+ */
+
+ private void assertIntPrint(int value)
+ {
+ String exp = ""+value;
+ String act = printToString(value);
+
+ if (!exp.equals(act)) {
+ assertEquals("Expected conversion (exp '"+exp+"', len "+exp.length()+"; act len "+act.length()+")", exp, act);
+ }
+ }
+
+ private void assertLongPrint(long value, int index)
+ {
+ String exp = ""+value;
+ String act = printToString(value);
+
+ if (!exp.equals(act)) {
+ assertEquals("Expected conversion (exp '"+exp+"', len "+exp.length()+"; act len "+act.length()+"; number index "+index+")", exp, act);
+ }
+ }
+
+ private String printToString(int value)
+ {
+ char[] buffer = new char[12];
+ int offset = NumberOutput.outputInt(value, buffer, 0);
+ return new String(buffer, 0, offset);
+ }
+
+ private String printToString(long value)
+ {
+ char[] buffer = new char[22];
+ int offset = NumberOutput.outputLong(value, buffer, 0);
+ return new String(buffer, 0, offset);
+ }
+}
+
diff --git a/src/test/main/TestNumericValues.java b/src/test/main/TestNumericValues.java
new file mode 100644
index 0000000..529617d
--- /dev/null
+++ b/src/test/main/TestNumericValues.java
@@ -0,0 +1,124 @@
+package main;
+
+import java.math.BigDecimal;
+
+import org.codehaus.jackson.*;
+
+/**
+ * Set of basic unit tests for verifying that the basic parser
+ * functionality works as expected.
+ */
+public class TestNumericValues
+ extends BaseTest
+{
+ public void testSimpleInt()
+ throws Exception
+ {
+ int EXP_I = 1234;
+
+ JsonParser jp = createParserUsingReader("[ "+EXP_I+" ]");
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(""+EXP_I, jp.getText());
+
+ assertEquals(EXP_I, jp.getIntValue());
+ assertEquals((long) EXP_I, jp.getLongValue());
+ assertEquals((double) EXP_I, jp.getDoubleValue());
+ assertEquals(BigDecimal.valueOf((long) EXP_I), jp.getDecimalValue());
+ }
+
+ public void testSimpleLong()
+ throws Exception
+ {
+ long EXP_L = 12345678907L;
+
+ JsonParser jp = createParserUsingReader("[ "+EXP_L+" ]");
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(""+EXP_L, jp.getText());
+
+ assertEquals(EXP_L, jp.getLongValue());
+ // Should get an exception if trying to convert to int
+ try {
+ jp.getIntValue();
+ } catch (JsonParseException jpe) {
+ verifyException(jpe, "out of range");
+ }
+ assertEquals((double) EXP_L, jp.getDoubleValue());
+ assertEquals(BigDecimal.valueOf((long) EXP_L), jp.getDecimalValue());
+ }
+
+ public void testSimpleDouble()
+ throws Exception
+ {
+ /* Testing double is more difficult, given the rounding
+ * errors and such. But let's try anyways.
+ */
+ String EXP_D_STR = "1234.00";
+ double EXP_D = Double.parseDouble(EXP_D_STR);
+
+ JsonParser jp = createParserUsingReader("[ "+EXP_D_STR+" ]");
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
+ assertEquals(EXP_D_STR, jp.getText());
+ assertEquals(EXP_D, jp.getDoubleValue());
+ jp.close();
+
+ EXP_D_STR = "2.1101567E-16";
+ EXP_D = Double.parseDouble(EXP_D_STR);
+
+ jp = createParserUsingReader("[ "+EXP_D_STR+" ]");
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
+ assertEquals(EXP_D_STR, jp.getText());
+ assertEquals(EXP_D, jp.getDoubleValue());
+ jp.close();
+ }
+
+ public void testNumbers()
+ throws Exception
+ {
+ final String DOC = "[ -13, 8100200300, 13.5, 0.00010, -2.033 ]";
+ JsonParser jp = createParserUsingStream(DOC, "UTF-8");
+
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(-13, jp.getIntValue());
+ assertEquals(-13L, jp.getLongValue());
+ assertEquals(-13., jp.getDoubleValue());
+ assertEquals("-13", jp.getText());
+
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(8100200300L, jp.getLongValue());
+ // Should get exception for overflow:
+ try {
+ /*int x =*/ jp.getIntValue();
+ fail("Expected an exception for overflow");
+ } catch (Exception e) {
+ verifyException(e, "out of range");
+ }
+ assertEquals(8100200300., jp.getDoubleValue());
+ assertEquals("8100200300", jp.getText());
+
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
+ assertEquals(13, jp.getIntValue());
+ assertEquals(13L, jp.getLongValue());
+ assertEquals(13.5, jp.getDoubleValue());
+ assertEquals("13.5", jp.getText());
+
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
+ assertEquals(0, jp.getIntValue());
+ assertEquals(0L, jp.getLongValue());
+ assertEquals(0.00010, jp.getDoubleValue());
+ assertEquals("0.00010", jp.getText());
+
+ assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
+ assertEquals(-2, jp.getIntValue());
+ assertEquals(-2L, jp.getLongValue());
+ assertEquals(-2.033, jp.getDoubleValue());
+ assertEquals("-2.033", jp.getText());
+
+ assertToken(JsonToken.END_ARRAY, jp.nextToken());
+ }
+}
diff --git a/src/test/main/TestPrettyPrinter.java b/src/test/main/TestPrettyPrinter.java
new file mode 100644
index 0000000..afa6deb
--- /dev/null
+++ b/src/test/main/TestPrettyPrinter.java
@@ -0,0 +1,66 @@
+package main;
+
+import org.codehaus.jackson.*;
+
+import java.io.*;
+
+/**
+ * Set of basic unit tests for verifying that indenting
+ * option of generator works correctly
+ */
+public class TestPrettyPrinter
+ extends BaseTest
+{
+ public void testSimpleDoc()
+ throws Exception
+ {
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ gen.useDefaultPrettyPrinter();
+
+ gen.writeStartArray();
+ gen.writeNumber(3);
+ gen.writeString("abc");
+
+ gen.writeStartArray();
+ gen.writeBoolean(true);
+ gen.writeEndArray();
+
+ gen.writeStartObject();
+ gen.writeFieldName("f");
+ gen.writeNull();
+ gen.writeFieldName("f2");
+ gen.writeNull();
+ gen.writeEndObject();
+
+ gen.writeEndArray();
+ gen.close();
+
+ String docStr = sw.toString();
+ JsonParser jp = createParserUsingReader(docStr);
+
+ assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(3, jp.getIntValue());
+ assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals("abc", jp.getText());
+
+ assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+ assertEquals(JsonToken.VALUE_TRUE, jp.nextToken());
+ assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+
+ assertEquals(JsonToken.START_OBJECT, jp.nextToken());
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals("f", jp.getText());
+ assertEquals(JsonToken.VALUE_NULL, jp.nextToken());
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals("f2", jp.getText());
+ assertEquals(JsonToken.VALUE_NULL, jp.nextToken());
+ assertEquals(JsonToken.END_OBJECT, jp.nextToken());
+
+ assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+
+ jp.close();
+ }
+}
diff --git a/src/test/main/TestScopeMatching.java b/src/test/main/TestScopeMatching.java
new file mode 100644
index 0000000..08eee57
--- /dev/null
+++ b/src/test/main/TestScopeMatching.java
@@ -0,0 +1,73 @@
+package main;
+
+import org.codehaus.jackson.*;
+
+/**
+ * Set of basic unit tests for verifying that Array/Object scopes
+ * are properly matched.
+ */
+public class TestScopeMatching
+ extends BaseTest
+{
+ public void testUnclosedArray()
+ throws Exception
+ {
+ JsonParser jp = createParserUsingReader("[ 1, 2");
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+
+ try {
+ jp.nextToken();
+ fail("Expected an exception for unclosed ARRAY");
+ } catch (JsonParseException jpe) {
+ verifyException(jpe, "expected close marker for ARRAY");
+ }
+ }
+
+ public void testUnclosedObject()
+ throws Exception
+ {
+ JsonParser jp = createParserUsingReader("{ \"key\" : 3 ");
+ assertToken(JsonToken.START_OBJECT, jp.nextToken());
+ assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+
+ try {
+ jp.nextToken();
+ fail("Expected an exception for unclosed OBJECT");
+ } catch (JsonParseException jpe) {
+ verifyException(jpe, "expected close marker for OBJECT");
+ }
+ }
+
+ public void testMismatchArrayToObject()
+ throws Exception
+ {
+ JsonParser jp = createParserUsingReader("[ 1, 2 }");
+ assertToken(JsonToken.START_ARRAY, jp.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+
+ try {
+ jp.nextToken();
+ fail("Expected an exception for incorrectly closed ARRAY");
+ } catch (JsonParseException jpe) {
+ verifyException(jpe, "Unexpected close marker");
+ }
+ }
+
+ public void testMismatchObjectToArray()
+ throws Exception
+ {
+ JsonParser jp = createParserUsingReader("{ ]");
+ assertToken(JsonToken.START_OBJECT, jp.nextToken());
+
+ try {
+ jp.nextToken();
+ fail("Expected an exception for incorrectly closed OBJECT");
+ } catch (JsonParseException jpe) {
+ verifyException(jpe, "Unexpected close marker");
+ }
+ }
+}
diff --git a/src/test/main/TestStringGeneration.java b/src/test/main/TestStringGeneration.java
new file mode 100644
index 0000000..c68b847
--- /dev/null
+++ b/src/test/main/TestStringGeneration.java
@@ -0,0 +1,214 @@
+package main;
+
+import java.io.*;
+
+import org.codehaus.jackson.*;
+
+import java.util.Random;
+
+/**
+ * Set of basic unit tests for verifying that the string
+ * generation, including character escaping, works as expected.
+ */
+public class TestStringGeneration
+ extends BaseTest
+{
+ final static String[] SAMPLES = new String[] {
+ "\"test\"",
+ "\n", "\\n", "\r\n", "a\\b", "tab:\nok?",
+ "a\tb\tc\n\fdef\t \tg\"\"\"h\"\\ijklmn\b",
+ "\"\"\"", "\\r)'\"",
+ "Longer text & other stuff:\twith some\r\n\r\n random linefeeds etc added in to cause some \"special\" handling \\\\ to occur...\n"
+ };
+
+ public void testBasicEscaping()
+ throws Exception
+ {
+ doTestBasicEscaping(false);
+ doTestBasicEscaping(true);
+ }
+
+ public void testLongerRandomSingleChunk()
+ throws Exception
+ {
+ /* Let's first generate 100k of pseudo-random characters, favoring
+ * 7-bit ascii range
+ */
+ for (int round = 0; round < 80; ++round) {
+ String content = generateRandom(75000+round);
+ doTestLongerRandom(content, false);
+ doTestLongerRandom(content, true);
+ }
+ }
+
+ public void testLongerRandomMultiChunk()
+ throws Exception
+ {
+ /* Let's first generate 100k of pseudo-random characters, favoring
+ * 7-bit ascii range
+ */
+ for (int round = 0; round < 70; ++round) {
+ String content = generateRandom(73000+round);
+ doTestLongerRandomMulti(content, false, round);
+ doTestLongerRandomMulti(content, true, round);
+ }
+ }
+
+ /*
+ ///////////////////////////////////////////////////////////////
+ // Internal methods
+ ///////////////////////////////////////////////////////////////
+ */
+
+ private String generateRandom(int len)
+ {
+ StringBuilder sb = new StringBuilder(len+1000); // pad for surrogates
+ Random r = new Random(len);
+ for (int i = 0; i < len; ++i) {
+ if (r.nextBoolean()) { // non-ascii
+ int value = r.nextInt() & 0xFFFF;
+ // Otherwise easy, except that need to ensure that
+ // surrogates are properly paired: and, also
+ // their values do not exceed 0x10FFFF
+ if (value >= 0xD800 && value <= 0xDFFF) {
+ // Let's discard first value, then, and produce valid pair
+ int fullValue = (r.nextInt() & 0xFFFFF);
+ sb.append((char) (0xD800 + (fullValue >> 10)));
+ value = 0xDC00 + (fullValue & 0x3FF);
+ }
+ sb.append((char) value);
+ } else { // ascii
+ sb.append((char) (r.nextInt() & 0x7F));
+ }
+ }
+ return sb.toString();
+ }
+
+ private void doTestBasicEscaping(boolean charArray)
+ throws Exception
+ {
+ for (int i = 0; i < SAMPLES.length; ++i) {
+ String VALUE = SAMPLES[i];
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ gen.writeStartArray();
+ if (charArray) {
+ char[] buf = new char[VALUE.length() + i];
+ VALUE.getChars(0, VALUE.length(), buf, i);
+ gen.writeString(buf, i, VALUE.length());
+ } else {
+ gen.writeString(VALUE);
+ }
+ gen.writeEndArray();
+ gen.close();
+ String docStr = sw.toString();
+ JsonParser jp = createParserUsingReader(docStr);
+ assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+ JsonToken t = jp.nextToken();
+ assertEquals(JsonToken.VALUE_STRING, t);
+ assertEquals(VALUE, jp.getText());
+ assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+ assertEquals(null, jp.nextToken());
+ jp.close();
+ }
+ }
+
+ private void doTestLongerRandom(String text, boolean charArray)
+ throws Exception
+ {
+ ByteArrayOutputStream bow = new ByteArrayOutputStream(text.length());
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(bow, JsonFactory.Encoding.UTF8);
+ gen.writeStartArray();
+ if (charArray) {
+ char[] buf = new char[text.length()];
+ text.getChars(0, text.length(), buf, 0);
+ gen.writeString(buf, 0, text.length());
+ } else {
+ gen.writeString(text);
+ }
+ gen.writeEndArray();
+ gen.close();
+ byte[] docData = bow.toByteArray();
+ JsonParser jp = new JsonFactory().createJsonParser(new ByteArrayInputStream(docData));
+ assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+ JsonToken t = jp.nextToken();
+ assertEquals(JsonToken.VALUE_STRING, t);
+ String act = jp.getText();
+ if (!text.equals(act)) {
+ if (text.length() != act.length()) {
+ fail("Expected string length "+text.length()+", actual "+act.length());
+ }
+ int i = 0;
+ for (int len = text.length(); i < len; ++i) {
+ if (text.charAt(i) != act.charAt(i)) {
+ break;
+ }
+ }
+ fail("Strings differ at position #"+i+" (len "+text.length()+"): expected char 0x"+Integer.toHexString(text.charAt(i))+", actual 0x"+Integer.toHexString(act.charAt(i)));
+ }
+ assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+ assertEquals(null, jp.nextToken());
+ jp.close();
+ }
+
+ private void doTestLongerRandomMulti(String text, boolean charArray, int round)
+ throws Exception
+ {
+ ByteArrayOutputStream bow = new ByteArrayOutputStream(text.length());
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(bow, JsonFactory.Encoding.UTF8);
+ gen.writeStartArray();
+
+ gen.writeString(text);
+ gen.writeEndArray();
+ gen.close();
+
+ gen = new JsonFactory().createJsonGenerator(bow, JsonFactory.Encoding.UTF8);
+ gen.writeStartArray();
+ gen.writeStartArray();
+
+ Random rnd = new Random(text.length());
+ int offset = 0;
+
+ while (offset < text.length()) {
+ int shift = 1 + ((rnd.nextInt() & 0xFFFFF) % 12); // 1 - 12
+ int len = (1 << shift) + shift; // up to 4k
+ if ((offset + len) >= text.length()) {
+ len = text.length() - offset;
+ } else {
+ // Need to avoid splitting surrogates though
+ char c = text.charAt(offset+len-1);
+ if (c >= 0xD800 && c < 0xDC00) {
+ ++len;
+ }
+ }
+ if (charArray) {
+ char[] buf = new char[len];
+ text.getChars(offset, offset+len, buf, 0);
+ gen.writeString(buf, 0, len);
+ } else {
+ gen.writeString(text.substring(offset, offset+len));
+ }
+ offset += len;
+ }
+
+ gen.writeEndArray();
+ gen.close();
+ byte[] docData = bow.toByteArray();
+ JsonParser jp = new JsonFactory().createJsonParser(new ByteArrayInputStream(docData));
+ assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+
+ offset = 0;
+ while (jp.nextToken() == JsonToken.VALUE_STRING) {
+ // Let's verify, piece by piece
+ String act = jp.getText();
+ String exp = text.substring(offset, offset+act.length());
+ if (!act.equals(exp)) {
+ fail("String segment ["+offset+" - "+(offset+act.length())+"[ different");
+ }
+ offset += act.length();
+ }
+ assertEquals(JsonToken.END_ARRAY, jp.getCurrentToken());
+ jp.close();
+ }
+
+}
diff --git a/src/test/map/TestFromJavaType.java b/src/test/map/TestFromJavaType.java
new file mode 100644
index 0000000..ddb85b3
--- /dev/null
+++ b/src/test/map/TestFromJavaType.java
@@ -0,0 +1,102 @@
+package map;
+
+import main.BaseTest;
+
+import java.io.*;
+import java.util.*;
+
+import org.codehaus.jackson.*;
+import org.codehaus.jackson.map.*;
+
+/**
+ * This unit test suite tries to verify that the "Native" java type
+ * mapper can properly serialize Java core objects to JSON.
+ */
+public class TestFromJavaType
+ extends BaseTest
+{
+ public void testFromArray()
+ throws Exception
+ {
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+
+ ArrayList<Object> doc = new ArrayList<Object>();
+ doc.add("Elem1");
+ doc.add(Integer.valueOf(3));
+ Map<String,Object> struct = new LinkedHashMap<String, Object>();
+ struct.put("first", Boolean.TRUE);
+ struct.put("Second", new ArrayList<Object>());
+ doc.add(struct);
+ doc.add(Boolean.FALSE);
+
+ new JavaTypeMapper().writeAny(gen, doc);
+ gen.close();
+
+ JsonParser jp = new JsonFactory().createJsonParser(new StringReader(sw.toString()));
+
+ assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+
+ assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals("Elem1", getAndVerifyText(jp));
+
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(3, jp.getIntValue());
+
+ assertEquals(JsonToken.START_OBJECT, jp.nextToken());
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals("first", getAndVerifyText(jp));
+
+ assertEquals(JsonToken.VALUE_TRUE, jp.nextToken());
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals("Second", getAndVerifyText(jp));
+
+ assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+ assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+ assertEquals(JsonToken.END_OBJECT, jp.nextToken());
+
+ assertEquals(JsonToken.VALUE_FALSE, jp.nextToken());
+
+ assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+ assertNull(jp.nextToken());
+ }
+
+ public void testFromMap()
+ throws Exception
+ {
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+
+ LinkedHashMap<String,Object> doc = new LinkedHashMap<String,Object>();
+
+ doc.put("a1", "\"text\"");
+ doc.put("int", Integer.valueOf(137));
+ doc.put("foo bar", Long.valueOf(1234567890L));
+
+ new JavaTypeMapper().writeAny(gen, doc);
+ gen.close();
+
+ JsonParser jp = new JsonFactory().createJsonParser(new StringReader(sw.toString()));
+
+ assertEquals(JsonToken.START_OBJECT, jp.nextToken());
+
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals("a1", getAndVerifyText(jp));
+ assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals("\"text\"", getAndVerifyText(jp));
+
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals("int", getAndVerifyText(jp));
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(137, jp.getIntValue());
+
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals("foo bar", getAndVerifyText(jp));
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(1234567890L, jp.getLongValue());
+
+ assertEquals(JsonToken.END_OBJECT, jp.nextToken());
+
+ assertNull(jp.nextToken());
+ }
+}
diff --git a/src/test/map/TestFromJsonType.java b/src/test/map/TestFromJsonType.java
new file mode 100644
index 0000000..096dcfb
--- /dev/null
+++ b/src/test/map/TestFromJsonType.java
@@ -0,0 +1,145 @@
+package map;
+
+import main.BaseTest;
+
+import java.io.*;
+
+import org.codehaus.jackson.*;
+import org.codehaus.jackson.map.*;
+
+/**
+ * This unit test suite tries to verify that the "JSON type"
+ * mapper constructed JsonNodes can be serialized properly.
+ */
+public class TestFromJsonType
+ extends BaseTest
+{
+ final static String FIELD1 = "first";
+ final static String FIELD2 = "Second?";
+ final static String FIELD3 = "foo'n \"bar\"";
+ final static String FIELD4 = "4";
+
+ final static String TEXT1 = "Some text & \"stuff\"";
+ final static String TEXT2 = "Some more text:\twith\nlinefeeds and all!";
+
+ final static double DOUBLE_VALUE = 9.25;
+
+ public void testFromArray()
+ throws Exception
+ {
+ JsonTypeMapper mapper = new JsonTypeMapper();
+
+ JsonNode root = mapper.arrayNode();
+ root.appendElement(mapper.textNode(TEXT1));
+ root.appendElement(mapper.numberNode(3));
+ JsonNode obj = mapper.objectNode();
+ root.appendElement(obj);
+ obj.setElement(FIELD1, mapper.booleanNode(true));
+ obj.setElement(FIELD2, mapper.arrayNode());
+ root.appendElement(mapper.booleanNode(false));
+
+ /* Ok, ready... let's serialize using one of two alternate
+ * methods: first preferred (using generator)
+ */
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ root.writeTo(gen);
+ gen.close();
+ verifyFromArray(sw.toString());
+
+ // And then convenient but less efficient alternative:
+ verifyFromArray(root.toString());
+ }
+
+ public void testFromMap()
+ throws Exception
+ {
+ JsonTypeMapper mapper = new JsonTypeMapper();
+
+ JsonNode root = mapper.objectNode();
+ root.setElement(FIELD4, mapper.textNode(TEXT2));
+ root.setElement(FIELD3, mapper.numberNode(-1));
+ root.setElement(FIELD2, mapper.arrayNode());
+ root.setElement(FIELD1, mapper.numberNode(DOUBLE_VALUE));
+
+ /* Let's serialize using one of two alternate methods:
+ * first preferred (using generator)
+ */
+ StringWriter sw = new StringWriter();
+ JsonGenerator gen = new JsonFactory().createJsonGenerator(sw);
+ root.writeTo(gen);
+ gen.close();
+ verifyFromMap(sw.toString());
+
+ // And then convenient but less efficient alternative:
+ verifyFromMap(root.toString());
+ }
+
+ /*
+ ///////////////////////////////////////////////////////////////
+ // Internal methods
+ ///////////////////////////////////////////////////////////////
+ */
+
+ private void verifyFromArray(String input)
+ throws Exception
+ {
+ JsonParser jp = new JsonFactory().createJsonParser(new StringReader(input));
+
+ assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+
+ assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals(TEXT1, getAndVerifyText(jp));
+
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(3, jp.getIntValue());
+
+ assertEquals(JsonToken.START_OBJECT, jp.nextToken());
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals(FIELD1, getAndVerifyText(jp));
+
+ assertEquals(JsonToken.VALUE_TRUE, jp.nextToken());
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals(FIELD2, getAndVerifyText(jp));
+
+ assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+ assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+ assertEquals(JsonToken.END_OBJECT, jp.nextToken());
+
+ assertEquals(JsonToken.VALUE_FALSE, jp.nextToken());
+
+ assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+ assertNull(jp.nextToken());
+ }
+
+ private void verifyFromMap(String input)
+ throws Exception
+ {
+ JsonParser jp = new JsonFactory().createJsonParser(new StringReader(input));
+ assertEquals(JsonToken.START_OBJECT, jp.nextToken());
+
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals(FIELD4, getAndVerifyText(jp));
+ assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
+ assertEquals(TEXT2, getAndVerifyText(jp));
+
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals(FIELD3, getAndVerifyText(jp));
+ assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+ assertEquals(-1, jp.getIntValue());
+
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals(FIELD2, getAndVerifyText(jp));
+ assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+ assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+
+ assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+ assertEquals(FIELD1, getAndVerifyText(jp));
+ assertEquals(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
+ assertEquals(DOUBLE_VALUE, jp.getDoubleValue());
+
+ assertEquals(JsonToken.END_OBJECT, jp.nextToken());
+
+ assertNull(jp.nextToken());
+ }
+}
diff --git a/src/test/map/TestToJavaType.java b/src/test/map/TestToJavaType.java
new file mode 100644
index 0000000..6180742
--- /dev/null
+++ b/src/test/map/TestToJavaType.java
@@ -0,0 +1,141 @@
+package map;
+
+import main.BaseTest;
+
+import java.io.*;
+
+import org.codehaus.jackson.*;
+import org.codehaus.jackson.map.*;
+import org.codehaus.jackson.map.impl.*;
+
+/**
+ * This unit test suite tries to verify that the "JSON" type
+ * mapper can properly parse JSON and bind contents into appropriate
+ * JsonNode instances.
+ */
+public class TestToJavaType
+ extends BaseTest
+{
+ public void testSimple()
+ throws Exception
+ {
+ final String JSON = SAMPLE_DOC_JSON_SPEC;
+
+ JsonFactory jf = new JsonFactory();
+ JsonNode result = new JsonTypeMapper().read(jf.createJsonParser(new StringReader(JSON)));
+ assertType(result, ObjectNode.class);
+ assertEquals(1, result.size());
+ assertTrue(result.isObject());
+
+ ObjectNode main = (ObjectNode) result;
+ assertEquals("Image", main.getFieldNames().next());
+ JsonNode ob = main.getFieldValues().next();
+ assertType(ob, ObjectNode.class);
+ ObjectNode imageMap = (ObjectNode) ob;
+
+ assertEquals(5, imageMap.size());
+ ob = imageMap.getFieldValue("Width");
+ assertTrue(ob.isIntegralNumber());
+ assertEquals(SAMPLE_SPEC_VALUE_WIDTH, ob.getIntValue());
+
+ ob = imageMap.getFieldValue("Height");
+ assertTrue(ob.isIntegralNumber());
+ assertEquals(SAMPLE_SPEC_VALUE_HEIGHT, ob.getIntValue());
+
+ ob = imageMap.getFieldValue("Title");
+ assertTrue(ob.isTextual());
+ assertEquals(SAMPLE_SPEC_VALUE_TITLE, ob.getTextValue());
+
+ ob = imageMap.getFieldValue("Thumbnail");
+ assertType(ob, ObjectNode.class);
+ ObjectNode tn = (ObjectNode) ob;
+ ob = tn.getFieldValue("Url");
+ assertTrue(ob.isTextual());
+ assertEquals(SAMPLE_SPEC_VALUE_TN_URL, ob.getTextValue());
+ ob = tn.getFieldValue("Height");
+ assertTrue(ob.isIntegralNumber());
+ assertEquals(SAMPLE_SPEC_VALUE_TN_HEIGHT, ob.getIntValue());
+ ob = tn.getFieldValue("Width");
+ assertTrue(ob.isTextual());
+ assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, ob.getTextValue());
+
+ ob = imageMap.getFieldValue("IDs");
+ assertTrue(ob.isArray());
+ ArrayNode idList = (ArrayNode) ob;
+ assertEquals(4, idList.size());
+ {
+ int[] values = new int[] {
+ SAMPLE_SPEC_VALUE_TN_ID1,
+ SAMPLE_SPEC_VALUE_TN_ID2,
+ SAMPLE_SPEC_VALUE_TN_ID3,
+ SAMPLE_SPEC_VALUE_TN_ID4
+ };
+ for (int i = 0; i < values.length; ++i) {
+ assertEquals(values[i], idList.getElementValue(i).getIntValue());
+ }
+ }
+ }
+
+ /**
+ * Type mappers should be able to gracefully deal with end of
+ * input.
+ */
+ public void testEOF()
+ throws Exception
+ {
+ JsonFactory jf = new JsonFactory();
+ String JSON =
+ "{ \"key\": [ { \"a\" : { \"name\": \"foo\", \"type\": 1\n"
+ +"}, \"type\": 3, \"url\": \"http://www.google.com\" } ],\n"
+ +"\"name\": \"xyz\", \"type\": 1, \"url\" : null }\n "
+ ;
+ JsonParser jp = jf.createJsonParser(new StringReader(JSON));
+ JsonTypeMapper mapper = new JsonTypeMapper();
+ JsonNode result = mapper.read(jp);
+ assertTrue(result.isObject());
+ assertEquals(4, result.size());
+
+ assertNull(mapper.read(jp));
+ }
+
+ public void testMultipl()
+ throws Exception
+ {
+ JsonFactory jf = new JsonFactory();
+ String JSON = "12 \"string\" [ 1, 2, 3 ]";
+ JsonParser jp = jf.createJsonParser(new StringReader(JSON));
+ JsonTypeMapper mapper = new JsonTypeMapper();
+
+ JsonNode result = mapper.read(jp);
+ assertTrue(result.isIntegralNumber());
+ assertEquals(12, result.getIntValue());
+
+ result = mapper.read(jp);
+ assertTrue(result.isTextual());
+ assertEquals("string", result.getTextValue());
+
+ result = mapper.read(jp);
+ assertTrue(result.isArray());
+ assertEquals(3, result.size());
+
+ assertNull(mapper.read(jp));
+ }
+
+ /*
+ //////////////////////////////////////////////
+ // Helper methods
+ //////////////////////////////////////////////
+ */
+
+ private void assertType(Object ob, Class<?> expType)
+ {
+ if (ob == null) {
+ fail("Expected an object of type "+expType.getName()+", got null");
+ }
+ Class<?> cls = ob.getClass();
+ if (!expType.isAssignableFrom(cls)) {
+ fail("Expected type "+expType.getName()+", got "+cls.getName());
+ }
+ }
+}
+